Browse Source

Merge IMAP with phpimap

Laurent Destailleur 2 năm trước cách đây
mục cha
commit
b15a4e504e
100 tập tin đã thay đổi với 24177 bổ sung277 xóa
  1. 3 3
      .travis.yml
  2. 7 9
      htdocs/admin/emailcollector_card.php
  3. 507 265
      htdocs/emailcollector/class/emailcollector.class.php
  4. 31 0
      htdocs/includes/webklex/php-imap/.github/ISSUE_TEMPLATE/bug_report.md
  5. 17 0
      htdocs/includes/webklex/php-imap/.github/ISSUE_TEMPLATE/feature_request.md
  6. 12 0
      htdocs/includes/webklex/php-imap/.github/ISSUE_TEMPLATE/general-help-request.md
  7. 3 0
      htdocs/includes/webklex/php-imap/.gitignore
  8. 577 0
      htdocs/includes/webklex/php-imap/CHANGELOG.md
  9. 46 0
      htdocs/includes/webklex/php-imap/CODE_OF_CONDUCT.md
  10. 21 0
      htdocs/includes/webklex/php-imap/LICENSE
  11. 21 0
      htdocs/includes/webklex/php-imap/LICENSE.md
  12. 158 0
      htdocs/includes/webklex/php-imap/README.md
  13. 55 0
      htdocs/includes/webklex/php-imap/composer.json
  14. 16 0
      htdocs/includes/webklex/php-imap/deleted.txt
  15. 90 0
      htdocs/includes/webklex/php-imap/src/Address.php
  16. 344 0
      htdocs/includes/webklex/php-imap/src/Attachment.php
  17. 274 0
      htdocs/includes/webklex/php-imap/src/Attribute.php
  18. 669 0
      htdocs/includes/webklex/php-imap/src/Client.php
  19. 262 0
      htdocs/includes/webklex/php-imap/src/ClientManager.php
  20. 1074 0
      htdocs/includes/webklex/php-imap/src/Connection/Protocols/ImapProtocol.php
  21. 607 0
      htdocs/includes/webklex/php-imap/src/Connection/Protocols/LegacyProtocol.php
  22. 224 0
      htdocs/includes/webklex/php-imap/src/Connection/Protocols/Protocol.php
  23. 375 0
      htdocs/includes/webklex/php-imap/src/Connection/Protocols/ProtocolInterface.php
  24. 482 0
      htdocs/includes/webklex/php-imap/src/EncodingAliases.php
  25. 28 0
      htdocs/includes/webklex/php-imap/src/Events/Event.php
  26. 22 0
      htdocs/includes/webklex/php-imap/src/Events/FlagDeletedEvent.php
  27. 39 0
      htdocs/includes/webklex/php-imap/src/Events/FlagNewEvent.php
  28. 22 0
      htdocs/includes/webklex/php-imap/src/Events/FolderDeletedEvent.php
  29. 38 0
      htdocs/includes/webklex/php-imap/src/Events/FolderMovedEvent.php
  30. 35 0
      htdocs/includes/webklex/php-imap/src/Events/FolderNewEvent.php
  31. 22 0
      htdocs/includes/webklex/php-imap/src/Events/MessageCopiedEvent.php
  32. 22 0
      htdocs/includes/webklex/php-imap/src/Events/MessageDeletedEvent.php
  33. 38 0
      htdocs/includes/webklex/php-imap/src/Events/MessageMovedEvent.php
  34. 35 0
      htdocs/includes/webklex/php-imap/src/Events/MessageNewEvent.php
  35. 22 0
      htdocs/includes/webklex/php-imap/src/Events/MessageRestoredEvent.php
  36. 24 0
      htdocs/includes/webklex/php-imap/src/Exceptions/AuthFailedException.php
  37. 24 0
      htdocs/includes/webklex/php-imap/src/Exceptions/ConnectionFailedException.php
  38. 24 0
      htdocs/includes/webklex/php-imap/src/Exceptions/EventNotFoundException.php
  39. 24 0
      htdocs/includes/webklex/php-imap/src/Exceptions/FolderFetchingException.php
  40. 24 0
      htdocs/includes/webklex/php-imap/src/Exceptions/GetMessagesFailedException.php
  41. 24 0
      htdocs/includes/webklex/php-imap/src/Exceptions/InvalidMessageDateException.php
  42. 24 0
      htdocs/includes/webklex/php-imap/src/Exceptions/InvalidWhereQueryCriteriaException.php
  43. 24 0
      htdocs/includes/webklex/php-imap/src/Exceptions/MaskNotFoundException.php
  44. 24 0
      htdocs/includes/webklex/php-imap/src/Exceptions/MessageContentFetchingException.php
  45. 24 0
      htdocs/includes/webklex/php-imap/src/Exceptions/MessageFlagException.php
  46. 24 0
      htdocs/includes/webklex/php-imap/src/Exceptions/MessageHeaderFetchingException.php
  47. 24 0
      htdocs/includes/webklex/php-imap/src/Exceptions/MessageNotFoundException.php
  48. 24 0
      htdocs/includes/webklex/php-imap/src/Exceptions/MessageSearchValidationException.php
  49. 24 0
      htdocs/includes/webklex/php-imap/src/Exceptions/MethodNotFoundException.php
  50. 24 0
      htdocs/includes/webklex/php-imap/src/Exceptions/MethodNotSupportedException.php
  51. 24 0
      htdocs/includes/webklex/php-imap/src/Exceptions/ProtocolNotSupportedException.php
  52. 24 0
      htdocs/includes/webklex/php-imap/src/Exceptions/RuntimeException.php
  53. 444 0
      htdocs/includes/webklex/php-imap/src/Folder.php
  54. 753 0
      htdocs/includes/webklex/php-imap/src/Header.php
  55. 375 0
      htdocs/includes/webklex/php-imap/src/IMAP.php
  56. 1419 0
      htdocs/includes/webklex/php-imap/src/Message.php
  57. 312 0
      htdocs/includes/webklex/php-imap/src/Part.php
  58. 842 0
      htdocs/includes/webklex/php-imap/src/Query/Query.php
  59. 551 0
      htdocs/includes/webklex/php-imap/src/Query/WhereQuery.php
  60. 174 0
      htdocs/includes/webklex/php-imap/src/Structure.php
  61. 22 0
      htdocs/includes/webklex/php-imap/src/Support/AttachmentCollection.php
  62. 22 0
      htdocs/includes/webklex/php-imap/src/Support/FlagCollection.php
  63. 22 0
      htdocs/includes/webklex/php-imap/src/Support/FolderCollection.php
  64. 44 0
      htdocs/includes/webklex/php-imap/src/Support/Masks/AttachmentMask.php
  65. 137 0
      htdocs/includes/webklex/php-imap/src/Support/Masks/Mask.php
  66. 86 0
      htdocs/includes/webklex/php-imap/src/Support/Masks/MessageMask.php
  67. 22 0
      htdocs/includes/webklex/php-imap/src/Support/MessageCollection.php
  68. 82 0
      htdocs/includes/webklex/php-imap/src/Support/PaginatedCollection.php
  69. 77 0
      htdocs/includes/webklex/php-imap/src/Traits/HasEvents.php
  70. 216 0
      htdocs/includes/webklex/php-imap/src/config/imap.php
  71. 74 0
      htdocs/includes/webklex/php-imap/vendor/autoload.php
  72. 572 0
      htdocs/includes/webklex/php-imap/vendor/composer/ClassLoader.php
  73. 352 0
      htdocs/includes/webklex/php-imap/vendor/composer/InstalledVersions.php
  74. 21 0
      htdocs/includes/webklex/php-imap/vendor/composer/LICENSE
  75. 460 0
      htdocs/includes/webklex/php-imap/vendor/composer/autoload_classmap.php
  76. 15 0
      htdocs/includes/webklex/php-imap/vendor/composer/autoload_files.php
  77. 9 0
      htdocs/includes/webklex/php-imap/vendor/composer/autoload_namespaces.php
  78. 30 0
      htdocs/includes/webklex/php-imap/vendor/composer/autoload_psr4.php
  79. 57 0
      htdocs/includes/webklex/php-imap/vendor/composer/autoload_real.php
  80. 623 0
      htdocs/includes/webklex/php-imap/vendor/composer/autoload_static.php
  81. 2565 0
      htdocs/includes/webklex/php-imap/vendor/composer/installed.json
  82. 371 0
      htdocs/includes/webklex/php-imap/vendor/composer/installed.php
  83. 26 0
      htdocs/includes/webklex/php-imap/vendor/composer/platform_check.php
  84. 747 0
      htdocs/includes/webklex/php-imap/vendor/illuminate/collections/Arr.php
  85. 1672 0
      htdocs/includes/webklex/php-imap/vendor/illuminate/collections/Collection.php
  86. 1027 0
      htdocs/includes/webklex/php-imap/vendor/illuminate/collections/Enumerable.php
  87. 63 0
      htdocs/includes/webklex/php-imap/vendor/illuminate/collections/HigherOrderCollectionProxy.php
  88. 63 0
      htdocs/includes/webklex/php-imap/vendor/illuminate/collections/HigherOrderWhenProxy.php
  89. 9 0
      htdocs/includes/webklex/php-imap/vendor/illuminate/collections/ItemNotFoundException.php
  90. 21 0
      htdocs/includes/webklex/php-imap/vendor/illuminate/collections/LICENSE.md
  91. 1585 0
      htdocs/includes/webklex/php-imap/vendor/illuminate/collections/LazyCollection.php
  92. 9 0
      htdocs/includes/webklex/php-imap/vendor/illuminate/collections/MultipleItemsFoundException.php
  93. 1116 0
      htdocs/includes/webklex/php-imap/vendor/illuminate/collections/Traits/EnumeratesValues.php
  94. 41 0
      htdocs/includes/webklex/php-imap/vendor/illuminate/collections/composer.json
  95. 186 0
      htdocs/includes/webklex/php-imap/vendor/illuminate/collections/helpers.php
  96. 15 0
      htdocs/includes/webklex/php-imap/vendor/illuminate/contracts/Auth/Access/Authorizable.php
  97. 150 0
      htdocs/includes/webklex/php-imap/vendor/illuminate/contracts/Auth/Access/Gate.php
  98. 49 0
      htdocs/includes/webklex/php-imap/vendor/illuminate/contracts/Auth/Authenticatable.php
  99. 21 0
      htdocs/includes/webklex/php-imap/vendor/illuminate/contracts/Auth/CanResetPassword.php
  100. 22 0
      htdocs/includes/webklex/php-imap/vendor/illuminate/contracts/Auth/Factory.php

+ 3 - 3
.travis.yml

@@ -54,11 +54,11 @@ jobs:
       env: DB=mysql
     - stage: PHP Dev
       if: type = push AND branch = develop
-      php: nightly 
+      php: nightly
       env: DB=mysql
     - stage: PHP Dev
       if: type = push AND branch = 15.0
-      php: nightly 
+      php: nightly
       env: DB=mysql
 
 notifications:
@@ -289,7 +289,7 @@ script:
       --exclude htdocs/includes/sabre --exclude htdocs/includes/phpoffice/PhpSpreadsheet --exclude htdocs/includes/sebastian \
       --exclude htdocs/includes/squizlabs/php_codesniffer --exclude htdocs/includes/jakub-onderka --exclude htdocs/includes/php-parallel-lint --exclude htdocs/includes/symfony \
       --exclude htdocs/includes/mike42/escpos-php/example --exclude htdocs/includes/maximebf \
-      --exclude htdocs/includes/phpunit/ --exclude htdocs/includes/tecnickcom/tcpdf/include/barcodes --exclude htdocs/includes/webmozart --blame .
+      --exclude htdocs/includes/phpunit/ --exclude htdocs/includes/tecnickcom/tcpdf/include/barcodes --exclude htdocs/includes/webmozart --exclude htdocs/includes/webklex --blame .
   fi
   set +e
   echo

+ 7 - 9
htdocs/admin/emailcollector_card.php

@@ -91,9 +91,9 @@ include DOL_DOCUMENT_ROOT.'/core/actions_fetchobject.inc.php'; // Must be includ
 //$isdraft = (($object->statut == MyObject::STATUS_DRAFT) ? 1 : 0);
 //$result = restrictedArea($user, 'mymodule', $object->id, '', '', 'fk_soc', 'rowid', $isdraft);
 
-$permissionnote = $user->rights->emailcollector->write; // Used by the include of actions_setnotes.inc.php
-$permissiondellink = $user->rights->emailcollector->write; // Used by the include of actions_dellink.inc.php
-$permissiontoadd = $user->rights->emailcollector->write; // Used by the include of actions_addupdatedelete.inc.php and actions_lineupdown.inc.php
+$permissionnote = $user->admin; // Used by the include of actions_setnotes.inc.php
+$permissiondellink = $user->admin; // Used by the include of actions_dellink.inc.php
+$permissiontoadd = $user->admin; // Used by the include of actions_addupdatedelete.inc.php and actions_lineupdown.inc.php
 
 $debuginfo = '';
 
@@ -337,14 +337,12 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea
 
 	// Confirmation of action process
 	if ($action == 'collect') {
-		$formquestion = array(
-			'text' => $langs->trans("EmailCollectorConfirmCollect"),
-		);
-		$formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans('EmailCollectorConfirmCollectTitle'), $text, 'confirm_collect', $formquestion, 0, 1, 220);
+		$formquestion = array();
+		$formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans('EmailCollectorConfirmCollectTitle'), $langs->trans('EmailCollectorConfirmCollect'), 'confirm_collect', $formquestion, 0, 1, 220);
 	}
 
 	// Call Hook formConfirm
-	$parameters = array('formConfirm' => $formconfirm, 'lineid' => $lineid);
+	$parameters = array('formConfirm' => $formconfirm);
 	$reshook = $hookmanager->executeHooks('formConfirm', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
 	if (empty($reshook)) {
 		$formconfirm .= $hookmanager->resPrint;
@@ -679,7 +677,7 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea
 			print '<div class="inline-block divButAction"><a class="butAction" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=edit&token='.newToken().'">'.$langs->trans("Edit").'</a></div>';
 
 			// Clone
-			print '<div class="inline-block divButAction"><a class="butAction" href="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'&socid='.$object->socid.'&action=clone&token='.newToken().'&object=order">'.$langs->trans("ToClone").'</a></div>';
+			print '<div class="inline-block divButAction"><a class="butAction" href="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'&action=clone&token='.newToken().'&object=order">'.$langs->trans("ToClone").'</a></div>';
 
 			// Collect now
 			if (count($object->actions) > 0) {

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 507 - 265
htdocs/emailcollector/class/emailcollector.class.php


+ 31 - 0
htdocs/includes/webklex/php-imap/.github/ISSUE_TEMPLATE/bug_report.md

@@ -0,0 +1,31 @@
+---
+name: Bug report
+about: Create a report to help us improve
+
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**Used config**
+Please provide the used config, if you are not using the package default config.
+
+**Code to Reproduce**
+The troubling code section which produces the reported bug.
+```php
+echo "Bug";
+```
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Screenshots**
+If applicable, add screenshots to help explain your problem.
+
+**Desktop / Server (please complete the following information):**
+- OS: [e.g. Debian 10]
+- PHP: [e.g. 5.5.9]
+- Version [e.g. v2.3.1]
+
+**Additional context**
+Add any other context about the problem here.

+ 17 - 0
htdocs/includes/webklex/php-imap/.github/ISSUE_TEMPLATE/feature_request.md

@@ -0,0 +1,17 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.

+ 12 - 0
htdocs/includes/webklex/php-imap/.github/ISSUE_TEMPLATE/general-help-request.md

@@ -0,0 +1,12 @@
+---
+name: General help request
+about: Feel free to ask about any project related stuff
+
+---
+
+Please be aware that these issues will be closed if inactive for more then 14 days.
+
+Also make sure to use https://github.com/Webklex/php-imap/issues/new?template=bug_report.md if you want to report a bug 
+or https://github.com/Webklex/php-imap/issues/new?template=feature_request.md if you want to suggest a feature.
+
+Still here? Well clean this out and go ahead :)

+ 3 - 0
htdocs/includes/webklex/php-imap/.gitignore

@@ -0,0 +1,3 @@
+composer.lock
+.idea
+/build/

+ 577 - 0
htdocs/includes/webklex/php-imap/CHANGELOG.md

@@ -0,0 +1,577 @@
+# Changelog
+
+All notable changes to `webklex/php-imap` will be documented in this file.
+
+Updates should follow the [Keep a CHANGELOG](http://keepachangelog.com/) principles.
+
+## [UNRELEASED]
+### Fixed
+- NaN
+
+### Added
+- NaN
+
+### Affected Classes
+- NaN
+
+### Breaking changes
+- NaN
+
+## [2.7.2] - 2021-09-27
+### Fixed
+- Fixed problem with skipping last line of the response. #166 (thanks @szymekjanaczek)
+
+## [2.7.1] - 2021-09-08
+### Added
+- Added `UID` as available search criteria #161 (thanks @szymekjanaczek)
+
+## [2.7.0] - 2021-09-04
+### Fixed
+- Fixes handling of long header lines which are seperated by `\r\n\t` (thanks @Oliver-Holz)
+- Fixes to line parsing with multiple addresses (thanks @Oliver-Holz)
+
+### Added
+- Expose message folder path #154 (thanks @Magiczne)
+- Adds mailparse_rfc822_parse_addresses integration (thanks @Oliver-Holz)
+- Added moveManyMessages method (thanks @Magiczne)
+- Added copyManyMessages method (thanks @Magiczne)
+
+### Affected Classes
+- [Header::class](src/Header.php)
+- [Message::class](src/Message.php)
+
+## [2.6.0] - 2021-08-20
+### Fixed
+- POP3 fixes #151 (thanks @Korko)
+
+### Added
+- Added imap 4 handling. #146 (thanks @szymekjanaczek)
+- Added laravel's conditionable methods. #147  (thanks @szymekjanaczek)
+
+### Affected Classes
+- [Query::class](src/Query/Query.php)
+- [Client::class](src/Client.php)
+
+## [2.5.1] - 2021-06-19
+### Fixed
+- Fix setting default mask from config #133 (thanks @shacky)
+- Chunked fetch fails in case of less available mails than page size #114
+- Protocol::createStream() exception information fixed #137
+- Legacy methods (headers, content, flags) fixed #125
+- Legacy connection cycle fixed #124 (thanks @zssarkany)
+
+### Added
+- Disable rfc822 header parsing via config option #115
+
+## [2.5.0] - 2021-02-01
+### Fixed
+- Attachment saving filename fixed
+- Unnecessary parameter removed from `Client::getTimeout()`
+- Missing encryption variable added - could have caused problems with unencrypted communications
+- Prefer attachment filename attribute over name attribute #82
+- Missing connection settings added to `Folder:idle()` auto mode #89
+- Message move / copy expect a folder path #79
+- `Client::getFolder()` updated to circumvent special edge cases #79
+- Missing connection status checks added to various methods
+- Unused default attribute `message_no` removed from `Message::class`
+
+### Added
+- Dynamic Attribute access support added (e.g `$message->from[0]`)
+- Message not found exception added #93
+- Chunked fetching support added `Query::chunked()`. Just in case you can't fetch all messages at once
+- "Soft fail" support added
+- Count method added to `Attribute:class`
+- Convert an Attribute instance into a Carbon date object #95
+
+### Affected Classes
+- [Attachment::class](src/Attachment.php)
+- [Attribute::class](src/Attribute.php)
+- [Query::class](src/Query/Query.php)
+- [Message::class](src/Message.php)
+- [Client::class](src/Client.php)
+- [Folder::class](src/Folder.php)
+
+### Breaking changes
+- A new exception can occur if a message can't be fetched (`\Webklex\PHPIMAP\Exceptions\MessageNotFoundException::class`)
+- `Message::move()` and `Message::copy()` no longer accept folder names as folder path
+- A `Message::class` instance might no longer have a `message_no` attribute
+
+## [2.4.4] - 2021-01-22
+### Fixed
+- Boundary detection simplified #90
+- Prevent potential body overwriting #90
+- CSV files are no longer regarded as plain body
+- Boundary detection overhauled to support "related" and "alternative" multipart messages #90 #91
+
+### Affected Classes
+- [Structure::class](src/Structure.php)
+- [Message::class](src/Message.php)
+- [Header::class](src/Header.php)
+- [Part::class](src/Part.php)
+
+## [2.4.3] - 2021-01-21
+### Fixed
+- Attachment detection updated #82 #90
+- Timeout handling improved
+- Additional utf-8 checks added to prevent decoding of unencoded values #76
+
+### Added
+- Auto reconnect option added to `Folder::idle()` #89
+
+### Affected Classes
+- [Folder::class](src/Folder.php)
+- [Part::class](src/Part.php)
+- [Client::class](src/Client.php)
+- [Header::class](src/Header.php)
+
+## [2.4.2] - 2021-01-09
+### Fixed
+- Attachment::save() return error 'A facade root has not been set' #87
+- Unused dependencies removed
+- Fix PHP 8 error that changes null back in to an empty string. #88 (thanks @mennovanhout)
+- Fix regex to be case insensitive #88 (thanks @mennovanhout)
+
+### Affected Classes
+- [Attachment::class](src/Attachment.php)
+- [Address::class](src/Address.php)
+- [Attribute::class](src/Attribute.php)
+- [Structure::class](src/Structure.php)
+
+## [2.4.1] - 2021-01-06
+### Fixed
+- Debug line position fixed
+- Handle incomplete address to string conversion #83
+- Configured message key gets overwritten by the first fetched message #84
+
+### Affected Classes
+- [Address::class](src/Address.php)
+- [Query::class](src/Query/Query.php)
+
+## [2.4.0] - 2021-01-03
+### Fixed
+- Get partial overview when `IMAP::ST_UID` is set #74
+- Unnecessary "'" removed from address names
+- Folder referral typo fixed
+- Legacy protocol fixed
+- Treat message collection keys always as strings
+
+### Added
+- Configurable supported default flags added
+- Message attribute class added to unify value handling
+- Address class added and integrated
+- Alias `Message::attachments()` for `Message::getAttachments()` added
+- Alias `Message::addFlag()` for `Message::setFlag()` added
+- Alias `Message::removeFlag()` for `Message::unsetFlag()` added
+- Alias `Message::flags()` for `Message::getFlags()` added
+- New Exception `MessageFlagException::class` added
+- New method `Message::setSequenceId($id)` added 
+- Optional Header attributizion option added
+
+### Affected Classes
+- [Folder::class](src/Folder.php)
+- [Header::class](src/Header.php)
+- [Message::class](src/Message.php)
+- [Address::class](src/Address.php)
+- [Query::class](src/Query/Query.php)
+- [Attribute::class](src/Attribute.php)
+
+### Breaking changes
+- Stringified message headers are now separated by ", " instead of " ". 
+- All message header values such as subject, message_id, from, to, etc now consists of an `Àttribute::class` instance (should behave the same way as before, but might cause some problem in certain edge cases)
+- The formal address object "from", "to", etc now consists of an `Address::class` instance  (should behave the same way as before, but might cause some problem in certain edge cases)
+- When fetching or manipulating message flags a `MessageFlagException::class` exception can be thrown if a runtime error occurs
+- Learn more about the new `Attribute` class here: [www.php-imap.com/api/attribute](https://www.php-imap.com/api/attribute)
+- Learn more about the new `Address` class here: [www.php-imap.com/api/address](https://www.php-imap.com/api/address)
+- Folder attribute "referal" is now called "referral"
+
+## [2.3.1] - 2020-12-30
+### Fixed
+- Missing RFC attributes added 
+- Set the message sequence when idling
+- Missing UID commands added #64
+
+### Added
+- Get a message by its message number 
+- Get a message by its uid #72 #66 #63
+
+### Affected Classes
+- [Message::class](src/Message.php)
+- [Folder::class](src/Folder.php)
+- [Query::class](src/Query/Query.php)
+
+## [2.3.0] - 2020-12-21
+### Fixed
+- Cert validation issue fixed
+- Allow boundaries ending with a space or semicolon (thanks [@smartilabs](https://github.com/smartilabs))
+- Ignore IMAP DONE command response #57
+- Default `options.fetch` set to `IMAP::FT_PEEK`
+- Address parsing fixed #60
+- Alternative rfc822 header parsing fixed #60
+- Parse more than one Received: header #61
+- Fetch folder overview fixed
+- `Message::getTextBody()` fallback value fixed
+
+### Added
+- Proxy support added 
+- Flexible disposition support added #58
+- New `options.message_key` option `uid` added
+- Protocol UID support added
+- Flexible sequence type support added
+
+### Affected Classes
+- [Structure::class](src/Structure.php)
+- [Query::class](src/Query/Query.php)
+- [Client::class](src/Client.php)
+- [Header::class](src/Header.php)
+- [Folder::class](src/Folder.php)
+- [Part::class](src/Part.php)
+
+### Breaking changes
+- Depending on your configuration, your certificates actually get checked. Which can cause an aborted connection if the certificate can not be validated.
+- Messages don't get flagged as read unless you are using your own custom config.
+- All `Header::class` attribute keys are now in a snake_format and no longer minus-separated.
+- `Message::getTextBody()` no longer returns false if no text body is present. `null` is returned instead.
+
+## [2.2.5] - 2020-12-11
+### Fixed
+- Missing array decoder method added #51 (thanks [@lutchin](https://github.com/lutchin))
+- Additional checks added to prevent message from getting marked as seen #33
+- Boundary parsing improved #39 #36 (thanks [@AntonioDiPassio-AppSys](https://github.com/AntonioDiPassio-AppSys))
+- Idle operation updated #44
+
+### Added
+- Force a folder to be opened
+
+### Affected Classes
+- [Header::class](src/Header.php)
+- [Folder::class](src/Folder.php)
+- [Query::class](src/Query/Query.php)
+- [Message::class](src/Message.php)
+- [Structure::class](src/Structure.php)
+
+## [2.2.4] - 2020-12-08
+### Fixed
+- Search performance increased by fetching all headers, bodies and flags at once #42
+- Legacy protocol support updated
+- Fix Query pagination. (#52 [@mikemiller891](https://github.com/mikemiller891))
+
+### Added
+- Missing message setter methods added
+- `Folder::overview()` method added to fetch all headers of all messages in the current folder
+
+### Affected Classes
+- [Message::class](src/Message.php)
+- [Folder::class](src/Folder.php)
+- [Query::class](src/Query/Query.php)
+- [PaginatedCollection::class](src/Support/PaginatedCollection.php)
+
+## [2.2.3] - 2020-11-02
+### Fixed
+- Text/Html body fetched as attachment if subtype is null #34
+- Potential header overwriting through header extensions #35
+- Prevent empty attachments #37
+
+### Added
+- Set fetch order during query #41 [@Max13](https://github.com/Max13)
+
+### Affected Classes
+- [Message::class](src/Message.php)
+- [Part::class](src/Part.php)
+- [Header::class](src/Header.php)
+- [Query::class](src/Query/Query.php)
+
+
+## [2.2.2] - 2020-10-20
+### Fixed
+- IMAP::FT_PEEK removing "Seen" flag issue fixed #33
+
+### Affected Classes
+- [Message::class](src/Message.php)
+
+## [2.2.1] - 2020-10-19
+### Fixed
+- Header decoding problem fixed #31
+
+### Added
+- Search for messages by message-Id
+- Search for messages by In-Reply-To
+- Message threading added `Message::thread()`
+- Default folder locations added
+
+### Affected Classes
+- [Query::class](src/Query/Query.php)
+- [Message::class](src/Message.php)
+- [Header::class](src/Header.php)
+
+
+## [2.2.0] - 2020-10-16
+### Fixed
+- Prevent text bodies from being fetched as attachment #27
+- Missing variable check added to prevent exception while parsing an address [webklex/laravel-imap #356](https://github.com/Webklex/laravel-imap/issues/356)
+- Missing variable check added to prevent exception while parsing a part subtype #27
+- Missing variable check added to prevent exception while parsing a part content-type [webklex/laravel-imap #356](https://github.com/Webklex/laravel-imap/issues/356)
+- Mixed message header attribute `in_reply_to` "unified" to be always an array  #26
+- Potential message moving / copying problem fixed #29
+- Move messages by using `Protocol::moveMessage()` instead of `Protocol::copyMessage()` and `Message::delete()` #29
+
+### Added
+- `Protocol::moveMessage()` method added #29
+
+### Affected Classes
+- [Message::class](src/Message.php)
+- [Header::class](src/Header.php)
+- [Part::class](src/Part.php)
+
+### Breaking changes
+- Text bodies might no longer get fetched as attachment
+- `Message::$in_reply_to` type changed from mixed to array
+
+## [2.1.13] - 2020-10-13
+### Fixed
+-  Boundary detection problem fixed (#28  [@DasTobbel](https://github.com/DasTobbel))
+-  Content-Type detection problem fixed (#28  [@DasTobbel](https://github.com/DasTobbel))
+
+### Affected Classes
+- [Structure::class](src/Structure.php)
+
+## [2.1.12] - 2020-10-13
+### Fixed
+- If content disposition is multiline, implode the array to a simple string (#25 [@DasTobbel](https://github.com/DasTobbel))
+
+### Affected Classes
+- [Part::class](src/Part.php)
+
+## [2.1.11] - 2020-10-13
+### Fixed
+- Potential problematic prefixed white-spaces removed from header attributes
+
+### Added
+- Expended `Client::getFolder($name, $deleimiter = null)` to accept either a folder name or path ([@DasTobbel](https://github.com/DasTobbel))
+- Special MS-Exchange header decoding support added
+
+### Affected Classes
+- [Client::class](src/Client.php)
+- [Header::class](src/Header.php)
+
+## [2.1.10] - 2020-10-09
+### Added
+- `ClientManager::make()` method added to support undefined accounts
+
+### Affected Classes
+- [ClientManager::class](src/ClientManager.php)
+
+## [2.1.9] - 2020-10-08
+### Fixed
+- Fix inline attachments and embedded images (#22 [@dwalczyk](https://github.com/dwalczyk))
+
+### Added
+- Alternative attachment names support added (#20 [@oneFoldSoftware](https://github.com/oneFoldSoftware))
+- Fetch message content without leaving a "Seen" flag behind
+
+### Affected Classes
+- [Attachment::class](src/Attachment.php)
+- [Message::class](src/Message.php)
+- [Part::class](src/Part.php)
+- [Query::class](src/Query/Query.php)
+
+## [2.1.8] - 2020-10-08
+### Fixed
+- Possible error during address decoding fixed (#16 [@Slauta](https://github.com/Slauta))
+- Flag event dispatching fixed #15
+
+### Added
+- Support multiple boundaries (#17, #19 [@dwalczyk](https://github.com/dwalczyk))
+
+### Affected Classes
+- [Structure::class](src/Structure.php)
+
+## [2.1.7] - 2020-10-03
+### Fixed
+- Fixed `Query::paginate()` (#13 #14 by [@Max13](https://github.com/Max13))
+
+### Affected Classes
+- [Query::class](src/Query/Query.php)
+
+## [2.1.6] - 2020-10-02
+### Fixed
+- `Message::getAttributes()` hasn't returned all parameters
+
+### Affected Classes
+- [Message::class](src/Message.php)
+
+### Added
+- Part number added to attachment
+- `Client::getFolderByPath()` added (#12 by [@Max13](https://github.com/Max13))
+- `Client::getFolderByName()` added (#12 by [@Max13](https://github.com/Max13))
+- Throws exceptions if the authentication fails  (#11 by [@Max13](https://github.com/Max13))
+
+### Affected Classes
+- [Client::class](src/Client.php)
+
+## [2.1.5] - 2020-09-30
+### Fixed
+- Wrong message content property reference fixed (#10)
+
+## [2.1.4] - 2020-09-30
+### Fixed
+- Fix header extension values
+- Part header detection method changed (#10)
+
+### Affected Classes
+- [Header::class](src/Header.php)
+- [Part::class](src/Part.php)
+
+## [2.1.3] - 2020-09-29
+### Fixed
+- Possible decoding problem fixed
+- `Str::class` dependency removed from `Header::class`
+
+### Affected Classes
+- [Header::class](src/Header.php)
+
+## [2.1.2] - 2020-09-28
+### Fixed
+- Dependency problem in `Attachement::getExtension()` fixed (#9)
+
+### Affected Classes
+- [Attachment::class](src/Attachment.php)
+
+## [2.1.1] - 2020-09-23
+### Fixed
+- Missing default config parameter added
+
+### Added
+- Default account config fallback added
+
+### Affected Classes
+- [Client::class](src/Client.php)
+
+## [2.1.0] - 2020-09-22
+### Fixed
+- Quota handling fixed
+
+### Added
+- Event system and callbacks added
+
+### Affected Classes
+- [Client::class](src/Client.php)
+- [Folder::class](src/Folder.php)
+- [Message::class](src/Message.php)
+
+## [2.0.1] - 2020-09-20
+### Fixed
+- Carbon dependency fixed
+
+## [2.0.0] - 2020-09-20
+### Fixed
+- Missing pagination item records fixed
+
+### Added
+- php-imap module replaced by direct socket communication
+- Legacy support added
+- IDLE support added
+- oAuth support added
+- Charset detection method updated
+- Decoding fallback charsets added
+
+### Affected Classes
+- All
+
+## [1.4.5] - 2019-01-23
+### Fixed
+- .csv attachement is not processed
+- mail part structure property comparison changed to lowercase
+- Replace helper functions for Laravel 6.0 #4 (@koenhoeijmakers)
+- Date handling in Folder::appendMessage() fixed
+- Carbon Exception Parse Data
+- Convert sender name from non-utf8 to uf8 (@hwilok)
+- Convert encoding of personal data struct
+
+### Added
+- Path prefix option added to Client::getFolder() method
+- Attachment size handling added
+- Find messages by custom search criteria
+
+### Affected Classes
+- [Query::class](src/Query/WhereQuery.php)
+- [Mask::class](src/Support/Masks/Mask.php)
+- [Attachment::class](src/Attachment.php)
+- [Client::class](src/Client.php)
+- [Folder::class](src/Folder.php)
+- [Message::class](src/Message.php)
+
+## [1.4.2.1] - 2019-07-03
+### Fixed
+- Error in Attachment::__construct #3
+- Examples added
+
+## [1.4.2] - 2019-07-02
+### Fixed
+- Pagination count total bug #213
+- Changed internal message move and copy methods #210
+- Query::since() query returning empty response #215
+- Carbon Exception Parse Data #45
+- Reading a blank body (text / html) but only from this sender #203
+- Problem with Message::moveToFolder() and multiple moves #31
+- Problem with encoding conversion #203
+- Message null value attribute problem fixed
+- Client connection path handling changed to be handled inside the calling method #31
+- iconv(): error suppressor for //IGNORE added #184
+- Typo Folder attribute fullName changed to full_name
+- Query scope error fixed #153
+- Replace embedded image with URL #151
+- Fix sender name in non-latin emails sent from Gmail (#155)
+- Fix broken non-latin characters in body in ASCII (us-ascii) charset #156
+- Message::getMessageId() returns wrong value #197
+- Message date validation extended #45 #192
+- Removed "-i" from "iso-8859-8-i" in Message::parseBody #146
+
+### Added
+- Message::getFolder() method
+- Create a fast count method for queries #216
+- STARTTLS encryption alias added
+- Mailbox fetching exception added #201
+- Message::moveToFolder() fetches new Message::class afterwards #31
+- Message structure accessor added #182
+- Shadow Imap const class added #188
+- Connectable "NOT" queries added
+- Additional where methods added
+- Message attribute handling changed
+- Attachment attribute handling changed
+- Message flag handling updated
+- Message::getHTMLBody($callback) extended
+- Masks added (take look at the examples for more information on masks)
+- More examples added
+- Query::paginate() method added
+- Imap client timeout can be modified and read #186
+- Decoder config options added #175
+- Message search criteria "NOT" added #181
+- Invalid message date exception added 
+- Blade examples
+
+### Breaking changes
+- Message::moveToFolder() returns either a Message::class instance or null and not a boolean
+- Folder::fullName is now Folder::full_name
+- Attachment::image_src might no longer work as expected - use Attachment::getImageSrc() instead
+
+### Affected Classes
+- [Folder::class](src/Folder.php)
+- [Client::class](src/Client.php)
+- [Message::class](src/Message.php)
+- [Attachment::class](src/Attachment.php)
+- [Query::class](src/Query/Query.php)
+- [WhereQuery::class](src/Query/WhereQuery.php)
+
+## 0.0.3 - 2018-12-02
+### Fixed
+- Folder delimiter check added #137
+- Config setting not getting loaded
+- Date parsing updated
+
+### Affected Classes
+- [Folder::class](src/IMAP/Client.php)
+- [Folder::class](src/IMAP/Message.php)
+
+## 0.0.1 - 2018-08-13
+### Added
+- new php-imap package (fork from [webklex/laravel-imap](https://github.com/Webklex/laravel-imap))

+ 46 - 0
htdocs/includes/webklex/php-imap/CODE_OF_CONDUCT.md

@@ -0,0 +1,46 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at github@webklex.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
+
+[homepage]: http://contributor-covenant.org
+[version]: http://contributor-covenant.org/version/1/4/

+ 21 - 0
htdocs/includes/webklex/php-imap/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2016 Webklex
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 21 - 0
htdocs/includes/webklex/php-imap/LICENSE.md

@@ -0,0 +1,21 @@
+# The MIT License (MIT)
+
+Copyright (c) 2016 Malte Goldenbaum <info@webklex.com>
+
+> Permission is hereby granted, free of charge, to any person obtaining a copy
+> of this software and associated documentation files (the "Software"), to deal
+> in the Software without restriction, including without limitation the rights
+> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+> copies of the Software, and to permit persons to whom the Software is
+> furnished to do so, subject to the following conditions:
+>
+> The above copyright notice and this permission notice shall be included in
+> all copies or substantial portions of the Software.
+>
+> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+> THE SOFTWARE.

+ 158 - 0
htdocs/includes/webklex/php-imap/README.md

@@ -0,0 +1,158 @@
+
+# IMAP Library for PHP
+
+[![Latest Version on Packagist][ico-version]][link-packagist]
+[![Software License][ico-license]][link-license]
+[![Build Status][ico-travis]][link-scrutinizer] 
+[![Total Downloads][ico-downloads]][link-downloads]
+[![Hits][ico-hits]][link-hits]
+
+
+## Description
+PHP-IMAP is a wrapper for common IMAP communication without the need to have the php-imap module installed / enabled.
+The protocol is completely integrated and therefore supports IMAP IDLE operation and the "new" oAuth authentication 
+process as well.
+You can enable the `php-imap` module in order to handle edge cases, improve message decoding quality and is required if 
+you want to use legacy protocols such as pop3.
+
+Official documentation: [php-imap.com](https://www.php-imap.com/)
+
+Laravel wrapper: [webklex/laravel-imap](https://github.com/Webklex/laravel-imap)
+
+
+## Table of Contents
+- [Documentations](#documentations)
+- [Basic usage example](#basic-usage-example)
+- [Known issues](#known-issues)
+- [Support](#support)
+- [Features & pull requests](#features--pull-requests)
+- [Security](#security)
+- [Credits](#credits)
+- [License](#license)
+
+
+## Documentations
+- Legacy (< v2.0.0): [legacy documentation](https://github.com/Webklex/php-imap/tree/1.4.5)
+- Core documentation: [php-imap.com](https://www.php-imap.com/)
+
+
+## Basic usage example
+This is a basic example, which will echo out all Mails within all imap folders
+and will move every message into INBOX.read. Please be aware that this should not be
+tested in real life and is only meant to gives an impression on how things work.
+
+```php
+use Webklex\PHPIMAP\ClientManager;
+
+$cm = new ClientManager('path/to/config/imap.php');
+
+/** @var \Webklex\PHPIMAP\Client $client */
+$client = $cm->account('account_identifier');
+
+//Connect to the IMAP Server
+$client->connect();
+
+//Get all Mailboxes
+/** @var \Webklex\PHPIMAP\Support\FolderCollection $folders */
+$folders = $client->getFolders();
+
+//Loop through every Mailbox
+/** @var \Webklex\PHPIMAP\Folder $folder */
+foreach($folders as $folder){
+
+    //Get all Messages of the current Mailbox $folder
+    /** @var \Webklex\PHPIMAP\Support\MessageCollection $messages */
+    $messages = $folder->messages()->all()->get();
+    
+    /** @var \Webklex\PHPIMAP\Message $message */
+    foreach($messages as $message){
+        echo $message->getSubject().'<br />';
+        echo 'Attachments: '.$message->getAttachments()->count().'<br />';
+        echo $message->getHTMLBody();
+        
+        //Move the current Message to 'INBOX.read'
+        if($message->move('INBOX.read') == true){
+            echo 'Message has ben moved';
+        }else{
+            echo 'Message could not be moved';
+        }
+    }
+}
+```
+
+
+### Known issues 
+| Error                                                                     | Solution                                                   | 
+| ------------------------------------------------------------------------- | ---------------------------------------------------------- | 
+| Kerberos error: No credentials cache file found (try running kinit) (...) | Uncomment "DISABLE_AUTHENTICATOR" inside your config and use the `legacy-imap` protocol | 
+
+ 
+## Support 
+If you encounter any problems or if you find a bug, please don't hesitate to create a new [issue](https://github.com/Webklex/php-imap/issues). 
+However please be aware that it might take some time to get an answer. 
+Off topic, rude or abusive issues will be deleted without any notice. 
+ 
+If you need **commercial** support, feel free to send me a mail at github@webklex.com.  
+ 
+ 
+##### A little notice 
+If you write source code in your issue, please consider to format it correctly. This makes it so much nicer to read  
+and people are more likely to comment and help :) 
+ 
+&#96;&#96;&#96;php 
+
+echo 'your php code...'; 
+ 
+&#96;&#96;&#96; 
+ 
+will turn into: 
+```php 
+echo 'your php code...'; 
+``` 
+ 
+  
+## Features & pull requests 
+Everyone can contribute to this project. Every pull request will be considered but it can also happen to be declined.  
+To prevent unnecessary work, please consider to create a [feature issue](https://github.com/Webklex/php-imap/issues/new?template=feature_request.md)  
+first, if you're planning to do bigger changes. Of course you can also create a new [feature issue](https://github.com/Webklex/php-imap/issues/new?template=feature_request.md) 
+if you're just wishing a feature ;)   
+
+
+## Change log
+Please see [CHANGELOG][link-changelog] for more information what has changed recently.
+
+
+## Security
+If you discover any security related issues, please email github@webklex.com instead of using the issue tracker.
+
+
+## Credits
+- [Webklex][link-author]
+- [All Contributors][link-contributors]
+
+
+## License
+The MIT License (MIT). Please see [License File][link-license] for more information.
+
+
+[ico-version]: https://img.shields.io/packagist/v/Webklex/php-imap.svg?style=flat-square
+[ico-license]: https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square
+[ico-travis]: https://img.shields.io/travis/Webklex/php-imap/master.svg?style=flat-square
+[ico-scrutinizer]: https://img.shields.io/scrutinizer/coverage/g/Webklex/php-imap.svg?style=flat-square
+[ico-code-quality]: https://img.shields.io/scrutinizer/g/Webklex/php-imap.svg?style=flat-square
+[ico-downloads]: https://img.shields.io/packagist/dt/Webklex/php-imap.svg?style=flat-square
+[ico-build]: https://img.shields.io/scrutinizer/build/g/Webklex/php-imap/master?style=flat-square
+[ico-quality]: https://img.shields.io/scrutinizer/quality/g/Webklex/php-imap/master?style=flat-square
+[ico-hits]: https://hits.webklex.com/svg/webklex/php-imap
+
+[link-packagist]: https://packagist.org/packages/Webklex/php-imap
+[link-travis]: https://travis-ci.org/Webklex/php-imap
+[link-scrutinizer]: https://scrutinizer-ci.com/g/Webklex/php-imap/code-structure
+[link-code-quality]: https://scrutinizer-ci.com/g/Webklex/php-imap
+[link-downloads]: https://packagist.org/packages/Webklex/php-imap
+[link-author]: https://github.com/webklex
+[link-contributors]: https://github.com/Webklex/php-imap/graphs/contributors
+[link-license]: https://github.com/Webklex/php-imap/blob/master/LICENSE
+[link-changelog]: https://github.com/Webklex/php-imap/blob/master/CHANGELOG.md
+[link-jetbrains]: https://www.jetbrains.com
+[link-hits]: https://hits.webklex.com

+ 55 - 0
htdocs/includes/webklex/php-imap/composer.json

@@ -0,0 +1,55 @@
+{
+    "name": "webklex/php-imap",
+    "type": "library",
+    "description": "PHP IMAP client",
+    "keywords": [
+        "webklex",
+        "imap",
+        "pop3",
+        "php-imap",
+        "mail"
+    ],
+    "homepage": "https://github.com/webklex/php-imap",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Malte Goldenbaum",
+            "email": "github@webklex.com",
+            "role": "Developer"
+        }
+    ],
+    "require": {
+        "php": ">=5.5.9",
+        "ext-openssl": "*",
+        "ext-json": "*",
+        "ext-mbstring": "*",
+        "ext-iconv": "*",
+        "ext-fileinfo": "*",
+        "nesbot/carbon": ">=1.0",
+        "symfony/http-foundation": ">=2.8.0",
+        "illuminate/pagination": ">=5.0.0"
+    },
+    "require-dev": {
+        "phpunit/phpunit": "~4.0"
+    },
+    "autoload": {
+        "psr-4": {
+            "Webklex\\PHPIMAP\\": "src"
+        }
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "Tests\\": "tests"
+        }
+    },
+    "scripts": {
+        "test": "phpunit"
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "1.0-dev"
+        }
+    },
+    "minimum-stability": "dev",
+    "prefer-stable": true
+}

+ 16 - 0
htdocs/includes/webklex/php-imap/deleted.txt

@@ -0,0 +1,16 @@
+./vendor/carbon
+./vendor/nesbot/carbon/bin
+./vendor/phpunit
+./vendor/sebastian
+./vendor/psr
+./vendor/webmozart
+./vendor/symfony/http-foundation
+./vendor/symfony/translation
+./vendor/symfony/translation-contracts
+./vendor/symfony/yaml
+./vendor/voku
+./vendor/phpspec
+./vendor/phpdocumentor
+./vendor/nesbot/carbon/src/Carbon/Lang
+./vendor/doctrine
+./tests

+ 90 - 0
htdocs/includes/webklex/php-imap/src/Address.php

@@ -0,0 +1,90 @@
+<?php
+/*
+* File:     Address.php
+* Category: -
+* Author:   M. Goldenbaum
+* Created:  01.01.21 21:17
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP;
+
+/**
+ * Class Address
+ *
+ * @package Webklex\PHPIMAP
+ */
+class Address {
+
+    /**
+     * Address attributes
+     * @var string $personal
+     * @var string $mailbox
+     * @var string $host
+     * @var string $mail
+     * @var string $full
+     */
+    public $personal = "";
+    public $mailbox = "";
+    public $host = "";
+    public $mail = "";
+    public $full = "";
+
+    /**
+     * Address constructor.
+     * @param object   $object
+     */
+    public function __construct($object) {
+        if (property_exists($object, "personal")){ $this->personal = $object->personal; }
+        if (property_exists($object, "mailbox")){ $this->mailbox = $object->mailbox; }
+        if (property_exists($object, "host")){ $this->host = $object->host; }
+        if (property_exists($object, "mail")){ $this->mail = $object->mail; }
+        if (property_exists($object, "full")){ $this->full = $object->full; }
+    }
+
+
+    /**
+     * Return the stringified address
+     *
+     * @return string
+     */
+    public function __toString() {
+        return $this->full ? $this->full : "";
+    }
+
+    /**
+     * Return the serialized address
+     *
+     * @return array
+     */
+    public function __serialize(){
+        return [
+            "personal" => $this->personal,
+            "mailbox" => $this->mailbox,
+            "host" => $this->host,
+            "mail" => $this->mail,
+            "full" => $this->full,
+        ];
+    }
+
+    /**
+     * Convert instance to array
+     *
+     * @return array
+     */
+    public function toArray(){
+        return $this->__serialize();
+    }
+
+    /**
+     * Return the stringified attribute
+     *
+     * @return string
+     */
+    public function toString(){
+        return $this->__toString();
+    }
+}

+ 344 - 0
htdocs/includes/webklex/php-imap/src/Attachment.php

@@ -0,0 +1,344 @@
+<?php
+/*
+* File:     Attachment.php
+* Category: -
+* Author:   M. Goldenbaum
+* Created:  16.03.18 19:37
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP;
+
+use Illuminate\Support\Str;
+use Webklex\PHPIMAP\Exceptions\MaskNotFoundException;
+use Webklex\PHPIMAP\Exceptions\MethodNotFoundException;
+use Webklex\PHPIMAP\Support\Masks\AttachmentMask;
+
+/**
+ * Class Attachment
+ *
+ * @package Webklex\PHPIMAP
+ *
+ * @property integer part_number
+ * @property integer size
+ * @property string content
+ * @property string type
+ * @property string content_type
+ * @property string id
+ * @property string name
+ * @property string disposition
+ * @property string img_src
+ *
+ * @method integer getPartNumber()
+ * @method integer setPartNumber(integer $part_number)
+ * @method string  getContent()
+ * @method string  setContent(string $content)
+ * @method string  getType()
+ * @method string  setType(string $type)
+ * @method string  getContentType()
+ * @method string  setContentType(string $content_type)
+ * @method string  getId()
+ * @method string  setId(string $id)
+ * @method string  getSize()
+ * @method string  setSize(integer $size)
+ * @method string  getName()
+ * @method string  getDisposition()
+ * @method string  setDisposition(string $disposition)
+ * @method string  setImgSrc(string $img_src)
+ */
+class Attachment {
+
+    /**
+     * @var Message $oMessage
+     */
+    protected $oMessage;
+
+    /**
+     * Used config
+     *
+     * @var array $config
+     */
+    protected $config = [];
+
+    /** @var Part $part */
+    protected $part;
+
+    /**
+     * Attribute holder
+     *
+     * @var array $attributes
+     */
+    protected $attributes = [
+        'content' => null,
+        'type' => null,
+        'part_number' => 0,
+        'content_type' => null,
+        'id' => null,
+        'name' => null,
+        'disposition' => null,
+        'img_src' => null,
+        'size' => null,
+    ];
+
+    /**
+     * Default mask
+     *
+     * @var string $mask
+     */
+    protected $mask = AttachmentMask::class;
+
+    /**
+     * Attachment constructor.
+     * @param Message   $oMessage
+     * @param Part      $part
+     */
+    public function __construct(Message $oMessage, Part $part) {
+        $this->config = ClientManager::get('options');
+
+        $this->oMessage = $oMessage;
+        $this->part = $part;
+        $this->part_number = $part->part_number;
+
+        $default_mask = $this->oMessage->getClient()->getDefaultAttachmentMask();
+        if($default_mask != null) {
+            $this->mask = $default_mask;
+        }
+
+        $this->findType();
+        $this->fetch();
+    }
+
+    /**
+     * Call dynamic attribute setter and getter methods
+     * @param string $method
+     * @param array $arguments
+     *
+     * @return mixed
+     * @throws MethodNotFoundException
+     */
+    public function __call($method, $arguments) {
+        if(strtolower(substr($method, 0, 3)) === 'get') {
+            $name = Str::snake(substr($method, 3));
+
+            if(isset($this->attributes[$name])) {
+                return $this->attributes[$name];
+            }
+
+            return null;
+        }elseif (strtolower(substr($method, 0, 3)) === 'set') {
+            $name = Str::snake(substr($method, 3));
+
+            $this->attributes[$name] = array_pop($arguments);
+
+            return $this->attributes[$name];
+        }
+
+        throw new MethodNotFoundException("Method ".self::class.'::'.$method.'() is not supported');
+    }
+
+    /**
+     * Magic setter
+     * @param $name
+     * @param $value
+     *
+     * @return mixed
+     */
+    public function __set($name, $value) {
+        $this->attributes[$name] = $value;
+
+        return $this->attributes[$name];
+    }
+
+    /**
+     * magic getter
+     * @param $name
+     *
+     * @return mixed|null
+     */
+    public function __get($name) {
+        if(isset($this->attributes[$name])) {
+            return $this->attributes[$name];
+        }
+
+        return null;
+    }
+
+    /**
+     * Determine the structure type
+     */
+    protected function findType() {
+        switch ($this->part->type) {
+            case IMAP::ATTACHMENT_TYPE_MESSAGE:
+                $this->type = 'message';
+                break;
+            case IMAP::ATTACHMENT_TYPE_APPLICATION:
+                $this->type = 'application';
+                break;
+            case IMAP::ATTACHMENT_TYPE_AUDIO:
+                $this->type = 'audio';
+                break;
+            case IMAP::ATTACHMENT_TYPE_IMAGE:
+                $this->type = 'image';
+                break;
+            case IMAP::ATTACHMENT_TYPE_VIDEO:
+                $this->type = 'video';
+                break;
+            case IMAP::ATTACHMENT_TYPE_MODEL:
+                $this->type = 'model';
+                break;
+            case IMAP::ATTACHMENT_TYPE_TEXT:
+                $this->type = 'text';
+                break;
+            case IMAP::ATTACHMENT_TYPE_MULTIPART:
+                $this->type = 'multipart';
+                break;
+            default:
+                $this->type = 'other';
+                break;
+        }
+    }
+
+    /**
+     * Fetch the given attachment
+     */
+    protected function fetch() {
+
+        $content = $this->part->content;
+
+        $this->content_type = $this->part->content_type;
+        $this->content = $this->oMessage->decodeString($content, $this->part->encoding);
+
+        if (($id = $this->part->id) !== null) {
+            $this->id = str_replace(['<', '>'], '', $id);
+        }
+
+        $this->size = $this->part->bytes;
+        $this->disposition = $this->part->disposition;
+
+        if (($filename = $this->part->filename) !== null) {
+            $this->setName($filename);
+        } elseif (($name = $this->part->name) !== null) {
+            $this->setName($name);
+        }else {
+            $this->setName("undefined");
+        }
+
+        if (IMAP::ATTACHMENT_TYPE_MESSAGE == $this->part->type) {
+            if ($this->part->ifdescription) {
+                $this->setName($this->part->description);
+            } else {
+                $this->setName($this->part->subtype);
+            }
+        }
+    }
+
+    /**
+     * Save the attachment content to your filesystem
+     * @param string $path
+     * @param string|null $filename
+     *
+     * @return boolean
+     */
+    public function save($path, $filename = null) {
+        $filename = $filename ? $filename : $this->getName();
+
+        return file_put_contents($path.$filename, $this->getContent()) !== false;
+    }
+
+    /**
+     * Set the attachment name and try to decode it
+     * @param $name
+     */
+    public function setName($name) {
+        $decoder = $this->config['decoder']['attachment'];
+        if ($name !== null) {
+            if($decoder === 'utf-8' && extension_loaded('imap')) {
+                $this->name = \imap_utf8($name);
+            }else{
+                $this->name = mb_decode_mimeheader($name);
+            }
+        }
+    }
+
+    /**
+     * Get the attachment mime type
+     *
+     * @return string|null
+     */
+    public function getMimeType(){
+        return (new \finfo())->buffer($this->getContent(), FILEINFO_MIME_TYPE);
+    }
+
+    /**
+     * Try to guess the attachment file extension
+     *
+     * @return string|null
+     */
+    public function getExtension(){
+        $deprecated_guesser = "\Symfony\Component\HttpFoundation\File\MimeType\ExtensionGuesser";
+        if (class_exists($deprecated_guesser) !== false){
+            return $deprecated_guesser::getInstance()->guess($this->getMimeType());
+        }
+        $guesser = "\Symfony\Component\Mime\MimeTypes";
+        $extensions = $guesser::getDefault()->getExtensions($this->getMimeType());
+        return isset($extensions[0]) ? $extensions[0] : null;
+    }
+
+    /**
+     * Get all attributes
+     *
+     * @return array
+     */
+    public function getAttributes(){
+        return $this->attributes;
+    }
+
+    /**
+     * @return Message
+     */
+    public function getMessage(){
+        return $this->oMessage;
+    }
+
+    /**
+     * Set the default mask
+     * @param $mask
+     *
+     * @return $this
+     */
+    public function setMask($mask){
+        if(class_exists($mask)){
+            $this->mask = $mask;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get the used default mask
+     *
+     * @return string
+     */
+    public function getMask(){
+        return $this->mask;
+    }
+
+    /**
+     * Get a masked instance by providing a mask name
+     * @param string|null $mask
+     *
+     * @return mixed
+     * @throws MaskNotFoundException
+     */
+    public function mask($mask = null){
+        $mask = $mask !== null ? $mask : $this->mask;
+        if(class_exists($mask)){
+            return new $mask($this);
+        }
+
+        throw new MaskNotFoundException("Unknown mask provided: ".$mask);
+    }
+}

+ 274 - 0
htdocs/includes/webklex/php-imap/src/Attribute.php

@@ -0,0 +1,274 @@
+<?php
+/*
+* File:     Attribute.php
+* Category: -
+* Author:   M. Goldenbaum
+* Created:  01.01.21 20:17
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP;
+
+use ArrayAccess;
+use Carbon\Carbon;
+
+/**
+ * Class Attribute
+ *
+ * @package Webklex\PHPIMAP
+ */
+class Attribute implements ArrayAccess {
+
+    /** @var string $name */
+    protected $name;
+
+    /**
+     * Value holder
+     *
+     * @var array $values
+     */
+    protected $values = [];
+
+    /**
+     * Attribute constructor.
+     * @param string   $name
+     * @param array|mixed      $value
+     */
+    public function __construct($name, $value = null) {
+        $this->setName($name);
+        $this->add($value);
+    }
+
+
+    /**
+     * Return the stringified attribute
+     *
+     * @return string
+     */
+    public function __toString() {
+        return implode(", ", $this->values);
+    }
+
+    /**
+     * Return the stringified attribute
+     *
+     * @return string
+     */
+    public function toString(){
+        return $this->__toString();
+    }
+
+    /**
+     * Return the serialized attribute
+     *
+     * @return array
+     */
+    public function __serialize(){
+        return $this->values;
+    }
+
+    /**
+     * Convert instance to array
+     *
+     * @return array
+     */
+    public function toArray(){
+        return $this->__serialize();
+    }
+
+    /**
+     * Convert first value to a date object
+     *
+     * @return Carbon|null
+     */
+    public function toDate(){
+        $date = $this->first();
+        if ($date instanceof Carbon) return $date;
+
+        return Carbon::parse($date);
+    }
+
+    /**
+     * Determine if a value exists at an offset.
+     *
+     * @param  mixed  $key
+     * @return bool
+     */
+    public function offsetExists($key) {
+        return array_key_exists($key, $this->values);
+    }
+
+    /**
+     * Get a value at a given offset.
+     *
+     * @param  mixed  $key
+     * @return mixed
+     */
+    public function offsetGet($key) {
+        return $this->values[$key];
+    }
+
+    /**
+     * Set the value at a given offset.
+     *
+     * @param  mixed  $key
+     * @param  mixed  $value
+     * @return void
+     */
+    public function offsetSet($key, $value) {
+        if (is_null($key)) {
+            $this->values[] = $value;
+        } else {
+            $this->values[$key] = $value;
+        }
+    }
+
+    /**
+     * Unset the value at a given offset.
+     *
+     * @param  string  $key
+     * @return void
+     */
+    public function offsetUnset($key) {
+        unset($this->values[$key]);
+    }
+
+    /**
+     * Add one or more values to the attribute
+     * @param array|mixed $value
+     * @param boolean $strict
+     *
+     * @return Attribute
+     */
+    public function add($value, $strict = false) {
+        if (is_array($value)) {
+            return $this->merge($value, $strict);
+        }elseif ($value !== null) {
+            $this->attach($value, $strict);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Merge a given array of values with the current values array
+     * @param array $values
+     * @param boolean $strict
+     *
+     * @return Attribute
+     */
+    public function merge($values, $strict = false) {
+        if (is_array($values)) {
+            foreach ($values as $value) {
+                $this->attach($value, $strict);
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Check if the attribute contains the given value
+     * @param mixed $value
+     *
+     * @return bool
+     */
+    public function contains($value) {
+        foreach ($this->values as $v) {
+            if ($v === $value) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Attach a given value to the current value array
+     * @param $value
+     * @param bool $strict
+     */
+    public function attach($value, $strict = false) {
+        if ($strict === true) {
+            if ($this->contains($value) === false) {
+                $this->values[] = $value;
+            }
+        }else{
+            $this->values[] = $value;
+        }
+    }
+
+    /**
+     * Set the attribute name
+     * @param $name
+     *
+     * @return Attribute
+     */
+    public function setName($name){
+        $this->name = $name;
+
+        return $this;
+    }
+
+    /**
+     * Get the attribute name
+     *
+     * @return string
+     */
+    public function getName(){
+        return $this->name;
+    }
+
+    /**
+     * Get all values
+     *
+     * @return array
+     */
+    public function get(){
+        return $this->values;
+    }
+
+    /**
+     * Alias method for self::get()
+     *
+     * @return array
+     */
+    public function all(){
+        return $this->get();
+    }
+
+    /**
+     * Get the first value if possible
+     *
+     * @return mixed|null
+     */
+    public function first(){
+        if ($this->offsetExists(0)) {
+            return $this->values[0];
+        }
+        return null;
+    }
+
+    /**
+     * Get the last value if possible
+     *
+     * @return mixed|null
+     */
+    public function last(){
+        if (($cnt = $this->count()) > 0) {
+            return $this->values[$cnt - 1];
+        }
+        return null;
+    }
+
+    /**
+     * Get the number of values
+     *
+     * @return int
+     */
+    public function count(){
+        return count($this->values);
+    }
+}

+ 669 - 0
htdocs/includes/webklex/php-imap/src/Client.php

@@ -0,0 +1,669 @@
+<?php
+/*
+* File:     Client.php
+* Category: -
+* Author:   M. Goldenbaum
+* Created:  19.01.17 22:21
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP;
+
+use ErrorException;
+use Exception;
+use Webklex\PHPIMAP\Connection\Protocols\ImapProtocol;
+use Webklex\PHPIMAP\Connection\Protocols\LegacyProtocol;
+use Webklex\PHPIMAP\Connection\Protocols\Protocol;
+use Webklex\PHPIMAP\Connection\Protocols\ProtocolInterface;
+use Webklex\PHPIMAP\Exceptions\AuthFailedException;
+use Webklex\PHPIMAP\Exceptions\ConnectionFailedException;
+use Webklex\PHPIMAP\Exceptions\FolderFetchingException;
+use Webklex\PHPIMAP\Exceptions\MaskNotFoundException;
+use Webklex\PHPIMAP\Exceptions\ProtocolNotSupportedException;
+use Webklex\PHPIMAP\Support\FolderCollection;
+use Webklex\PHPIMAP\Support\Masks\AttachmentMask;
+use Webklex\PHPIMAP\Support\Masks\MessageMask;
+use Webklex\PHPIMAP\Traits\HasEvents;
+
+/**
+ * Class Client
+ *
+ * @package Webklex\PHPIMAP
+ */
+class Client {
+    use HasEvents;
+
+    /**
+     * Connection resource
+     *
+     * @var boolean|Protocol|ProtocolInterface
+     */
+    public $connection = false;
+
+    /**
+     * Server hostname.
+     *
+     * @var string
+     */
+    public $host;
+
+    /**
+     * Server port.
+     *
+     * @var int
+     */
+    public $port;
+
+    /**
+     * Service protocol.
+     *
+     * @var int
+     */
+    public $protocol;
+
+    /**
+     * Server encryption.
+     * Supported: none, ssl, tls, or notls.
+     *
+     * @var string
+     */
+    public $encryption;
+
+    /**
+     * If server has to validate cert.
+     *
+     * @var bool
+     */
+    public $validate_cert = true;
+
+    /**
+     * Proxy settings
+     * @var array
+     */
+    protected $proxy = [
+        'socket' => null,
+        'request_fulluri' => false,
+        'username' => null,
+        'password' => null,
+    ];
+
+    /**
+     * Connection timeout
+     * @var int $timeout
+     */
+    public $timeout;
+
+    /**
+     * Account username/
+     *
+     * @var mixed
+     */
+    public $username;
+
+    /**
+     * Account password.
+     *
+     * @var string
+     */
+    public $password;
+
+    /**
+     * Account authentication method.
+     *
+     * @var string
+     */
+    public $authentication;
+
+    /**
+     * Active folder path.
+     *
+     * @var string
+     */
+    protected $active_folder = null;
+
+    /**
+     * Default message mask
+     *
+     * @var string $default_message_mask
+     */
+    protected $default_message_mask = MessageMask::class;
+
+    /**
+     * Default attachment mask
+     *
+     * @var string $default_attachment_mask
+     */
+    protected $default_attachment_mask = AttachmentMask::class;
+
+    /**
+     * Used default account values
+     *
+     * @var array $default_account_config
+     */
+    protected $default_account_config = [
+        'host' => 'localhost',
+        'port' => 993,
+        'protocol'  => 'imap',
+        'encryption' => 'ssl',
+        'validate_cert' => true,
+        'username' => '',
+        'password' => '',
+        'authentication' => null,
+        'proxy' => [
+            'socket' => null,
+            'request_fulluri' => false,
+            'username' => null,
+            'password' => null,
+        ],
+        "timeout" => 30
+    ];
+
+    /**
+     * Client constructor.
+     * @param array $config
+     *
+     * @throws MaskNotFoundException
+     */
+    public function __construct($config = []) {
+        $this->setConfig($config);
+        $this->setMaskFromConfig($config);
+        $this->setEventsFromConfig($config);
+    }
+
+    /**
+     * Client destructor
+     */
+    public function __destruct() {
+        $this->disconnect();
+    }
+
+    /**
+     * Set the Client configuration
+     * @param array $config
+     *
+     * @return self
+     */
+    public function setConfig(array $config) {
+        $default_account = ClientManager::get('default');
+        $default_config  = ClientManager::get("accounts.$default_account");
+
+        foreach ($this->default_account_config as $key => $value) {
+            $this->setAccountConfig($key, $config, $default_config);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Set a specific account config
+     * @param string $key
+     * @param array $config
+     * @param array $default_config
+     */
+    private function setAccountConfig($key, $config, $default_config){
+        $value = $this->default_account_config[$key];
+        if(isset($config[$key])) {
+            $value = $config[$key];
+        }elseif(isset($default_config[$key])) {
+            $value = $default_config[$key];
+        }
+        $this->$key = $value;
+    }
+
+    /**
+     * Look for a possible events in any available config
+     * @param $config
+     */
+    protected function setEventsFromConfig($config) {
+        $this->events = ClientManager::get("events");
+        if(isset($config['events'])){
+            foreach($config['events'] as $section => $events) {
+                $this->events[$section] = array_merge($this->events[$section], $events);
+            }
+        }
+    }
+
+    /**
+     * Look for a possible mask in any available config
+     * @param $config
+     *
+     * @throws MaskNotFoundException
+     */
+    protected function setMaskFromConfig($config) {
+        $default_config  = ClientManager::get("masks");
+
+        if(isset($config['masks'])){
+            if(isset($config['masks']['message'])) {
+                if(class_exists($config['masks']['message'])) {
+                    $this->default_message_mask = $config['masks']['message'];
+                }else{
+                    throw new MaskNotFoundException("Unknown mask provided: ".$config['masks']['message']);
+                }
+            }else{
+                if(class_exists($default_config['message'])) {
+                    $this->default_message_mask = $default_config['message'];
+                }else{
+                    throw new MaskNotFoundException("Unknown mask provided: ".$default_config['message']);
+                }
+            }
+            if(isset($config['masks']['attachment'])) {
+                if(class_exists($config['masks']['attachment'])) {
+                    $this->default_attachment_mask = $config['masks']['attachment'];
+                }else{
+                    throw new MaskNotFoundException("Unknown mask provided: ".$config['masks']['attachment']);
+                }
+            }else{
+                if(class_exists($default_config['attachment'])) {
+                    $this->default_attachment_mask = $default_config['attachment'];
+                }else{
+                    throw new MaskNotFoundException("Unknown mask provided: ".$default_config['attachment']);
+                }
+            }
+        }else{
+            if(class_exists($default_config['message'])) {
+                $this->default_message_mask = $default_config['message'];
+            }else{
+                throw new MaskNotFoundException("Unknown mask provided: ".$default_config['message']);
+            }
+
+            if(class_exists($default_config['attachment'])) {
+                $this->default_attachment_mask = $default_config['attachment'];
+            }else{
+                throw new MaskNotFoundException("Unknown mask provided: ".$default_config['attachment']);
+            }
+        }
+
+    }
+
+    /**
+     * Get the current imap resource
+     *
+     * @return bool|Protocol|ProtocolInterface
+     * @throws ConnectionFailedException
+     */
+    public function getConnection() {
+        $this->checkConnection();
+        return $this->connection;
+    }
+
+    /**
+     * Determine if connection was established.
+     *
+     * @return bool
+     */
+    public function isConnected() {
+        return $this->connection ? $this->connection->connected() : false;
+    }
+
+    /**
+     * Determine if connection was established and connect if not.
+     *
+     * @throws ConnectionFailedException
+     */
+    public function checkConnection() {
+        if (!$this->isConnected()) {
+            $this->connect();
+        }
+    }
+
+    /**
+     * Force a reconnect
+     *
+     * @throws ConnectionFailedException
+     */
+    public function reconnect() {
+        if ($this->isConnected()) {
+            $this->disconnect();
+        }
+        $this->connect();
+    }
+
+    /**
+     * Connect to server.
+     *
+     * @return $this
+     * @throws ConnectionFailedException
+     */
+    public function connect() {
+        $this->disconnect();
+        $protocol = strtolower($this->protocol);
+
+        if (in_array($protocol, ['imap', 'imap4', 'imap4rev1'])) {
+            $this->connection = new ImapProtocol($this->validate_cert, $this->encryption);
+            $this->connection->setConnectionTimeout($this->timeout);
+            $this->connection->setProxy($this->proxy);
+        }else{
+            if (extension_loaded('imap') === false) {
+                throw new ConnectionFailedException("connection setup failed - no imap function", 0, new ProtocolNotSupportedException($protocol." is an unsupported protocol"));
+            }
+            $this->connection = new LegacyProtocol($this->validate_cert, $this->encryption);
+            if (strpos($protocol, "legacy-") === 0) {
+                $protocol = substr($protocol, 7);
+            }
+            $this->connection->setProtocol($protocol);
+        }
+
+        try {
+            $this->connection->connect($this->host, $this->port);
+        } catch (ErrorException $e) {
+            throw new ConnectionFailedException("connection setup failed - connect exception", 0, $e);
+        } catch (Exceptions\RuntimeException $e) {
+            throw new ConnectionFailedException("connection setup failed - run exception", 0, $e);
+        }
+        $this->authenticate();
+
+        return $this;
+    }
+
+    /**
+     * Authenticate the current session
+     *
+     * @throws ConnectionFailedException
+     */
+    protected function authenticate() {
+        try {
+            if ($this->authentication == "oauth") {
+                if (!$this->connection->authenticate($this->username, $this->password)) {
+                    throw new AuthFailedException();
+                }
+            } elseif (!$this->connection->login($this->username, $this->password)) {
+                throw new AuthFailedException();
+            }
+        } catch (AuthFailedException $e) {
+            throw new ConnectionFailedException("connection setup failed - authenticate", 0, $e);
+        }
+    }
+
+    /**
+     * Disconnect from server.
+     *
+     * @return $this
+     */
+    public function disconnect() {
+        if ($this->isConnected() && $this->connection !== false) {
+            $this->connection->logout();
+        }
+        $this->active_folder = null;
+
+        return $this;
+    }
+
+    /**
+     * Get a folder instance by a folder name
+     * @param string $folder_name
+     * @param string|bool|null $delimiter
+     *
+     * @return mixed
+     * @throws ConnectionFailedException
+     * @throws FolderFetchingException
+     * @throws Exceptions\RuntimeException
+     */
+    public function getFolder($folder_name, $delimiter = null) {
+        if ($delimiter !== false && $delimiter !== null) {
+            return $this->getFolderByPath($folder_name);
+        }
+
+        // Set delimiter to false to force selection via getFolderByName (maybe useful for uncommon folder names)
+        $delimiter = is_null($delimiter) ? ClientManager::get('options.delimiter', "/") : $delimiter;
+        if (strpos($folder_name, (string)$delimiter) !== false) {
+            return $this->getFolderByPath($folder_name);
+        }
+
+        return $this->getFolderByName($folder_name);
+    }
+
+    /**
+     * Get a folder instance by a folder name
+     * @param $folder_name
+     *
+     * @return mixed
+     * @throws ConnectionFailedException
+     * @throws FolderFetchingException
+     * @throws Exceptions\RuntimeException
+     */
+    public function getFolderByName($folder_name) {
+        return $this->getFolders(false)->where("name", $folder_name)->first();
+    }
+
+    /**
+     * Get a folder instance by a folder path
+     * @param $folder_path
+     *
+     * @return mixed
+     * @throws ConnectionFailedException
+     * @throws FolderFetchingException
+     * @throws Exceptions\RuntimeException
+     */
+    public function getFolderByPath($folder_path) {
+        return $this->getFolders(false)->where("path", $folder_path)->first();
+    }
+
+    /**
+     * Get folders list.
+     * If hierarchical order is set to true, it will make a tree of folders, otherwise it will return flat array.
+     *
+     * @param boolean $hierarchical
+     * @param string|null $parent_folder
+     *
+     * @return FolderCollection
+     * @throws ConnectionFailedException
+     * @throws FolderFetchingException
+     * @throws Exceptions\RuntimeException
+     */
+    public function getFolders($hierarchical = true, $parent_folder = null) {
+        $this->checkConnection();
+        $folders = FolderCollection::make([]);
+
+        $pattern = $parent_folder.($hierarchical ? '%' : '*');
+        $items = $this->connection->folders('', $pattern);
+
+        if(is_array($items)){
+            foreach ($items as $folder_name => $item) {
+                $folder = new Folder($this, $folder_name, $item["delimiter"], $item["flags"]);
+
+                if ($hierarchical && $folder->hasChildren()) {
+                    $pattern = $folder->full_name.$folder->delimiter.'%';
+
+                    $children = $this->getFolders(true, $pattern);
+                    $folder->setChildren($children);
+                }
+
+                $folders->push($folder);
+            }
+
+            return $folders;
+        }else{
+            throw new FolderFetchingException("failed to fetch any folders");
+        }
+    }
+
+    /**
+     * Open a given folder.
+     * @param string $folder_path
+     * @param boolean $force_select
+     *
+     * @return mixed
+     * @throws ConnectionFailedException
+     * @throws Exceptions\RuntimeException
+     */
+    public function openFolder($folder_path, $force_select = false) {
+        if ($this->active_folder == $folder_path && $this->isConnected() && $force_select === false) {
+            return true;
+        }
+        $this->checkConnection();
+        $this->active_folder = $folder_path;
+        return $this->connection->selectFolder($folder_path);
+    }
+
+    /**
+     * Create a new Folder
+     * @param string $folder
+     * @param boolean $expunge
+     *
+     * @return bool
+     * @throws ConnectionFailedException
+     * @throws FolderFetchingException
+     * @throws Exceptions\EventNotFoundException
+     * @throws Exceptions\RuntimeException
+     */
+    public function createFolder($folder, $expunge = true) {
+        $this->checkConnection();
+        $status = $this->connection->createFolder($folder);
+
+        if($expunge) $this->expunge();
+
+        $folder = $this->getFolder($folder);
+        if($status && $folder) {
+            $event = $this->getEvent("folder", "new");
+            $event::dispatch($folder);
+        }
+
+        return $folder;
+    }
+
+    /**
+     * Check a given folder
+     * @param $folder
+     *
+     * @return false|object
+     * @throws ConnectionFailedException
+     * @throws Exceptions\RuntimeException
+     */
+    public function checkFolder($folder) {
+        $this->checkConnection();
+        return $this->connection->examineFolder($folder);
+    }
+
+    /**
+     * Get the current active folder
+     *
+     * @return string
+     */
+    public function getFolderPath(){
+        return $this->active_folder;
+    }
+
+    /**
+     * Retrieve the quota level settings, and usage statics per mailbox
+     *
+     * @return array
+     * @throws ConnectionFailedException
+     * @throws Exceptions\RuntimeException
+     */
+    public function getQuota() {
+        $this->checkConnection();
+        return $this->connection->getQuota($this->username);
+    }
+
+    /**
+     * Retrieve the quota settings per user
+     * @param string $quota_root
+     *
+     * @return array
+     * @throws ConnectionFailedException
+     */
+    public function getQuotaRoot($quota_root = 'INBOX') {
+        $this->checkConnection();
+        return $this->connection->getQuotaRoot($quota_root);
+    }
+
+    /**
+     * Delete all messages marked for deletion
+     *
+     * @return bool
+     * @throws ConnectionFailedException
+     * @throws Exceptions\RuntimeException
+     */
+    public function expunge() {
+        $this->checkConnection();
+        return $this->connection->expunge();
+    }
+
+    /**
+     * Set the connection timeout
+     * @param integer $timeout
+     *
+     * @return Protocol
+     * @throws ConnectionFailedException
+     */
+    public function setTimeout($timeout) {
+        $this->checkConnection();
+        return $this->connection->setConnectionTimeout($timeout);
+    }
+
+    /**
+     * Get the connection timeout
+     *
+     * @return int
+     * @throws ConnectionFailedException
+     */
+    public function getTimeout(){
+        $this->checkConnection();
+        return $this->connection->getConnectionTimeout();
+    }
+
+    /**
+     * Get the default message mask
+     *
+     * @return string
+     */
+    public function getDefaultMessageMask(){
+        return $this->default_message_mask;
+    }
+
+    /**
+     * Get the default events for a given section
+     * @param $section
+     *
+     * @return array
+     */
+    public function getDefaultEvents($section){
+        return $this->events[$section];
+    }
+
+    /**
+     * Set the default message mask
+     * @param $mask
+     *
+     * @return $this
+     * @throws MaskNotFoundException
+     */
+    public function setDefaultMessageMask($mask) {
+        if(class_exists($mask)) {
+            $this->default_message_mask = $mask;
+
+            return $this;
+        }
+
+        throw new MaskNotFoundException("Unknown mask provided: ".$mask);
+    }
+
+    /**
+     * Get the default attachment mask
+     *
+     * @return string
+     */
+    public function getDefaultAttachmentMask(){
+        return $this->default_attachment_mask;
+    }
+
+    /**
+     * Set the default attachment mask
+     * @param $mask
+     *
+     * @return $this
+     * @throws MaskNotFoundException
+     */
+    public function setDefaultAttachmentMask($mask) {
+        if(class_exists($mask)) {
+            $this->default_attachment_mask = $mask;
+
+            return $this;
+        }
+
+        throw new MaskNotFoundException("Unknown mask provided: ".$mask);
+    }
+}

+ 262 - 0
htdocs/includes/webklex/php-imap/src/ClientManager.php

@@ -0,0 +1,262 @@
+<?php
+/*
+* File:     ClientManager.php
+* Category: -
+* Author:   M. Goldenbaum
+* Created:  19.01.17 22:21
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP;
+
+/**
+ * Class ClientManager
+ *
+ * @package Webklex\IMAP
+ *
+ * @mixin Client
+ */
+class ClientManager {
+
+    /**
+     * All library config
+     *
+     * @var array $config
+     */
+    public static $config = [];
+
+    /**
+     * @var array $accounts
+     */
+    protected $accounts = [];
+
+    /**
+     * ClientManager constructor.
+     * @param array|string $config
+     */
+    public function __construct($config = []) {
+        $this->setConfig($config);
+    }
+
+    /**
+     * Dynamically pass calls to the default account.
+     * @param  string  $method
+     * @param  array   $parameters
+     *
+     * @return mixed
+     * @throws Exceptions\MaskNotFoundException
+     */
+    public function __call($method, $parameters) {
+        $callable = [$this->account(), $method];
+
+        return call_user_func_array($callable, $parameters);
+    }
+
+    /**
+     * Safely create a new client instance which is not listed in accounts
+     * @param array $config
+     *
+     * @return Client
+     * @throws Exceptions\MaskNotFoundException
+     */
+    public function make($config) {
+        return new Client($config);
+    }
+
+    /**
+     * Get a dotted config parameter
+     * @param string $key
+     * @param null   $default
+     *
+     * @return mixed|null
+     */
+    public static function get($key, $default = null) {
+        $parts = explode('.', $key);
+        $value = null;
+        foreach($parts as $part) {
+            if($value === null) {
+                if(isset(self::$config[$part])) {
+                    $value = self::$config[$part];
+                }else{
+                    break;
+                }
+            }else{
+                if(isset($value[$part])) {
+                    $value = $value[$part];
+                }else{
+                    break;
+                }
+            }
+        }
+
+        return $value === null ? $default : $value;
+    }
+
+    /**
+     * Resolve a account instance.
+     * @param  string  $name
+     *
+     * @return Client
+     * @throws Exceptions\MaskNotFoundException
+     */
+    public function account($name = null) {
+        $name = $name ?: $this->getDefaultAccount();
+
+        // If the connection has not been resolved yet we will resolve it now as all
+        // of the connections are resolved when they are actually needed so we do
+        // not make any unnecessary connection to the various queue end-points.
+        if (!isset($this->accounts[$name])) {
+            $this->accounts[$name] = $this->resolve($name);
+        }
+
+        return $this->accounts[$name];
+    }
+
+    /**
+     * Resolve a account.
+     *
+     * @param  string  $name
+     *
+     * @return Client
+     * @throws Exceptions\MaskNotFoundException
+     */
+    protected function resolve($name) {
+        $config = $this->getClientConfig($name);
+
+        return new Client($config);
+    }
+
+    /**
+     * Get the account configuration.
+     * @param  string  $name
+     *
+     * @return array
+     */
+    protected function getClientConfig($name) {
+        if ($name === null || $name === 'null') {
+            return ['driver' => 'null'];
+        }
+
+        return self::$config["accounts"][$name];
+    }
+
+    /**
+     * Get the name of the default account.
+     *
+     * @return string
+     */
+    public function getDefaultAccount() {
+        return self::$config['default'];
+    }
+
+    /**
+     * Set the name of the default account.
+     * @param  string  $name
+     *
+     * @return void
+     */
+    public function setDefaultAccount($name) {
+        self::$config['default'] = $name;
+    }
+
+
+    /**
+     * Merge the vendor settings with the local config
+     *
+     * The default account identifier will be used as default for any missing account parameters.
+     * If however the default account is missing a parameter the package default account parameter will be used.
+     * This can be disabled by setting imap.default in your config file to 'false'
+     *
+     * @param array|string $config
+     *
+     * @return $this
+     */
+    public function setConfig($config) {
+
+        if(is_array($config) === false) {
+            $config = require $config;
+        }
+
+        $config_key = 'imap';
+        $path = __DIR__.'/config/'.$config_key.'.php';
+
+        $vendor_config = require $path;
+        $config = $this->array_merge_recursive_distinct($vendor_config, $config);
+
+        if(is_array($config)){
+            if(isset($config['default'])){
+                if(isset($config['accounts']) && $config['default'] != false){
+
+                    $default_config = $vendor_config['accounts']['default'];
+                    if(isset($config['accounts'][$config['default']])){
+                        $default_config = array_merge($default_config, $config['accounts'][$config['default']]);
+                    }
+
+                    if(is_array($config['accounts'])){
+                        foreach($config['accounts'] as $account_key => $account){
+                            $config['accounts'][$account_key] = array_merge($default_config, $account);
+                        }
+                    }
+                }
+            }
+        }
+
+        self::$config = $config;
+
+        return $this;
+    }
+
+    /**
+     * Marge arrays recursively and distinct
+     *
+     * Merges any number of arrays / parameters recursively, replacing
+     * entries with string keys with values from latter arrays.
+     * If the entry or the next value to be assigned is an array, then it
+     * automatically treats both arguments as an array.
+     * Numeric entries are appended, not replaced, but only if they are
+     * unique
+     *
+     * @param  array $array1 Initial array to merge.
+     * @param  array ...     Variable list of arrays to recursively merge.
+     *
+     * @return array|mixed
+     *
+     * @link   http://www.php.net/manual/en/function.array-merge-recursive.php#96201
+     * @author Mark Roduner <mark.roduner@gmail.com>
+     */
+    private function array_merge_recursive_distinct() {
+
+        $arrays = func_get_args();
+        $base = array_shift($arrays);
+
+        if(!is_array($base)) $base = empty($base) ? array() : array($base);
+
+        foreach($arrays as $append) {
+
+            if(!is_array($append)) $append = array($append);
+
+            foreach($append as $key => $value) {
+
+                if(!array_key_exists($key, $base) and !is_numeric($key)) {
+                    $base[$key] = $append[$key];
+                    continue;
+                }
+
+                if(is_array($value) or is_array($base[$key])) {
+                    $base[$key] = $this->array_merge_recursive_distinct($base[$key], $append[$key]);
+                } else if(is_numeric($key)) {
+                    if(!in_array($value, $base)) $base[] = $value;
+                } else {
+                    $base[$key] = $value;
+                }
+
+            }
+
+        }
+
+        return $base;
+    }
+}

+ 1074 - 0
htdocs/includes/webklex/php-imap/src/Connection/Protocols/ImapProtocol.php

@@ -0,0 +1,1074 @@
+<?php
+/*
+* File: ImapProtocol.php
+* Category: Protocol
+* Author: M.Goldenbaum
+* Created: 16.09.20 18:27
+* Updated: -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Connection\Protocols;
+
+use Exception;
+use Webklex\PHPIMAP\Exceptions\AuthFailedException;
+use Webklex\PHPIMAP\Exceptions\ConnectionFailedException;
+use Webklex\PHPIMAP\Exceptions\InvalidMessageDateException;
+use Webklex\PHPIMAP\Exceptions\MessageNotFoundException;
+use Webklex\PHPIMAP\Exceptions\RuntimeException;
+use Webklex\PHPIMAP\Header;
+
+/**
+ * Class ImapProtocol
+ *
+ * @package Webklex\PHPIMAP\Connection\Protocols
+ */
+class ImapProtocol extends Protocol {
+
+    /**
+     * Request noun
+     * @var int
+     */
+    protected $noun = 0;
+
+    /**
+     * Imap constructor.
+     * @param bool $cert_validation set to false to skip SSL certificate validation
+     * @param mixed $encryption Connection encryption method
+     */
+    public function __construct($cert_validation = true, $encryption = false) {
+        $this->setCertValidation($cert_validation);
+        $this->encryption = $encryption;
+    }
+
+    /**
+     * Public destructor
+     */
+    public function __destruct() {
+        $this->logout();
+    }
+
+    /**
+     * Open connection to IMAP server
+     * @param string $host hostname or IP address of IMAP server
+     * @param int|null $port of IMAP server, default is 143 and 993 for ssl
+     *
+     * @throws ConnectionFailedException
+     */
+    public function connect($host, $port = null) {
+        $transport = 'tcp';
+        $encryption = "";
+
+        if ($this->encryption) {
+            $encryption = strtolower($this->encryption);
+            if ($encryption == "ssl") {
+                $transport = 'ssl';
+                $port = $port === null ? 993 : $port;
+            }
+        }
+        $port = $port === null ? 143 : $port;
+        try {
+            $this->stream = $this->createStream($transport, $host, $port, $this->connection_timeout);
+            if (!$this->assumedNextLine('* OK')) {
+                throw new ConnectionFailedException('connection refused');
+            }
+            if ($encryption == "tls") {
+                $this->enableTls();
+            }
+        } catch (Exception $e) {
+            throw new ConnectionFailedException('connection failed', 0, $e);
+        }
+    }
+
+    /**
+     * Enable tls on the current connection
+     *
+     * @throws ConnectionFailedException
+     * @throws RuntimeException
+     */
+    protected function enableTls(){
+        $response = $this->requestAndResponse('STARTTLS');
+        $result = $response && stream_socket_enable_crypto($this->stream, true, $this->getCryptoMethod());
+        if (!$result) {
+            throw new ConnectionFailedException('failed to enable TLS');
+        }
+    }
+
+    /**
+     * Get the next line from stream
+     *
+     * @return string next line
+     * @throws RuntimeException
+     */
+    public function nextLine() {
+        $line = fgets($this->stream);
+
+        if ($line === false) {
+            throw new RuntimeException('failed to read - connection closed?');
+        }
+
+        return $line;
+    }
+
+    /**
+     * Get the next line and check if it starts with a given string
+     * @param string $start
+     *
+     * @return bool
+     * @throws RuntimeException
+     */
+    protected function assumedNextLine($start) {
+        $line = $this->nextLine();
+        return strpos($line, $start) === 0;
+    }
+
+    /**
+     * Get the next line and split the tag
+     * @param string $tag reference tag
+     *
+     * @return string next line
+     * @throws RuntimeException
+     */
+    protected function nextTaggedLine(&$tag) {
+        $line = $this->nextLine();
+        list($tag, $line) = explode(' ', $line, 2);
+
+        return $line;
+    }
+
+    /**
+     * Split a given line in values. A value is literal of any form or a list
+     * @param string $line
+     *
+     * @return array
+     * @throws RuntimeException
+     */
+    protected function decodeLine($line) {
+        $tokens = [];
+        $stack = [];
+
+        //  replace any trailing <NL> including spaces with a single space
+        $line = rtrim($line) . ' ';
+        while (($pos = strpos($line, ' ')) !== false) {
+            $token = substr($line, 0, $pos);
+            if (!strlen($token)) {
+                continue;
+            }
+            while ($token[0] == '(') {
+                array_push($stack, $tokens);
+                $tokens = [];
+                $token = substr($token, 1);
+            }
+            if ($token[0] == '"') {
+                if (preg_match('%^\(*"((.|\\\\|\\")*?)" *%', $line, $matches)) {
+                    $tokens[] = $matches[1];
+                    $line = substr($line, strlen($matches[0]));
+                    continue;
+                }
+            }
+            if ($token[0] == '{') {
+                $endPos = strpos($token, '}');
+                $chars = substr($token, 1, $endPos - 1);
+                if (is_numeric($chars)) {
+                    $token = '';
+                    while (strlen($token) < $chars) {
+                        $token .= $this->nextLine();
+                    }
+                    $line = '';
+                    if (strlen($token) > $chars) {
+                        $line = substr($token, $chars);
+                        $token = substr($token, 0, $chars);
+                    } else {
+                        $line .= $this->nextLine();
+                    }
+                    $tokens[] = $token;
+                    $line = trim($line) . ' ';
+                    continue;
+                }
+            }
+            if ($stack && $token[strlen($token) - 1] == ')') {
+                // closing braces are not separated by spaces, so we need to count them
+                $braces = strlen($token);
+                $token = rtrim($token, ')');
+                // only count braces if more than one
+                $braces -= strlen($token) + 1;
+                // only add if token had more than just closing braces
+                if (rtrim($token) != '') {
+                    $tokens[] = rtrim($token);
+                }
+                $token = $tokens;
+                $tokens = array_pop($stack);
+                // special handline if more than one closing brace
+                while ($braces-- > 0) {
+                    $tokens[] = $token;
+                    $token = $tokens;
+                    $tokens = array_pop($stack);
+                }
+            }
+            $tokens[] = $token;
+            $line = substr($line, $pos + 1);
+        }
+
+        // maybe the server forgot to send some closing braces
+        while ($stack) {
+            $child = $tokens;
+            $tokens = array_pop($stack);
+            $tokens[] = $child;
+        }
+
+        return $tokens;
+    }
+
+    /**
+     * Read abd decode a response "line"
+     * @param array|string $tokens to decode
+     * @param string $wantedTag targeted tag
+     * @param bool $dontParse if true only the unparsed line is returned in $tokens
+     *
+     * @return bool
+     * @throws RuntimeException
+     */
+    public function readLine(&$tokens = [], $wantedTag = '*', $dontParse = false) {
+        $line = $this->nextTaggedLine($tag); // get next tag
+        if (!$dontParse) {
+            $tokens = $this->decodeLine($line);
+        } else {
+            $tokens = $line;
+        }
+        if ($this->debug) echo "<< ".$line."\n";
+
+        // if tag is wanted tag we might be at the end of a multiline response
+        return $tag == $wantedTag;
+    }
+
+    /**
+     * Read all lines of response until given tag is found
+     * @param string $tag request tag
+     * @param bool $dontParse if true every line is returned unparsed instead of the decoded tokens
+     *
+     * @return void|null|bool|array tokens if success, false if error, null if bad request
+     * @throws RuntimeException
+     */
+    public function readResponse($tag, $dontParse = false) {
+        $lines = [];
+        $tokens = null; // define $tokens variable before first use
+        do {
+            $readAll = $this->readLine($tokens, $tag, $dontParse);
+            $lines[] = $tokens;
+        } while (!$readAll);
+
+        if ($dontParse) {
+            // First two chars are still needed for the response code
+            $tokens = [substr($tokens, 0, 2)];
+        }
+
+        // last line has response code
+        if ($tokens[0] == 'OK') {
+            return $lines ? $lines : true;
+        } elseif ($tokens[0] == 'NO') {
+            return false;
+        }
+
+        return;
+    }
+
+    /**
+     * Send a new request
+     * @param string $command
+     * @param array $tokens additional parameters to command, use escapeString() to prepare
+     * @param string $tag provide a tag otherwise an autogenerated is returned
+     *
+     * @throws RuntimeException
+     */
+    public function sendRequest($command, $tokens = [], &$tag = null) {
+        if (!$tag) {
+            $this->noun++;
+            $tag = 'TAG' . $this->noun;
+        }
+
+        $line = $tag . ' ' . $command;
+
+        foreach ($tokens as $token) {
+            if (is_array($token)) {
+                if (fwrite($this->stream, $line . ' ' . $token[0] . "\r\n") === false) {
+                    throw new RuntimeException('failed to write - connection closed?');
+                }
+                if (!$this->assumedNextLine('+ ')) {
+                    throw new RuntimeException('failed to send literal string');
+                }
+                $line = $token[1];
+            } else {
+                $line .= ' ' . $token;
+            }
+        }
+        if ($this->debug) echo ">> ".$line."\n";
+
+        if (fwrite($this->stream, $line . "\r\n") === false) {
+            throw new RuntimeException('failed to write - connection closed?');
+        }
+    }
+
+    /**
+     * Send a request and get response at once
+     * @param string $command
+     * @param array $tokens parameters as in sendRequest()
+     * @param bool $dontParse if true unparsed lines are returned instead of tokens
+     *
+     * @return void|null|bool|array response as in readResponse()
+     * @throws RuntimeException
+     */
+    public function requestAndResponse($command, $tokens = [], $dontParse = false) {
+        $this->sendRequest($command, $tokens, $tag);
+
+        return $this->readResponse($tag, $dontParse);
+    }
+
+    /**
+     * Escape one or more literals i.e. for sendRequest
+     * @param string|array $string the literal/-s
+     *
+     * @return string|array escape literals, literals with newline ar returned
+     *                      as array('{size}', 'string');
+     */
+    public function escapeString($string) {
+        if (func_num_args() < 2) {
+            if (strpos($string, "\n") !== false) {
+                return ['{' . strlen($string) . '}', $string];
+            } else {
+                return '"' . str_replace(['\\', '"'], ['\\\\', '\\"'], $string) . '"';
+            }
+        }
+        $result = [];
+        foreach (func_get_args() as $string) {
+            $result[] = $this->escapeString($string);
+        }
+        return $result;
+    }
+
+    /**
+     * Escape a list with literals or lists
+     * @param array $list list with literals or lists as PHP array
+     *
+     * @return string escaped list for imap
+     */
+    public function escapeList($list) {
+        $result = [];
+        foreach ($list as $v) {
+            if (!is_array($v)) {
+                $result[] = $v;
+                continue;
+            }
+            $result[] = $this->escapeList($v);
+        }
+        return '(' . implode(' ', $result) . ')';
+    }
+
+    /**
+     * Login to a new session.
+     * @param string $user username
+     * @param string $password password
+     *
+     * @return bool|mixed
+     * @throws AuthFailedException
+     */
+    public function login($user, $password) {
+        try {
+            return $this->requestAndResponse('LOGIN', $this->escapeString($user, $password), true);
+        } catch (RuntimeException $e) {
+            throw new AuthFailedException("failed to authenticate", 0, $e);
+        }
+    }
+
+    /**
+     * Authenticate your current IMAP session.
+     * @param string $user username
+     * @param string $token access token
+     *
+     * @return bool
+     * @throws AuthFailedException
+     */
+    public function authenticate($user, $token) {
+        try {
+            $authenticateParams = ['XOAUTH2', base64_encode("user=$user\1auth=Bearer $token\1\1")];
+            $this->sendRequest('AUTHENTICATE', $authenticateParams);
+
+            while (true) {
+                $response = "";
+                $is_plus = $this->readLine($response, '+', true);
+                if ($is_plus) {
+                    // try to log the challenge somewhere where it can be found
+                    error_log("got an extra server challenge: $response");
+                    // respond with an empty response.
+                    $this->sendRequest('');
+                } else {
+                    if (preg_match('/^NO /i', $response) ||
+                        preg_match('/^BAD /i', $response)) {
+                        error_log("got failure response: $response");
+                        return false;
+                    } else if (preg_match("/^OK /i", $response)) {
+                        return true;
+                    }
+                }
+            }
+        } catch (RuntimeException $e) {
+            throw new AuthFailedException("failed to authenticate", 0, $e);
+        }
+        return false;
+    }
+
+    /**
+     * Logout of imap server
+     *
+     * @return bool success
+     */
+    public function logout() {
+        $result = false;
+        if ($this->stream) {
+            try {
+                $result = $this->requestAndResponse('LOGOUT', [], true);
+            } catch (Exception $e) {}
+            fclose($this->stream);
+            $this->stream = null;
+        }
+        return $result;
+    }
+
+    /**
+     * Check if the current session is connected
+     *
+     * @return bool
+     */
+    public function connected(){
+        return (boolean) $this->stream;
+    }
+
+    /**
+     * Get an array of available capabilities
+     *
+     * @return array list of capabilities
+     * @throws RuntimeException
+     */
+    public function getCapabilities() {
+        $response = $this->requestAndResponse('CAPABILITY');
+
+        if (!$response) return [];
+
+        $capabilities = [];
+        foreach ($response as $line) {
+            $capabilities = array_merge($capabilities, $line);
+        }
+        return $capabilities;
+    }
+
+    /**
+     * Examine and select have the same response.
+     * @param string $command can be 'EXAMINE' or 'SELECT'
+     * @param string $folder target folder
+     *
+     * @return bool|array
+     * @throws RuntimeException
+     */
+    public function examineOrSelect($command = 'EXAMINE', $folder = 'INBOX') {
+        $this->sendRequest($command, [$this->escapeString($folder)], $tag);
+
+        $result = [];
+        $tokens = null; // define $tokens variable before first use
+        while (!$this->readLine($tokens, $tag)) {
+            if ($tokens[0] == 'FLAGS') {
+                array_shift($tokens);
+                $result['flags'] = $tokens;
+                continue;
+            }
+            switch ($tokens[1]) {
+                case 'EXISTS':
+                case 'RECENT':
+                    $result[strtolower($tokens[1])] = $tokens[0];
+                    break;
+                case '[UIDVALIDITY':
+                    $result['uidvalidity'] = (int)$tokens[2];
+                    break;
+                case '[UIDNEXT':
+                    $result['uidnext'] = (int)$tokens[2];
+                    break;
+                default:
+                    // ignore
+                    break;
+            }
+        }
+
+        if ($tokens[0] != 'OK') {
+            return false;
+        }
+        return $result;
+    }
+
+    /**
+     * Change the current folder
+     * @param string $folder change to this folder
+     *
+     * @return bool|array see examineOrselect()
+     * @throws RuntimeException
+     */
+    public function selectFolder($folder = 'INBOX') {
+        return $this->examineOrSelect('SELECT', $folder);
+    }
+
+    /**
+     * Examine a given folder
+     * @param string $folder examine this folder
+     *
+     * @return bool|array see examineOrselect()
+     * @throws RuntimeException
+     */
+    public function examineFolder($folder = 'INBOX') {
+        return $this->examineOrSelect('EXAMINE', $folder);
+    }
+
+    /**
+     * Fetch one or more items of one or more messages
+     * @param string|array $items items to fetch [RFC822.HEADER, FLAGS, RFC822.TEXT, etc]
+     * @param int|array $from message for items or start message if $to !== null
+     * @param int|null $to if null only one message ($from) is fetched, else it's the
+     *                             last message, INF means last message available
+     * @param bool $uid set to true if passing a unique id
+     *
+     * @return string|array if only one item of one message is fetched it's returned as string
+     *                      if items of one message are fetched it's returned as (name => value)
+     *                      if one items of messages are fetched it's returned as (msgno => value)
+     *                      if items of messages are fetched it's returned as (msgno => (name => value))
+     * @throws RuntimeException
+     */
+    protected function fetch($items, $from, $to = null, $uid = false) {
+        if (is_array($from)) {
+            $set = implode(',', $from);
+        } elseif ($to === null) {
+            $set = (int)$from;
+        } elseif ($to === INF) {
+            $set = (int)$from . ':*';
+        } else {
+            $set = (int)$from . ':' . (int)$to;
+        }
+
+        $items = (array)$items;
+        $itemList = $this->escapeList($items);
+
+        $this->sendRequest(($uid ? 'UID ' : '') . 'FETCH', [$set, $itemList], $tag);
+
+        $result = [];
+        $tokens = null; // define $tokens variable before first use
+        while (!$this->readLine($tokens, $tag)) {
+            // ignore other responses
+            if ($tokens[1] != 'FETCH') {
+                continue;
+            }
+
+            // find array key of UID value; try the last elements, or search for it
+            if ($uid) {
+                $count = count($tokens[2]);
+                if ($tokens[2][$count - 2] == 'UID') {
+                    $uidKey = $count - 1;
+                } else if ($tokens[2][0] == 'UID') {
+                    $uidKey = 1;
+                } else {
+                    $uidKey = array_search('UID', $tokens[2]) + 1;
+                }
+            }
+
+            // ignore other messages
+            if ($to === null && !is_array($from) && ($uid ? $tokens[2][$uidKey] != $from : $tokens[0] != $from)) {
+                continue;
+            }
+
+            // if we only want one item we return that one directly
+            if (count($items) == 1) {
+                if ($tokens[2][0] == $items[0]) {
+                    $data = $tokens[2][1];
+                } elseif ($uid && $tokens[2][2] == $items[0]) {
+                    $data = $tokens[2][3];
+                } else {
+                    // maybe the server send an other field we didn't wanted
+                    $count = count($tokens[2]);
+                    // we start with 2, because 0 was already checked
+                    for ($i = 2; $i < $count; $i += 2) {
+                        if ($tokens[2][$i] != $items[0]) {
+                            continue;
+                        }
+                        $data = $tokens[2][$i + 1];
+                        break;
+                    }
+                }
+            } else {
+                $data = [];
+                while (key($tokens[2]) !== null) {
+                    $data[current($tokens[2])] = next($tokens[2]);
+                    next($tokens[2]);
+                }
+            }
+
+            // if we want only one message we can ignore everything else and just return
+            if ($to === null && !is_array($from) && ($uid ? $tokens[2][$uidKey] == $from : $tokens[0] == $from)) {
+                // we still need to read all lines
+                while (!$this->readLine($tokens, $tag))
+
+                    return $data;
+            }
+            if ($uid) {
+                $result[$tokens[2][$uidKey]] = $data;
+            }else{
+                $result[$tokens[0]] = $data;
+            }
+        }
+
+        if ($to === null && !is_array($from)) {
+            throw new RuntimeException('the single id was not found in response');
+        }
+
+        return $result;
+    }
+
+    /**
+     * Fetch message headers
+     * @param array|int $uids
+     * @param string $rfc
+     * @param bool $uid set to true if passing a unique id
+     *
+     * @return array
+     * @throws RuntimeException
+     */
+    public function content($uids, $rfc = "RFC822", $uid = false) {
+        return $this->fetch(["$rfc.TEXT"], $uids, null, $uid);
+    }
+
+    /**
+     * Fetch message headers
+     * @param array|int $uids
+     * @param string $rfc
+     * @param bool $uid set to true if passing a unique id
+     *
+     * @return array
+     * @throws RuntimeException
+     */
+    public function headers($uids, $rfc = "RFC822", $uid = false){
+        return $this->fetch(["$rfc.HEADER"], $uids, null, $uid);
+    }
+
+    /**
+     * Fetch message flags
+     * @param array|int $uids
+     * @param bool $uid set to true if passing a unique id
+     *
+     * @return array
+     * @throws RuntimeException
+     */
+    public function flags($uids, $uid = false){
+        return $this->fetch(["FLAGS"], $uids, null, $uid);
+    }
+
+    /**
+     * Get uid for a given id
+     * @param int|null $id message number
+     *
+     * @return array|string message number for given message or all messages as array
+     * @throws MessageNotFoundException
+     */
+    public function getUid($id = null) {
+        try {
+            $uids = $this->fetch('UID', 1, INF);
+            if ($id == null) {
+                return $uids;
+            }
+
+            foreach ($uids as $k => $v) {
+                if ($k == $id) {
+                    return $v;
+                }
+            }
+        } catch (RuntimeException $e) {}
+
+        throw new MessageNotFoundException('unique id not found');
+    }
+
+    /**
+     * Get a message number for a uid
+     * @param string $id uid
+     *
+     * @return int message number
+     * @throws MessageNotFoundException
+     */
+    public function getMessageNumber($id) {
+        $ids = $this->getUid();
+        foreach ($ids as $k => $v) {
+            if ($v == $id) {
+                return $k;
+            }
+        }
+
+        throw new MessageNotFoundException('message number not found');
+    }
+
+    /**
+     * Get a list of available folders
+     * @param string $reference mailbox reference for list
+     * @param string $folder mailbox name match with wildcards
+     *
+     * @return array folders that matched $folder as array(name => array('delimiter' => .., 'flags' => ..))
+     * @throws RuntimeException
+     */
+    public function folders($reference = '', $folder = '*') {
+        $result = [];
+        $list = $this->requestAndResponse('LIST', $this->escapeString($reference, $folder));
+        if (!$list || $list === true) {
+            return $result;
+        }
+
+        foreach ($list as $item) {
+            if (count($item) != 4 || $item[0] != 'LIST') {
+                continue;
+            }
+            $result[$item[3]] = ['delimiter' => $item[2], 'flags' => $item[1]];
+        }
+
+        return $result;
+    }
+
+    /**
+     * Manage flags
+     * @param array $flags flags to set, add or remove - see $mode
+     * @param int $from message for items or start message if $to !== null
+     * @param int|null $to if null only one message ($from) is fetched, else it's the
+     *                             last message, INF means last message available
+     * @param string|null $mode '+' to add flags, '-' to remove flags, everything else sets the flags as given
+     * @param bool $silent if false the return values are the new flags for the wanted messages
+     * @param bool $uid set to true if passing a unique id
+     *
+     * @return bool|array new flags if $silent is false, else true or false depending on success
+     * @throws RuntimeException
+     */
+    public function store(array $flags, $from, $to = null, $mode = null, $silent = true, $uid = false) {
+        $item = 'FLAGS';
+        if ($mode == '+' || $mode == '-') {
+            $item = $mode . $item;
+        }
+        if ($silent) {
+            $item .= '.SILENT';
+        }
+
+        $flags = $this->escapeList($flags);
+        $set = (int)$from;
+        if ($to !== null) {
+            $set .= ':' . ($to == INF ? '*' : (int)$to);
+        }
+
+        $command = ($uid ? "UID " : "")."STORE";
+        $result = $this->requestAndResponse($command, [$set, $item, $flags], $silent);
+
+        if ($silent) {
+            return (bool)$result;
+        }
+
+        $tokens = $result;
+        $result = [];
+        foreach ($tokens as $token) {
+            if ($token[1] != 'FETCH' || $token[2][0] != 'FLAGS') {
+                continue;
+            }
+            $result[$token[0]] = $token[2][1];
+        }
+
+        return $result;
+    }
+
+    /**
+     * Append a new message to given folder
+     * @param string $folder name of target folder
+     * @param string $message full message content
+     * @param array $flags flags for new message
+     * @param string $date date for new message
+     *
+     * @return bool success
+     * @throws RuntimeException
+     */
+    public function appendMessage($folder, $message, $flags = null, $date = null) {
+        $tokens = [];
+        $tokens[] = $this->escapeString($folder);
+        if ($flags !== null) {
+            $tokens[] = $this->escapeList($flags);
+        }
+        if ($date !== null) {
+            $tokens[] = $this->escapeString($date);
+        }
+        $tokens[] = $this->escapeString($message);
+
+        return $this->requestAndResponse('APPEND', $tokens, true);
+    }
+
+    /**
+     * Copy a message set from current folder to an other folder
+     * @param string $folder destination folder
+     * @param $from
+     * @param int|null $to if null only one message ($from) is fetched, else it's the
+     *                         last message, INF means last message available
+     * @param bool $uid set to true if passing a unique id
+     *
+     * @return bool success
+     * @throws RuntimeException
+     */
+    public function copyMessage($folder, $from, $to = null, $uid = false) {
+        $set = (int)$from;
+        if ($to !== null) {
+            $set .= ':' . ($to == INF ? '*' : (int)$to);
+        }
+        $command = ($uid ? "UID " : "")."COPY";
+
+        return $this->requestAndResponse($command, [$set, $this->escapeString($folder)], true);
+    }
+
+    /**
+     * Copy multiple messages to the target folder
+     *
+     * @param array<string> $messages List of message identifiers
+     * @param string $folder Destination folder
+     * @param bool $uid Set to true if you pass message unique identifiers instead of numbers
+     * @return array|bool Tokens if operation successful, false if an error occurred
+     *
+     * @throws RuntimeException
+     */
+    public function copyManyMessages($messages, $folder, $uid = false) {
+        $command = $uid ? 'UID COPY' : 'COPY';
+
+        $set = implode(',', $messages);
+        $tokens = [$set, $this->escapeString($folder)];
+
+        return $this->requestAndResponse($command, $tokens, true);
+    }
+
+    /**
+     * Move a message set from current folder to an other folder
+     * @param string $folder destination folder
+     * @param $from
+     * @param int|null $to if null only one message ($from) is fetched, else it's the
+     *                         last message, INF means last message available
+     * @param bool $uid set to true if passing a unique id
+     *
+     * @return bool success
+     * @throws RuntimeException
+     */
+    public function moveMessage($folder, $from, $to = null, $uid = false) {
+        $set = (int)$from;
+        if ($to !== null) {
+            $set .= ':' . ($to == INF ? '*' : (int)$to);
+        }
+        $command = ($uid ? "UID " : "")."MOVE";
+
+        return $this->requestAndResponse($command, [$set, $this->escapeString($folder)], true);
+    }
+
+    /**
+     * Move multiple messages to the target folder
+     *
+     * @param array<string> $messages List of message identifiers
+     * @param string $folder Destination folder
+     * @param bool $uid Set to true if you pass message unique identifiers instead of numbers
+     * @return array|bool Tokens if operation successful, false if an error occurred
+     *
+     * @throws RuntimeException
+     */
+    public function moveManyMessages($messages, $folder, $uid = false) {
+        $command = $uid ? 'UID MOVE' : 'MOVE';
+
+        $set = implode(',', $messages);
+        $tokens = [$set, $this->escapeString($folder)];
+
+        return $this->requestAndResponse($command, $tokens, true);
+    }
+
+    /**
+     * Create a new folder (and parent folders if needed)
+     * @param string $folder folder name
+     *
+     * @return bool success
+     * @throws RuntimeException
+     */
+    public function createFolder($folder) {
+        return $this->requestAndResponse('CREATE', [$this->escapeString($folder)], true);
+    }
+
+    /**
+     * Rename an existing folder
+     * @param string $old old name
+     * @param string $new new name
+     *
+     * @return bool success
+     * @throws RuntimeException
+     */
+    public function renameFolder($old, $new) {
+        return $this->requestAndResponse('RENAME', $this->escapeString($old, $new), true);
+    }
+
+    /**
+     * Delete a folder
+     * @param string $folder folder name
+     *
+     * @return bool success
+     * @throws RuntimeException
+     */
+    public function deleteFolder($folder) {
+        return $this->requestAndResponse('DELETE', [$this->escapeString($folder)], true);
+    }
+
+    /**
+     * Subscribe to a folder
+     * @param string $folder folder name
+     *
+     * @return bool success
+     * @throws RuntimeException
+     */
+    public function subscribeFolder($folder) {
+        return $this->requestAndResponse('SUBSCRIBE', [$this->escapeString($folder)], true);
+    }
+
+    /**
+     * Unsubscribe from a folder
+     * @param string $folder folder name
+     *
+     * @return bool success
+     * @throws RuntimeException
+     */
+    public function unsubscribeFolder($folder) {
+        return $this->requestAndResponse('UNSUBSCRIBE', [$this->escapeString($folder)], true);
+    }
+
+    /**
+     * Apply session saved changes to the server
+     *
+     * @return bool success
+     * @throws RuntimeException
+     */
+    public function expunge() {
+        return $this->requestAndResponse('EXPUNGE');
+    }
+
+    /**
+     * Send noop command
+     *
+     * @return bool success
+     * @throws RuntimeException
+     */
+    public function noop() {
+        return $this->requestAndResponse('NOOP');
+    }
+
+    /**
+     * Retrieve the quota level settings, and usage statics per mailbox
+     * @param $username
+     *
+     * @return array
+     * @throws RuntimeException
+     */
+    public function getQuota($username) {
+        return $this->requestAndResponse("GETQUOTA", ['"#user/'.$username.'"']);
+    }
+
+    /**
+     * Retrieve the quota settings per user
+     * @param string $quota_root
+     *
+     * @return array
+     * @throws RuntimeException
+     */
+    public function getQuotaRoot($quota_root = 'INBOX') {
+        return $this->requestAndResponse("QUOTA", [$quota_root]);
+    }
+
+    /**
+     * Send idle command
+     *
+     * @throws RuntimeException
+     */
+    public function idle() {
+        $this->sendRequest("IDLE");
+        if (!$this->assumedNextLine('+ ')) {
+            throw new RuntimeException('idle failed');
+        }
+    }
+
+    /**
+     * Send done command
+     * @throws RuntimeException
+     */
+    public function done() {
+        if (fwrite($this->stream, "DONE\r\n") === false) {
+            throw new RuntimeException('failed to write - connection closed?');
+        }
+        return true;
+    }
+
+    /**
+     * Search for matching messages
+     * @param array $params
+     * @param bool $uid set to true if passing a unique id
+     *
+     * @return array message ids
+     * @throws RuntimeException
+     */
+    public function search(array $params, $uid = false) {
+        $token = $uid == true ? "UID SEARCH" : "SEARCH";
+        $response = $this->requestAndResponse($token, $params);
+        if (!$response) {
+            return $response;
+        }
+
+        foreach ($response as $ids) {
+            if ($ids[0] == 'SEARCH') {
+                array_shift($ids);
+                return $ids;
+            }
+        }
+        return [];
+    }
+
+    /**
+     * Get a message overview
+     * @param string $sequence
+     * @param bool $uid set to true if passing a unique id
+     *
+     * @return array
+     * @throws RuntimeException
+     * @throws MessageNotFoundException
+     * @throws InvalidMessageDateException
+     */
+    public function overview($sequence, $uid = false) {
+        $result = [];
+        list($from, $to) = explode(":", $sequence);
+
+        $uids = $this->getUid();
+        $ids = [];
+        foreach ($uids as $msgn => $v) {
+            $id = $uid ? $v : $msgn;
+            if ( ($to >= $id && $from <= $id) || ($to === "*" && $from <= $id) ){
+                $ids[] = $id;
+            }
+        }
+        $headers = $this->headers($ids, "RFC822", $uid);
+        foreach ($headers as $id => $raw_header) {
+            $result[$id] = (new Header($raw_header, false))->getAttributes();
+        }
+        return $result;
+    }
+
+    /**
+     * Enable the debug mode
+     */
+    public function enableDebug(){
+        $this->debug = true;
+    }
+
+    /**
+     * Disable the debug mode
+     */
+    public function disableDebug(){
+        $this->debug = false;
+    }
+}

+ 607 - 0
htdocs/includes/webklex/php-imap/src/Connection/Protocols/LegacyProtocol.php

@@ -0,0 +1,607 @@
+<?php
+/*
+* File: LegacyProtocol.php
+* Category: Protocol
+* Author: M.Goldenbaum
+* Created: 16.09.20 18:27
+* Updated: -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Connection\Protocols;
+
+use Webklex\PHPIMAP\ClientManager;
+use Webklex\PHPIMAP\Exceptions\AuthFailedException;
+use Webklex\PHPIMAP\Exceptions\MethodNotSupportedException;
+use Webklex\PHPIMAP\Exceptions\RuntimeException;
+use Webklex\PHPIMAP\IMAP;
+
+/**
+ * Class LegacyProtocol
+ *
+ * @package Webklex\PHPIMAP\Connection\Protocols
+ */
+class LegacyProtocol extends Protocol {
+
+    protected $protocol = "imap";
+    protected $host = null;
+    protected $port = null;
+    protected $encryption = null;
+
+    /**
+     * Imap constructor.
+     * @param bool $cert_validation set to false to skip SSL certificate validation
+     * @param mixed $encryption Connection encryption method
+     */
+    public function __construct($cert_validation = true, $encryption = false) {
+        $this->setCertValidation($cert_validation);
+        $this->encryption = $encryption;
+    }
+
+    /**
+     * Public destructor
+     */
+    public function __destruct() {
+        $this->logout();
+    }
+
+    /**
+     * Save the information for a nw connection
+     * @param string $host
+     * @param null $port
+     */
+    public function connect($host, $port = null) {
+        if ($this->encryption) {
+            $encryption = strtolower($this->encryption);
+            if ($encryption == "ssl") {
+                $port = $port === null ? 993 : $port;
+            }
+        }
+        $port = $port === null ? 143 : $port;
+        $this->host = $host;
+        $this->port = $port;
+    }
+
+    /**
+     * Login to a new session.
+     * @param string $user username
+     * @param string $password password
+     *
+     * @return bool
+     * @throws AuthFailedException
+     * @throws RuntimeException
+     */
+    public function login($user, $password) {
+        try {
+            $this->stream = \imap_open(
+                $this->getAddress(),
+                $user,
+                $password,
+                0,
+                $attempts = 3,
+                ClientManager::get('options.open')
+            );
+        } catch (\ErrorException $e) {
+            $errors = \imap_errors();
+            $message = $e->getMessage().'. '.implode("; ", (is_array($errors) ? $errors : array()));
+            throw new AuthFailedException($message);
+        }
+
+        if(!$this->stream) {
+            $errors = \imap_errors();
+            $message = implode("; ", (is_array($errors) ? $errors : array()));
+            throw new AuthFailedException($message);
+        }
+
+        $errors = \imap_errors();
+        if(is_array($errors)) {
+            $status = $this->examineFolder();
+            if($status['exists'] !== 0) {
+                $message = implode("; ", (is_array($errors) ? $errors : array()));
+                throw new RuntimeException($message);
+            }
+        }
+
+        return $this->stream;
+    }
+
+    /**
+     * Authenticate your current session.
+     * @param string $user username
+     * @param string $token access token
+     *
+     * @return bool|resource
+     * @throws AuthFailedException|RuntimeException
+     */
+    public function authenticate($user, $token) {
+        return $this->login($user, $token);
+    }
+
+    /**
+     * Get full address of mailbox.
+     *
+     * @return string
+     */
+    protected function getAddress() {
+        $address = "{".$this->host.":".$this->port."/".$this->protocol;
+        if (!$this->cert_validation) {
+            $address .= '/novalidate-cert';
+        }
+        if (in_array($this->encryption,['tls', 'notls', 'ssl'])) {
+            $address .= '/'.$this->encryption;
+        } elseif ($this->encryption === "starttls") {
+            $address .= '/tls';
+        }
+
+        $address .= '}';
+
+        return $address;
+    }
+
+    /**
+     * Logout of the current session
+     *
+     * @return bool success
+     */
+    public function logout() {
+        if ($this->stream) {
+            $result = \imap_close($this->stream, IMAP::CL_EXPUNGE);
+            $this->stream = false;
+            return $result;
+        }
+        return false;
+    }
+
+    /**
+     * Check if the current session is connected
+     *
+     * @return bool
+     */
+    public function connected(){
+        return boolval($this->stream);
+    }
+
+    /**
+     * Get an array of available capabilities
+     *
+     * @throws MethodNotSupportedException
+     */
+    public function getCapabilities() {
+        throw new MethodNotSupportedException();
+    }
+
+    /**
+     * Change the current folder
+     * @param string $folder change to this folder
+     *
+     * @return bool|array see examineOrselect()
+     * @throws RuntimeException
+     */
+    public function selectFolder($folder = 'INBOX') {
+        \imap_reopen($this->stream, $folder, IMAP::OP_READONLY, 3);
+        return $this->examineFolder($folder);
+    }
+
+    /**
+     * Examine a given folder
+     * @param string $folder examine this folder
+     *
+     * @return bool|array
+     * @throws RuntimeException
+     */
+    public function examineFolder($folder = 'INBOX') {
+        if (strpos($folder, ".") === 0) {
+            throw new RuntimeException("Segmentation fault prevented. Folders starts with an illegal char '.'.");
+        }
+        $folder = $this->getAddress().$folder;
+        $status = \imap_status($this->stream, $folder, IMAP::SA_ALL);
+        return [
+            "flags" => [],
+            "exists" => $status->messages,
+            "recent" => $status->recent,
+            "unseen" => $status->unseen,
+            "uidnext" => $status->uidnext,
+        ];
+    }
+
+    /**
+     * Fetch message content
+     * @param array|int $uids
+     * @param string $rfc
+     * @param bool $uid set to true if passing a unique id
+     *
+     * @return array
+     */
+    public function content($uids, $rfc = "RFC822", $uid = false) {
+        $result = [];
+        $uids = is_array($uids) ? $uids : [$uids];
+        foreach ($uids as $id) {
+            $result[$id] = \imap_fetchbody($this->stream, $id, "", $uid ? IMAP::FT_UID : IMAP::NIL);
+        }
+        return $result;
+    }
+
+    /**
+     * Fetch message headers
+     * @param array|int $uids
+     * @param string $rfc
+     * @param bool $uid set to true if passing a unique id
+     *
+     * @return array
+     */
+    public function headers($uids, $rfc = "RFC822", $uid = false){
+        $result = [];
+        $uids = is_array($uids) ? $uids : [$uids];
+        foreach ($uids as $id) {
+            $result[$id] = \imap_fetchheader($this->stream, $id, $uid ? IMAP::FT_UID : IMAP::NIL);
+        }
+        return $result;
+    }
+
+    /**
+     * Fetch message flags
+     * @param array|int $uids
+     * @param bool $uid set to true if passing a unique id
+     *
+     * @return array
+     */
+    public function flags($uids, $uid = false){
+        $result = [];
+        $uids = is_array($uids) ? $uids : [$uids];
+        foreach ($uids as $id) {
+            $raw_flags = \imap_fetch_overview($this->stream, $id, $uid ? IMAP::FT_UID : IMAP::NIL);
+            $flags = [];
+            if (is_array($raw_flags) && isset($raw_flags[0])) {
+                $raw_flags = (array) $raw_flags[0];
+                foreach($raw_flags as $flag => $value) {
+                    if ($value === 1 && in_array($flag, ["size", "uid", "msgno", "update"]) === false){
+                        $flags[] = "\\".ucfirst($flag);
+                    }
+                }
+            }
+            $result[$uid] = $flags;
+        }
+
+        return $result;
+    }
+
+    /**
+     * Get uid for a given id
+     * @param int|null $id message number
+     *
+     * @return array|string message number for given message or all messages as array
+     */
+    public function getUid($id = null) {
+        if ($id === null) {
+            $overview = $this->overview("1:*");
+            $uids = [];
+            foreach($overview as $set){
+                $uids[$set->msgno] = $set->uid;
+            }
+            return $uids;
+        }
+        return \imap_uid($this->stream, $id);
+    }
+
+    /**
+     * Get a message number for a uid
+     * @param string $id uid
+     *
+     * @return int message number
+     */
+    public function getMessageNumber($id) {
+        return \imap_msgno($this->stream, $id);
+    }
+
+    /**
+     * Get a message overview
+     * @param string $sequence uid sequence
+     * @param bool $uid set to true if passing a unique id
+     *
+     * @return array
+     */
+    public function overview($sequence, $uid = false) {
+        return \imap_fetch_overview($this->stream, $sequence,$uid ? IMAP::FT_UID : IMAP::NIL);
+    }
+
+    /**
+     * Get a list of available folders
+     * @param string $reference mailbox reference for list
+     * @param string $folder mailbox name match with wildcards
+     *
+     * @return array folders that matched $folder as array(name => array('delimiter' => .., 'flags' => ..))
+     * @throws RuntimeException
+     */
+    public function folders($reference = '', $folder = '*') {
+        $result = [];
+
+        $items = \imap_getmailboxes($this->stream, $this->getAddress(), $reference.$folder);
+        if(is_array($items)){
+            foreach ($items as $item) {
+                $name = $this->decodeFolderName($item->name);
+                $result[$name] = ['delimiter' => $item->delimiter, 'flags' => []];
+            }
+        }else{
+            throw new RuntimeException(\imap_last_error());
+        }
+
+        return $result;
+    }
+
+    /**
+     * Manage flags
+     * @param array $flags flags to set, add or remove - see $mode
+     * @param int $from message for items or start message if $to !== null
+     * @param int|null $to if null only one message ($from) is fetched, else it's the
+     *                             last message, INF means last message available
+     * @param string|null $mode '+' to add flags, '-' to remove flags, everything else sets the flags as given
+     * @param bool $silent if false the return values are the new flags for the wanted messages
+     * @param bool $uid set to true if passing a unique id
+     *
+     * @return bool|array new flags if $silent is false, else true or false depending on success
+     */
+    public function store(array $flags, $from, $to = null, $mode = null, $silent = true, $uid = false) {
+        $flag = trim(is_array($flags) ? implode(" ", $flags) : $flags);
+
+        if ($mode == "+"){
+            $status = \imap_setflag_full($this->stream, $from, $flag, $uid ? IMAP::FT_UID : IMAP::NIL);
+        }else{
+            $status = \imap_clearflag_full($this->stream, $from, $flag, $uid ? IMAP::FT_UID : IMAP::NIL);
+        }
+
+        if ($silent === true) {
+            return $status;
+        }
+
+        return $this->flags($from);
+    }
+
+    /**
+     * Append a new message to given folder
+     * @param string $folder name of target folder
+     * @param string $message full message content
+     * @param array $flags flags for new message
+     * @param string $date date for new message
+     *
+     * @return bool success
+     */
+    public function appendMessage($folder, $message, $flags = null, $date = null) {
+        if ($date != null) {
+            if ($date instanceof \Carbon\Carbon){
+                $date = $date->format('d-M-Y H:i:s O');
+            }
+            return \imap_append($this->stream, $folder, $message, $flags, $date);
+        }
+
+        return \imap_append($this->stream, $folder, $message, $flags);
+    }
+
+    /**
+     * Copy message set from current folder to other folder
+     * @param string $folder destination folder
+     * @param $from
+     * @param int|null $to if null only one message ($from) is fetched, else it's the
+     *                         last message, INF means last message available
+     * @param bool $uid set to true if passing a unique id
+     *
+     * @return bool success
+     */
+    public function copyMessage($folder, $from, $to = null, $uid = false) {
+        return \imap_mail_copy($this->stream, $from, $folder, $uid ? IMAP::FT_UID : IMAP::NIL);
+    }
+
+    /**
+     * Copy multiple messages to the target folder
+     *
+     * @param array<string> $messages List of message identifiers
+     * @param string $folder Destination folder
+     * @param bool $uid Set to true if you pass message unique identifiers instead of numbers
+     * @return array|bool Tokens if operation successful, false if an error occurred
+     */
+    public function copyManyMessages($messages, $folder, $uid = false) {
+        foreach($messages as $msg) {
+            if ($this->copyMessage($folder, $msg, null, $uid) == false) {
+                return false;
+            }
+        }
+
+        return $messages;
+    }
+
+    /**
+     * Move a message set from current folder to an other folder
+     * @param string $folder destination folder
+     * @param $from
+     * @param int|null $to if null only one message ($from) is fetched, else it's the
+     *                         last message, INF means last message available
+     * @param bool $uid set to true if passing a unique id
+     *
+     * @return bool success
+     */
+    public function moveMessage($folder, $from, $to = null, $uid = false) {
+        return \imap_mail_move($this->stream, $from, $folder, $uid ? IMAP::FT_UID : IMAP::NIL);
+    }
+
+    /**
+     * Move multiple messages to the target folder
+     *
+     * @param array<string> $messages List of message identifiers
+     * @param string $folder Destination folder
+     * @param bool $uid Set to true if you pass message unique identifiers instead of numbers
+     * @return array|bool Tokens if operation successful, false if an error occurred
+     */
+    public function moveManyMessages($messages, $folder, $uid = false) {
+        foreach($messages as $msg) {
+            if ($this->moveMessage($folder, $msg, null, $uid) == false) {
+                return false;
+            }
+        }
+
+        return $messages;
+    }
+
+    /**
+     * Create a new folder (and parent folders if needed)
+     * @param string $folder folder name
+     *
+     * @return bool success
+     */
+    public function createFolder($folder) {
+        return \imap_createmailbox($this->stream, $folder);
+    }
+
+    /**
+     * Rename an existing folder
+     * @param string $old old name
+     * @param string $new new name
+     *
+     * @return bool success
+     */
+    public function renameFolder($old, $new) {
+        return \imap_renamemailbox($this->stream, $old, $new);
+    }
+
+    /**
+     * Delete a folder
+     * @param string $folder folder name
+     *
+     * @return bool success
+     */
+    public function deleteFolder($folder) {
+        return \imap_deletemailbox($this->stream, $folder);
+    }
+
+    /**
+     * Subscribe to a folder
+     * @param string $folder folder name
+     *
+     * @throws MethodNotSupportedException
+     */
+    public function subscribeFolder($folder) {
+        throw new MethodNotSupportedException();
+    }
+
+    /**
+     * Unsubscribe from a folder
+     * @param string $folder folder name
+     *
+     * @throws MethodNotSupportedException
+     */
+    public function unsubscribeFolder($folder) {
+        throw new MethodNotSupportedException();
+    }
+
+    /**
+     * Apply session saved changes to the server
+     *
+     * @return bool success
+     */
+    public function expunge() {
+        return \imap_expunge($this->stream);
+    }
+
+    /**
+     * Send noop command
+     *
+     * @throws MethodNotSupportedException
+     */
+    public function noop() {
+        throw new MethodNotSupportedException();
+    }
+
+    /**
+     * Send idle command
+     *
+     * @throws MethodNotSupportedException
+     */
+    public function idle() {
+        throw new MethodNotSupportedException();
+    }
+
+    /**
+     * Send done command
+     *
+     * @throws MethodNotSupportedException
+     */
+    public function done() {
+        throw new MethodNotSupportedException();
+    }
+
+    /**
+     * Search for matching messages
+     *
+     * @param array $params
+     * @return array message ids
+     */
+    public function search(array $params, $uid = false) {
+        return \imap_search($this->stream, $params[0], $uid ? IMAP::FT_UID : IMAP::NIL);
+    }
+
+    /**
+     * Enable the debug mode
+     */
+    public function enableDebug(){
+        $this->debug = true;
+    }
+
+    /**
+     * Disable the debug mode
+     */
+    public function disableDebug(){
+        $this->debug = false;
+    }
+
+    /**
+     * Decode name.
+     * It converts UTF7-IMAP encoding to UTF-8.
+     *
+     * @param $name
+     *
+     * @return mixed|string
+     */
+    protected function decodeFolderName($name) {
+        preg_match('#\{(.*)\}(.*)#', $name, $preg);
+        return mb_convert_encoding($preg[2], "UTF-8", "UTF7-IMAP");
+    }
+
+    /**
+     * @return string
+     */
+    public function getProtocol() {
+        return $this->protocol;
+    }
+
+    /**
+     * Retrieve the quota level settings, and usage statics per mailbox
+     * @param $username
+     *
+     * @return array
+     */
+    public function getQuota($username) {
+        return \imap_get_quota($this->stream, 'user.'.$username);
+    }
+
+    /**
+     * Retrieve the quota settings per user
+     * @param string $quota_root
+     *
+     * @return array
+     */
+    public function getQuotaRoot($quota_root = 'INBOX') {
+        return \imap_get_quotaroot($this->stream, $quota_root);
+    }
+
+    /**
+     * @param string $protocol
+     * @return LegacyProtocol
+     */
+    public function setProtocol($protocol) {
+        if (($pos = strpos($protocol, "legacy")) > 0) {
+            $protocol = substr($protocol, 0, ($pos + 2) * -1);
+        }
+        $this->protocol = $protocol;
+        return $this;
+    }
+}

+ 224 - 0
htdocs/includes/webklex/php-imap/src/Connection/Protocols/Protocol.php

@@ -0,0 +1,224 @@
+<?php
+/*
+* File: ImapProtocol.php
+* Category: Protocol
+* Author: M.Goldenbaum
+* Created: 16.09.20 18:27
+* Updated: -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Connection\Protocols;
+
+use Webklex\PHPIMAP\Exceptions\ConnectionFailedException;
+
+/**
+ * Class Protocol
+ *
+ * @package Webklex\PHPIMAP\Connection\Protocols
+ */
+abstract class Protocol implements ProtocolInterface {
+
+    /**
+     * Default connection timeout in seconds
+     */
+    protected $connection_timeout = 30;
+
+    /**
+     * @var boolean
+     */
+    protected $debug = false;
+
+    /**
+     * @var false|resource
+     */
+    public $stream = false;
+
+    /**
+     * Connection encryption method
+     * @var mixed $encryption
+     */
+    protected $encryption = false;
+
+    /**
+     * Set to false to ignore SSL certificate validation
+     * @var bool
+     */
+    protected $cert_validation = true;
+
+    /**
+     * Proxy settings
+     * @var array
+     */
+    protected $proxy = [
+        'socket' => null,
+        'request_fulluri' => false,
+        'username' => null,
+        'password' => null,
+    ];
+
+    /**
+     * Get an available cryptographic method
+     *
+     * @return int
+     */
+    public function getCryptoMethod() {
+        // Allow the best TLS version(s) we can
+        $cryptoMethod = STREAM_CRYPTO_METHOD_TLS_CLIENT;
+
+        // PHP 5.6.7 dropped inclusion of TLS 1.1 and 1.2 in STREAM_CRYPTO_METHOD_TLS_CLIENT
+        // so add them back in manually if we can
+        if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) {
+            $cryptoMethod = STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
+        }elseif (defined('STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT')) {
+            $cryptoMethod = STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
+        }
+
+        return $cryptoMethod;
+    }
+
+    /**
+     * Enable SSL certificate validation
+     *
+     * @return $this
+     */
+    public function enableCertValidation() {
+        $this->cert_validation = true;
+        return $this;
+    }
+
+    /**
+     * Disable SSL certificate validation
+     * @return $this
+     */
+    public function disableCertValidation() {
+        $this->cert_validation = false;
+        return $this;
+    }
+
+    /**
+     * Set SSL certificate validation
+     * @var int $cert_validation
+     *
+     * @return $this
+     */
+    public function setCertValidation($cert_validation) {
+        $this->cert_validation = $cert_validation;
+        return $this;
+    }
+
+    /**
+     * Should we validate SSL certificate?
+     *
+     * @return bool
+     */
+    public function getCertValidation() {
+        return $this->cert_validation;
+    }
+
+    /**
+     * Set connection proxy settings
+     * @var array $options
+     *
+     * @return $this
+     */
+    public function setProxy($options) {
+        foreach ($this->proxy as $key => $val) {
+            if (isset($options[$key])) {
+                $this->proxy[$key] = $options[$key];
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get the current proxy settings
+     *
+     * @return array
+     */
+    public function getProxy() {
+        return $this->proxy;
+    }
+
+    /**
+     * Prepare socket options
+     * @var string $transport
+     *
+     * @return array
+     */
+    private function defaultSocketOptions($transport) {
+        $options = [];
+        if ($this->encryption != false) {
+            $options["ssl"] = [
+                'verify_peer_name' => $this->getCertValidation(),
+                'verify_peer'      => $this->getCertValidation(),
+            ];
+        }
+
+        if ($this->proxy["socket"] != null) {
+            $options[$transport]["proxy"] = $this->proxy["socket"];
+            $options[$transport]["request_fulluri"] = $this->proxy["request_fulluri"];
+
+            if ($this->proxy["username"] != null) {
+                $auth = base64_encode($this->proxy["username"].':'.$this->proxy["password"]);
+
+                $options[$transport]["header"] = [
+                    "Proxy-Authorization: Basic $auth"
+                ];
+            }
+        }
+
+        return $options;
+    }
+
+    /**
+     * Create a new resource stream
+     * @param $transport
+     * @param string $host hostname or IP address of IMAP server
+     * @param int $port of IMAP server, default is 143 (993 for ssl)
+     * @param int $timeout timeout in seconds for initiating session
+     *
+     * @return resource|boolean The socket created.
+     * @throws ConnectionFailedException
+     */
+    protected function createStream($transport, $host, $port, $timeout) {
+        $socket = "$transport://$host:$port";
+        $stream = stream_socket_client($socket, $errno, $errstr, $timeout,
+            STREAM_CLIENT_CONNECT,
+            stream_context_create($this->defaultSocketOptions($transport))
+        );
+        stream_set_timeout($stream, $timeout);
+
+        if (!$stream) {
+            throw new ConnectionFailedException($errstr, $errno);
+        }
+
+        if (false === stream_set_timeout($stream, $timeout)) {
+            throw new ConnectionFailedException('Failed to set stream timeout');
+        }
+
+        return $stream;
+    }
+
+    /**
+     * @return int
+     */
+    public function getConnectionTimeout() {
+        return $this->connection_timeout;
+    }
+
+    /**
+     * @param int $connection_timeout
+     * @return Protocol
+     */
+    public function setConnectionTimeout($connection_timeout) {
+        if ($connection_timeout !== null) {
+            $this->connection_timeout = $connection_timeout;
+        }
+        return $this;
+    }
+
+}

+ 375 - 0
htdocs/includes/webklex/php-imap/src/Connection/Protocols/ProtocolInterface.php

@@ -0,0 +1,375 @@
+<?php
+/*
+* File: ImapProtocol.php
+* Category: Protocol
+* Author: M.Goldenbaum
+* Created: 16.09.20 18:27
+* Updated: -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Connection\Protocols;
+
+use ErrorException;
+use Webklex\PHPIMAP\Exceptions\AuthFailedException;
+use Webklex\PHPIMAP\Exceptions\ConnectionFailedException;
+use Webklex\PHPIMAP\Exceptions\InvalidMessageDateException;
+use Webklex\PHPIMAP\Exceptions\MessageNotFoundException;
+use Webklex\PHPIMAP\Exceptions\RuntimeException;
+
+/**
+ * Interface ProtocolInterface
+ *
+ * @package Webklex\PHPIMAP\Connection\Protocols
+ */
+interface ProtocolInterface {
+
+    /**
+     * Protocol constructor.
+     * @param bool $cert_validation set to false to skip SSL certificate validation
+     */
+    public function __construct($cert_validation = true);
+
+    /**
+     * Public destructor
+     */
+    public function __destruct();
+
+    /**
+     * Open a new connection / session
+     * @param string $host hostname or IP address of IMAP server
+     * @param int|null $port of service server
+     *
+     * @throws ErrorException
+     * @throws ConnectionFailedException
+     * @throws RuntimeException
+     */
+    public function connect($host, $port = null);
+
+    /**
+     * Login to a new session.
+     *
+     * @param string $user username
+     * @param string $password password
+     * @return bool success
+     * @throws AuthFailedException
+     */
+    public function login($user, $password);
+
+    /**
+     * Authenticate your current session.
+     * @param string $user username
+     * @param string $token access token
+     *
+     * @return bool|mixed
+     * @throws AuthFailedException
+     */
+    public function authenticate($user, $token);
+
+    /**
+     * Logout of the current server session
+     *
+     * @return bool success
+     */
+    public function logout();
+
+    /**
+     * Check if the current session is connected
+     *
+     * @return bool
+     */
+    public function connected();
+
+    /**
+     * Get an array of available capabilities
+     *
+     * @return array list of capabilities
+     * @throws RuntimeException
+     */
+    public function getCapabilities();
+
+    /**
+     * Change the current folder
+     *
+     * @param string $folder change to this folder
+     * @return bool|array see examineOrselect()
+     * @throws RuntimeException
+     */
+    public function selectFolder($folder = 'INBOX');
+
+    /**
+     * Examine a given folder
+     *
+     * @param string $folder
+     * @return bool|array
+     * @throws RuntimeException
+     */
+    public function examineFolder($folder = 'INBOX');
+
+    /**
+     * Fetch message headers
+     * @param array|int $uids
+     * @param string $rfc
+     * @param bool $uid set to true if passing a unique id
+     *
+     * @return array
+     * @throws RuntimeException
+     */
+    public function content($uids, $rfc = "RFC822", $uid = false);
+
+    /**
+     * Fetch message headers
+     * @param array|int $uids
+     * @param string $rfc
+     * @param bool $uid set to true if passing a unique id
+     *
+     * @return array
+     * @throws RuntimeException
+     */
+    public function headers($uids, $rfc = "RFC822", $uid = false);
+
+    /**
+     * Fetch message flags
+     * @param array|int $uids
+     * @param bool $uid set to true if passing a unique id
+     *
+     * @return array
+     * @throws RuntimeException
+     */
+    public function flags($uids, $uid = false);
+
+    /**
+     * Get uid for a given id
+     * @param int|null $id message number
+     *
+     * @return array|string message number for given message or all messages as array
+     * @throws MessageNotFoundException
+     */
+    public function getUid($id = null);
+
+    /**
+     * Get a message number for a uid
+     * @param string $id uid
+     *
+     * @return int message number
+     * @throws MessageNotFoundException
+     */
+    public function getMessageNumber($id);
+
+    /**
+     * Get a list of available folders
+     *
+     * @param string $reference mailbox reference for list
+     * @param string $folder mailbox / folder name match with wildcards
+     * @return array mailboxes that matched $folder as array(globalName => array('delim' => .., 'flags' => ..))
+     * @throws RuntimeException
+     */
+    public function folders($reference = '', $folder = '*');
+
+    /**
+     * Set message flags
+     *
+     * @param array $flags flags to set, add or remove
+     * @param int $from message for items or start message if $to !== null
+     * @param int|null $to if null only one message ($from) is fetched, else it's the
+     *                             last message, INF means last message available
+     * @param string|null $mode '+' to add flags, '-' to remove flags, everything else sets the flags as given
+     * @param bool $silent if false the return values are the new flags for the wanted messages
+     * @param bool $uid set to true if passing a unique id
+     *
+     * @return bool|array new flags if $silent is false, else true or false depending on success
+     * @throws RuntimeException
+     */
+    public function store(array $flags, $from, $to = null, $mode = null, $silent = true, $uid = false);
+
+    /**
+     * Append a new message to given folder
+     *
+     * @param string $folder name of target folder
+     * @param string $message full message content
+     * @param array $flags flags for new message
+     * @param string $date date for new message
+     * @return bool success
+     * @throws RuntimeException
+     */
+    public function appendMessage($folder, $message, $flags = null, $date = null);
+
+    /**
+     * Copy message set from current folder to other folder
+     *
+     * @param string $folder destination folder
+     * @param $from
+     * @param int|null $to if null only one message ($from) is fetched, else it's the
+     *                         last message, INF means last message available
+     * @param bool $uid set to true if passing a unique id
+     *
+     * @return bool success
+     * @throws RuntimeException
+     */
+    public function copyMessage($folder, $from, $to = null, $uid = false);
+
+    /**
+     * Copy multiple messages to the target folder
+     *
+     * @param array<string> $messages List of message identifiers
+     * @param string $folder Destination folder
+     * @param bool $uid Set to true if you pass message unique identifiers instead of numbers
+     * @return array|bool Tokens if operation successful, false if an error occurred
+     *
+     * @throws RuntimeException
+     */
+    public function copyManyMessages($messages, $folder, $uid = false);
+
+    /**
+     * Move a message set from current folder to an other folder
+     * @param string $folder destination folder
+     * @param $from
+     * @param int|null $to if null only one message ($from) is fetched, else it's the
+     *                         last message, INF means last message available
+     * @param bool $uid set to true if passing a unique id
+     *
+     * @return bool success
+     */
+    public function moveMessage($folder, $from, $to = null, $uid = false);
+
+    /**
+     * Move multiple messages to the target folder
+     *
+     * @param array<string> $messages List of message identifiers
+     * @param string $folder Destination folder
+     * @param bool $uid Set to true if you pass message unique identifiers instead of numbers
+     * @return array|bool Tokens if operation successful, false if an error occurred
+     *
+     * @throws RuntimeException
+     */
+    public function moveManyMessages($messages, $folder, $uid = false);
+
+    /**
+     * Create a new folder
+     *
+     * @param string $folder folder name
+     * @return bool success
+     * @throws RuntimeException
+     */
+    public function createFolder($folder);
+
+    /**
+     * Rename an existing folder
+     *
+     * @param string $old old name
+     * @param string $new new name
+     * @return bool success
+     * @throws RuntimeException
+     */
+    public function renameFolder($old, $new);
+
+    /**
+     * Delete a folder
+     *
+     * @param string $folder folder name
+     * @return bool success
+     * @throws RuntimeException
+     */
+    public function deleteFolder($folder);
+
+    /**
+     * Subscribe to a folder
+     *
+     * @param string $folder folder name
+     * @return bool success
+     * @throws RuntimeException
+     */
+    public function subscribeFolder($folder);
+
+    /**
+     * Unsubscribe from a folder
+     * @param string $folder folder name
+     *
+     * @return bool success
+     * @throws RuntimeException
+     */
+    public function unsubscribeFolder($folder);
+
+    /**
+     * Send idle command
+     *
+     * @throws RuntimeException
+     */
+    public function idle();
+
+    /**
+     * Send done command
+     * @throws RuntimeException
+     */
+    public function done();
+
+    /**
+     * Apply session saved changes to the server
+     *
+     * @return bool success
+     * @throws RuntimeException
+     */
+    public function expunge();
+
+    /**
+     * Retrieve the quota level settings, and usage statics per mailbox
+     * @param $username
+     *
+     * @return array
+     * @throws RuntimeException
+     */
+    public function getQuota($username);
+
+    /**
+     * Retrieve the quota settings per user
+     *
+     * @param string $quota_root
+     *
+     * @return array
+     * @throws ConnectionFailedException
+     */
+    public function getQuotaRoot($quota_root = 'INBOX');
+
+    /**
+     * Send noop command
+     *
+     * @return bool success
+     * @throws RuntimeException
+     */
+    public function noop();
+
+    /**
+     * Do a search request
+     *
+     * @param array $params
+     * @param bool $uid set to true if passing a unique id
+     *
+     * @return array message ids
+     * @throws RuntimeException
+     */
+    public function search(array $params, $uid = false);
+
+    /**
+     * Get a message overview
+     * @param string $sequence uid sequence
+     * @param bool $uid set to true if passing a unique id
+     *
+     * @return array
+     * @throws RuntimeException
+     * @throws MessageNotFoundException
+     * @throws InvalidMessageDateException
+     */
+    public function overview($sequence, $uid = false);
+
+    /**
+     * Enable the debug mode
+     */
+    public function enableDebug();
+
+    /**
+     * Disable the debug mode
+     */
+    public function disableDebug();
+}

+ 482 - 0
htdocs/includes/webklex/php-imap/src/EncodingAliases.php

@@ -0,0 +1,482 @@
+<?php
+/*
+* File:     EncodingAliases.php
+* Category: -
+* Author:   S. Todorov (https://github.com/todorowww)
+* Created:  23.04.18 14:16
+* Updated:  -
+*
+* Description:
+*  Contains email encoding aliases, thta can occur when fetching emails. These sometimes can break icvon()
+*  This file attempts to correct this by using a list of aliases and their mappings to supported iconv() encodings
+*/
+
+namespace Webklex\PHPIMAP;
+
+/**
+ * Class EncodingAliases
+ *
+ * @package Webklex\PHPIMAP
+ */
+class EncodingAliases {
+   
+    /**
+     * Contains email encoding mappings
+     *
+     * @var array
+     */
+    private static $aliases = [
+        /*
+        |--------------------------------------------------------------------------
+        | Email encoding aliases
+        |--------------------------------------------------------------------------
+        |
+        | Email encoding aliases used to convert to iconv supported charsets
+        |
+        |
+        | This Source Code Form is subject to the terms of the Mozilla Public
+        | License, v. 2.0. If a copy of the MPL was not distributed with this
+        | file, You can obtain one at http://mozilla.org/MPL/2.0/.
+        |
+        | This Original Code has been modified by IBM Corporation.
+        | Modifications made by IBM described herein are
+        | Copyright (c) International Business Machines
+        | Corporation, 1999
+        |
+        | Modifications to Mozilla code or documentation
+        | identified per MPL Section 3.3
+        |
+        | Date         Modified by     Description of modification
+        | 12/09/1999   IBM Corp.       Support for IBM codepages - 850,852,855,857,862,864
+        |
+        | Rule of this file:
+        | 1. key should always be in lower case ascii so we can do case insensitive
+        |    comparison in the code faster.
+        | 2. value should be the one used in unicode converter
+        |
+        | 3. If the charset is not used for document charset, but font charset
+        |    (e.g. XLFD charset- such as JIS x0201, JIS x0208), don't put here
+        |
+        */
+        "ascii"                    => "us-ascii",
+        "us-ascii"                 => "us-ascii",
+        "ansi_x3.4-1968"           => "us-ascii",
+        "646"                      => "us-ascii",
+        "iso-8859-1"               => "ISO-8859-1",
+        "iso-8859-2"               => "ISO-8859-2",
+        "iso-8859-3"               => "ISO-8859-3",
+        "iso-8859-4"               => "ISO-8859-4",
+        "iso-8859-5"               => "ISO-8859-5",
+        "iso-8859-6"               => "ISO-8859-6",
+        "iso-8859-6-i"             => "ISO-8859-6-I",
+        "iso-8859-6-e"             => "ISO-8859-6-E",
+        "iso-8859-7"               => "ISO-8859-7",
+        "iso-8859-8"               => "ISO-8859-8",
+        "iso-8859-8-i"             => "ISO-8859-8-I",
+        "iso-8859-8-e"             => "ISO-8859-8-E",
+        "iso-8859-9"               => "ISO-8859-9",
+        "iso-8859-10"              => "ISO-8859-10",
+        "iso-8859-11"              => "ISO-8859-11",
+        "iso-8859-13"              => "ISO-8859-13",
+        "iso-8859-14"              => "ISO-8859-14",
+        "iso-8859-15"              => "ISO-8859-15",
+        "iso-8859-16"              => "ISO-8859-16",
+        "iso-ir-111"               => "ISO-IR-111",
+        "iso-2022-cn"              => "ISO-2022-CN",
+        "iso-2022-cn-ext"          => "ISO-2022-CN",
+        "iso-2022-kr"              => "ISO-2022-KR",
+        "iso-2022-jp"              => "ISO-2022-JP",
+        "utf-16be"                 => "UTF-16BE",
+        "utf-16le"                 => "UTF-16LE",
+        "utf-16"                   => "UTF-16",
+        "windows-1250"             => "windows-1250",
+        "windows-1251"             => "windows-1251",
+        "windows-1252"             => "windows-1252",
+        "windows-1253"             => "windows-1253",
+        "windows-1254"             => "windows-1254",
+        "windows-1255"             => "windows-1255",
+        "windows-1256"             => "windows-1256",
+        "windows-1257"             => "windows-1257",
+        "windows-1258"             => "windows-1258",
+        "ibm866"                   => "IBM866",
+        "ibm850"                   => "IBM850",
+        "ibm852"                   => "IBM852",
+        "ibm855"                   => "IBM855",
+        "ibm857"                   => "IBM857",
+        "ibm862"                   => "IBM862",
+        "ibm864"                   => "IBM864",
+        "utf-8"                    => "UTF-8",
+        "utf-7"                    => "UTF-7",
+        "shift_jis"                => "Shift_JIS",
+        "big5"                     => "Big5",
+        "euc-jp"                   => "EUC-JP",
+        "euc-kr"                   => "EUC-KR",
+        "gb2312"                   => "GB2312",
+        "gb18030"                  => "gb18030",
+        "viscii"                   => "VISCII",
+        "koi8-r"                   => "KOI8-R",
+        "koi8_r"                   => "KOI8-R",
+        "cskoi8r"                  => "KOI8-R",
+        "koi"                      => "KOI8-R",
+        "koi8"                     => "KOI8-R",
+        "koi8-u"                   => "KOI8-U",
+        "tis-620"                  => "TIS-620",
+        "t.61-8bit"                => "T.61-8bit",
+        "hz-gb-2312"               => "HZ-GB-2312",
+        "big5-hkscs"               => "Big5-HKSCS",
+        "gbk"                      => "gbk",
+        "cns11643"                 => "x-euc-tw",
+        //
+        // Aliases for ISO-8859-1
+        //
+        "latin1"                   => "ISO-8859-1",
+        "iso_8859-1"               => "ISO-8859-1",
+        "iso8859-1"                => "ISO-8859-1",
+        "iso8859-2"                => "ISO-8859-2",
+        "iso8859-3"                => "ISO-8859-3",
+        "iso8859-4"                => "ISO-8859-4",
+        "iso8859-5"                => "ISO-8859-5",
+        "iso8859-6"                => "ISO-8859-6",
+        "iso8859-7"                => "ISO-8859-7",
+        "iso8859-8"                => "ISO-8859-8",
+        "iso8859-9"                => "ISO-8859-9",
+        "iso8859-10"               => "ISO-8859-10",
+        "iso8859-11"               => "ISO-8859-11",
+        "iso8859-13"               => "ISO-8859-13",
+        "iso8859-14"               => "ISO-8859-14",
+        "iso8859-15"               => "ISO-8859-15",
+        "iso_8859-1:1987"          => "ISO-8859-1",
+        "iso-ir-100"               => "ISO-8859-1",
+        "l1"                       => "ISO-8859-1",
+        "ibm819"                   => "ISO-8859-1",
+        "cp819"                    => "ISO-8859-1",
+        "csisolatin1"              => "ISO-8859-1",
+        //
+        // Aliases for ISO-8859-2
+        //
+        "latin2"                   => "ISO-8859-2",
+        "iso_8859-2"               => "ISO-8859-2",
+        "iso_8859-2:1987"          => "ISO-8859-2",
+        "iso-ir-101"               => "ISO-8859-2",
+        "l2"                       => "ISO-8859-2",
+        "csisolatin2"              => "ISO-8859-2",
+        //
+        // Aliases for ISO-8859-3
+        //
+        "latin3"                   => "ISO-8859-3",
+        "iso_8859-3"               => "ISO-8859-3",
+        "iso_8859-3:1988"          => "ISO-8859-3",
+        "iso-ir-109"               => "ISO-8859-3",
+        "l3"                       => "ISO-8859-3",
+        "csisolatin3"              => "ISO-8859-3",
+        //
+        // Aliases for ISO-8859-4
+        //
+        "latin4"                   => "ISO-8859-4",
+        "iso_8859-4"               => "ISO-8859-4",
+        "iso_8859-4:1988"          => "ISO-8859-4",
+        "iso-ir-110"               => "ISO-8859-4",
+        "l4"                       => "ISO-8859-4",
+        "csisolatin4"              => "ISO-8859-4",
+        //
+        // Aliases for ISO-8859-5
+        //
+        "cyrillic"                 => "ISO-8859-5",
+        "iso_8859-5"               => "ISO-8859-5",
+        "iso_8859-5:1988"          => "ISO-8859-5",
+        "iso-ir-144"               => "ISO-8859-5",
+        "csisolatincyrillic"       => "ISO-8859-5",
+        //
+        // Aliases for ISO-8859-6
+        //
+        "arabic"                   => "ISO-8859-6",
+        "iso_8859-6"               => "ISO-8859-6",
+        "iso_8859-6:1987"          => "ISO-8859-6",
+        "iso-ir-127"               => "ISO-8859-6",
+        "ecma-114"                 => "ISO-8859-6",
+        "asmo-708"                 => "ISO-8859-6",
+        "csisolatinarabic"         => "ISO-8859-6",
+        //
+        // Aliases for ISO-8859-6-I
+        //
+        "csiso88596i"              => "ISO-8859-6-I",
+        //
+        // Aliases for ISO-8859-6-E",
+        //
+        "csiso88596e"              => "ISO-8859-6-E",
+        //
+        // Aliases for ISO-8859-7",
+        //
+        "greek"                    => "ISO-8859-7",
+        "greek8"                   => "ISO-8859-7",
+        "sun_eu_greek"             => "ISO-8859-7",
+        "iso_8859-7"               => "ISO-8859-7",
+        "iso_8859-7:1987"          => "ISO-8859-7",
+        "iso-ir-126"               => "ISO-8859-7",
+        "elot_928"                 => "ISO-8859-7",
+        "ecma-118"                 => "ISO-8859-7",
+        "csisolatingreek"          => "ISO-8859-7",
+        //
+        // Aliases for ISO-8859-8",
+        //
+        "hebrew"                   => "ISO-8859-8",
+        "iso_8859-8"               => "ISO-8859-8",
+        "visual"                   => "ISO-8859-8",
+        "iso_8859-8:1988"          => "ISO-8859-8",
+        "iso-ir-138"               => "ISO-8859-8",
+        "csisolatinhebrew"         => "ISO-8859-8",
+        //
+        // Aliases for ISO-8859-8-I",
+        //
+        "csiso88598i"              => "ISO-8859-8-I",
+        "iso-8859-8i"              => "ISO-8859-8-I",
+        "logical"                  => "ISO-8859-8-I",
+        //
+        // Aliases for ISO-8859-8-E",
+        //
+        "csiso88598e"              => "ISO-8859-8-E",
+        //
+        // Aliases for ISO-8859-9",
+        //
+        "latin5"                   => "ISO-8859-9",
+        "iso_8859-9"               => "ISO-8859-9",
+        "iso_8859-9:1989"          => "ISO-8859-9",
+        "iso-ir-148"               => "ISO-8859-9",
+        "l5"                       => "ISO-8859-9",
+        "csisolatin5"              => "ISO-8859-9",
+        //
+        // Aliases for UTF-8",
+        //
+        "unicode-1-1-utf-8"        => "UTF-8",
+        // nl_langinfo(CODESET) in HP/UX returns 'utf8' under UTF-8 locales",
+        "utf8"                     => "UTF-8",
+        //
+        // Aliases for Shift_JIS",
+        //
+        "x-sjis"                   => "Shift_JIS",
+        "shift-jis"                => "Shift_JIS",
+        "ms_kanji"                 => "Shift_JIS",
+        "csshiftjis"               => "Shift_JIS",
+        "windows-31j"              => "Shift_JIS",
+        "cp932"                    => "Shift_JIS",
+        "sjis"                     => "Shift_JIS",
+        //
+        // Aliases for EUC_JP",
+        //
+        "cseucpkdfmtjapanese"      => "EUC-JP",
+        "x-euc-jp"                 => "EUC-JP",
+        //
+        // Aliases for ISO-2022-JP",
+        //
+        "csiso2022jp"              => "ISO-2022-JP",
+        // The following are really not aliases ISO-2022-JP, but sharing the same decoder",
+        "iso-2022-jp-2"            => "ISO-2022-JP",
+        "csiso2022jp2"             => "ISO-2022-JP",
+        //
+        // Aliases for Big5",
+        //
+        "csbig5"                   => "Big5",
+        "cn-big5"                  => "Big5",
+        // x-x-big5 is not really a alias for Big5, add it only for MS FrontPage",
+        "x-x-big5"                 => "Big5",
+        // Sun Solaris",
+        "zh_tw-big5"               => "Big5",
+        //
+        // Aliases for EUC-KR",
+        //
+        "cseuckr"                  => "EUC-KR",
+        "ks_c_5601-1987"           => "EUC-KR",
+        "iso-ir-149"               => "EUC-KR",
+        "ks_c_5601-1989"           => "EUC-KR",
+        "ksc_5601"                 => "EUC-KR",
+        "ksc5601"                  => "EUC-KR",
+        "korean"                   => "EUC-KR",
+        "csksc56011987"            => "EUC-KR",
+        "5601"                     => "EUC-KR",
+        "windows-949"              => "EUC-KR",
+        //
+        // Aliases for GB2312",
+        //
+        // The following are really not aliases GB2312, add them only for MS FrontPage",
+        "gb_2312-80"               => "GB2312",
+        "iso-ir-58"                => "GB2312",
+        "chinese"                  => "GB2312",
+        "csiso58gb231280"          => "GB2312",
+        "csgb2312"                 => "GB2312",
+        "zh_cn.euc"                => "GB2312",
+        // Sun Solaris",
+        "gb_2312"                  => "GB2312",
+        //
+        // Aliases for windows-125x ",
+        //
+        "x-cp1250"                 => "windows-1250",
+        "x-cp1251"                 => "windows-1251",
+        "x-cp1252"                 => "windows-1252",
+        "x-cp1253"                 => "windows-1253",
+        "x-cp1254"                 => "windows-1254",
+        "x-cp1255"                 => "windows-1255",
+        "x-cp1256"                 => "windows-1256",
+        "x-cp1257"                 => "windows-1257",
+        "x-cp1258"                 => "windows-1258",
+        //
+        // Aliases for windows-874 ",
+        //
+        "windows-874"              => "windows-874",
+        "ibm874"                   => "windows-874",
+        "dos-874"                  => "windows-874",
+        //
+        // Aliases for macintosh",
+        //
+        "macintosh"                => "macintosh",
+        "x-mac-roman"              => "macintosh",
+        "mac"                      => "macintosh",
+        "csmacintosh"              => "macintosh",
+        //
+        // Aliases for IBM866",
+        //
+        "cp866"                    => "IBM866",
+        "cp-866"                   => "IBM866",
+        "866"                      => "IBM866",
+        "csibm866"                 => "IBM866",
+        //
+        // Aliases for IBM850",
+        //
+        "cp850"                    => "IBM850",
+        "850"                      => "IBM850",
+        "csibm850"                 => "IBM850",
+        //
+        // Aliases for IBM852",
+        //
+        "cp852"                    => "IBM852",
+        "852"                      => "IBM852",
+        "csibm852"                 => "IBM852",
+        //
+        // Aliases for IBM855",
+        //
+        "cp855"                    => "IBM855",
+        "855"                      => "IBM855",
+        "csibm855"                 => "IBM855",
+        //
+        // Aliases for IBM857",
+        //
+        "cp857"                    => "IBM857",
+        "857"                      => "IBM857",
+        "csibm857"                 => "IBM857",
+        //
+        // Aliases for IBM862",
+        //
+        "cp862"                    => "IBM862",
+        "862"                      => "IBM862",
+        "csibm862"                 => "IBM862",
+        //
+        // Aliases for IBM864",
+        //
+        "cp864"                    => "IBM864",
+        "864"                      => "IBM864",
+        "csibm864"                 => "IBM864",
+        "ibm-864"                  => "IBM864",
+        //
+        // Aliases for T.61-8bit",
+        //
+        "t.61"                     => "T.61-8bit",
+        "iso-ir-103"               => "T.61-8bit",
+        "csiso103t618bit"          => "T.61-8bit",
+        //
+        // Aliases for UTF-7",
+        //
+        "x-unicode-2-0-utf-7"      => "UTF-7",
+        "unicode-2-0-utf-7"        => "UTF-7",
+        "unicode-1-1-utf-7"        => "UTF-7",
+        "csunicode11utf7"          => "UTF-7",
+        //
+        // Aliases for ISO-10646-UCS-2",
+        //
+        "csunicode"                => "UTF-16BE",
+        "csunicode11"              => "UTF-16BE",
+        "iso-10646-ucs-basic"      => "UTF-16BE",
+        "csunicodeascii"           => "UTF-16BE",
+        "iso-10646-unicode-latin1" => "UTF-16BE",
+        "csunicodelatin1"          => "UTF-16BE",
+        "iso-10646"                => "UTF-16BE",
+        "iso-10646-j-1"            => "UTF-16BE",
+        //
+        // Aliases for ISO-8859-10",
+        //
+        "latin6"                   => "ISO-8859-10",
+        "iso-ir-157"               => "ISO-8859-10",
+        "l6"                       => "ISO-8859-10",
+        // Currently .properties cannot handle : in key",
+        //iso_8859-10:1992" => "ISO-8859-10",
+        "csisolatin6"              => "ISO-8859-10",
+        //
+        // Aliases for ISO-8859-15",
+        //
+        "iso_8859-15"              => "ISO-8859-15",
+        "csisolatin9"              => "ISO-8859-15",
+        "l9"                       => "ISO-8859-15",
+        //
+        // Aliases for ISO-IR-111",
+        //
+        "ecma-cyrillic"            => "ISO-IR-111",
+        "csiso111ecmacyrillic"     => "ISO-IR-111",
+        //
+        // Aliases for ISO-2022-KR",
+        //
+        "csiso2022kr"              => "ISO-2022-KR",
+        //
+        // Aliases for VISCII",
+        //
+        "csviscii"                 => "VISCII",
+        //
+        // Aliases for x-euc-tw",
+        //
+        "zh_tw-euc"                => "x-euc-tw",
+        //
+        // Following names appears in unix nl_langinfo(CODESET)",
+        // They can be compiled as platform specific if necessary",
+        // DONT put things here if it does not look generic enough (like hp15CN)",
+        //
+        "iso88591"                 => "ISO-8859-1",
+        "iso88592"                 => "ISO-8859-2",
+        "iso88593"                 => "ISO-8859-3",
+        "iso88594"                 => "ISO-8859-4",
+        "iso88595"                 => "ISO-8859-5",
+        "iso88596"                 => "ISO-8859-6",
+        "iso88597"                 => "ISO-8859-7",
+        "iso88598"                 => "ISO-8859-8",
+        "iso88599"                 => "ISO-8859-9",
+        "iso885910"                => "ISO-8859-10",
+        "iso885911"                => "ISO-8859-11",
+        "iso885912"                => "ISO-8859-12",
+        "iso885913"                => "ISO-8859-13",
+        "iso885914"                => "ISO-8859-14",
+        "iso885915"                => "ISO-8859-15",
+        "cp1250"                   => "windows-1250",
+        "cp1251"                   => "windows-1251",
+        "cp1252"                   => "windows-1252",
+        "cp1253"                   => "windows-1253",
+        "cp1254"                   => "windows-1254",
+        "cp1255"                   => "windows-1255",
+        "cp1256"                   => "windows-1256",
+        "cp1257"                   => "windows-1257",
+        "cp1258"                   => "windows-1258",
+        "x-gbk"                    => "gbk",
+        "windows-936"              => "gbk",
+        "ansi-1251"                => "windows-1251",
+    ];        
+    
+    /**
+     * Returns proper encoding mapping, if exsists. If it doesn't, return unchanged $encoding
+     * @param string $encoding
+     * @param string|null $fallback
+     *
+     * @return string
+     */
+    public static function get($encoding, $fallback = null) {
+        if (isset(self::$aliases[strtolower($encoding)])) {
+            return self::$aliases[strtolower($encoding)];
+        }
+        return $fallback !== null ? $fallback : $encoding;
+    }
+    
+}

+ 28 - 0
htdocs/includes/webklex/php-imap/src/Events/Event.php

@@ -0,0 +1,28 @@
+<?php
+/*
+* File:     Event.php
+* Category: Event
+* Author:   M. Goldenbaum
+* Created:  25.11.20 22:21
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Events;
+
+/**
+ * Class Event
+ *
+ * @package Webklex\PHPIMAP\Events
+ */
+abstract class Event {
+
+    /**
+     * Dispatch the event with the given arguments.
+     */
+    public static function dispatch() {
+        return new static(func_get_args());
+    }
+}

+ 22 - 0
htdocs/includes/webklex/php-imap/src/Events/FlagDeletedEvent.php

@@ -0,0 +1,22 @@
+<?php
+/*
+* File:     FlagDeletedEvent.php
+* Category: Event
+* Author:   M. Goldenbaum
+* Created:  25.11.20 22:21
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Events;
+
+/**
+ * Class FlagDeletedEvent
+ *
+ * @package Webklex\PHPIMAP\Events
+ */
+class FlagDeletedEvent extends FlagNewEvent {
+
+}

+ 39 - 0
htdocs/includes/webklex/php-imap/src/Events/FlagNewEvent.php

@@ -0,0 +1,39 @@
+<?php
+/*
+* File:     FlagNewEvent.php
+* Category: Event
+* Author:   M. Goldenbaum
+* Created:  25.11.20 22:21
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Events;
+
+use Webklex\PHPIMAP\Message;
+
+/**
+ * Class FlagNewEvent
+ *
+ * @package Webklex\PHPIMAP\Events
+ */
+class FlagNewEvent extends Event {
+
+    /** @var Message $message */
+    public $message;
+
+    /** @var string $flag */
+    public $flag;
+
+    /**
+     * Create a new event instance.
+     * @var mixed[] $arguments
+     * @return void
+     */
+    public function __construct($arguments) {
+        $this->message = $arguments[0];
+        $this->flag = $arguments[1];
+    }
+}

+ 22 - 0
htdocs/includes/webklex/php-imap/src/Events/FolderDeletedEvent.php

@@ -0,0 +1,22 @@
+<?php
+/*
+* File:     FolderDeletedEvent.php
+* Category: Event
+* Author:   M. Goldenbaum
+* Created:  25.11.20 22:21
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Events;
+
+/**
+ * Class FolderDeletedEvent
+ *
+ * @package Webklex\PHPIMAP\Events
+ */
+class FolderDeletedEvent extends FolderNewEvent {
+
+}

+ 38 - 0
htdocs/includes/webklex/php-imap/src/Events/FolderMovedEvent.php

@@ -0,0 +1,38 @@
+<?php
+/*
+* File:     FolderMovedEvent.php
+* Category: Event
+* Author:   M. Goldenbaum
+* Created:  25.11.20 22:21
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Events;
+
+use Webklex\PHPIMAP\Folder;
+
+/**
+ * Class FolderMovedEvent
+ *
+ * @package Webklex\PHPIMAP\Events
+ */
+class FolderMovedEvent extends Event {
+
+    /** @var Folder $old_folder */
+    public $old_folder;
+    /** @var Folder $new_folder */
+    public $new_folder;
+
+    /**
+     * Create a new event instance.
+     * @var Folder[] $folders
+     * @return void
+     */
+    public function __construct($folders) {
+        $this->old_folder = $folders[0];
+        $this->new_folder = $folders[1];
+    }
+}

+ 35 - 0
htdocs/includes/webklex/php-imap/src/Events/FolderNewEvent.php

@@ -0,0 +1,35 @@
+<?php
+/*
+* File:     FolderNewEvent.php
+* Category: Event
+* Author:   M. Goldenbaum
+* Created:  25.11.20 22:21
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Events;
+
+use Webklex\PHPIMAP\Folder;
+
+/**
+ * Class FolderNewEvent
+ *
+ * @package Webklex\PHPIMAP\Events
+ */
+class FolderNewEvent extends Event {
+
+    /** @var Folder $folder */
+    public $folder;
+
+    /**
+     * Create a new event instance.
+     * @var Folder[] $folders
+     * @return void
+     */
+    public function __construct($folders) {
+        $this->folder = $folders[0];
+    }
+}

+ 22 - 0
htdocs/includes/webklex/php-imap/src/Events/MessageCopiedEvent.php

@@ -0,0 +1,22 @@
+<?php
+/*
+* File:     MessageCopiedEvent.php
+* Category: Event
+* Author:   M. Goldenbaum
+* Created:  25.11.20 22:21
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Events;
+
+/**
+ * Class MessageCopiedEvent
+ *
+ * @package Webklex\PHPIMAP\Events
+ */
+class MessageCopiedEvent extends MessageMovedEvent {
+
+}

+ 22 - 0
htdocs/includes/webklex/php-imap/src/Events/MessageDeletedEvent.php

@@ -0,0 +1,22 @@
+<?php
+/*
+* File:     MessageDeletedEvent.php
+* Category: Event
+* Author:   M. Goldenbaum
+* Created:  25.11.20 22:21
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Events;
+
+/**
+ * Class MessageDeletedEvent
+ *
+ * @package Webklex\PHPIMAP\Events
+ */
+class MessageDeletedEvent extends MessageNewEvent {
+
+}

+ 38 - 0
htdocs/includes/webklex/php-imap/src/Events/MessageMovedEvent.php

@@ -0,0 +1,38 @@
+<?php
+/*
+* File:     MessageMovedEvent.php
+* Category: Event
+* Author:   M. Goldenbaum
+* Created:  25.11.20 22:21
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Events;
+
+use Webklex\PHPIMAP\Message;
+
+/**
+ * Class MessageMovedEvent
+ *
+ * @package Webklex\PHPIMAP\Events
+ */
+class MessageMovedEvent extends Event {
+
+    /** @var Message $old_message */
+    public $old_message;
+    /** @var Message $new_message */
+    public $new_message;
+
+    /**
+     * Create a new event instance.
+     * @var Message[] $messages
+     * @return void
+     */
+    public function __construct($messages) {
+        $this->old_message = $messages[0];
+        $this->new_message = $messages[1];
+    }
+}

+ 35 - 0
htdocs/includes/webklex/php-imap/src/Events/MessageNewEvent.php

@@ -0,0 +1,35 @@
+<?php
+/*
+* File:     MessageNewEvent.php
+* Category: Event
+* Author:   M. Goldenbaum
+* Created:  25.11.20 22:21
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Events;
+
+use Webklex\PHPIMAP\Message;
+
+/**
+ * Class MessageNewEvent
+ *
+ * @package Webklex\PHPIMAP\Events
+ */
+class MessageNewEvent extends Event {
+
+    /** @var Message $message */
+    public $message;
+
+    /**
+     * Create a new event instance.
+     * @var Message[] $messages
+     * @return void
+     */
+    public function __construct($messages) {
+        $this->message = $messages[0];
+    }
+}

+ 22 - 0
htdocs/includes/webklex/php-imap/src/Events/MessageRestoredEvent.php

@@ -0,0 +1,22 @@
+<?php
+/*
+* File:     MessageRestoredEvent.php
+* Category: Event
+* Author:   M. Goldenbaum
+* Created:  25.11.20 22:21
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Events;
+
+/**
+ * Class MessageRestoredEvent
+ *
+ * @package Webklex\PHPIMAP\Events
+ */
+class MessageRestoredEvent extends MessageNewEvent {
+
+}

+ 24 - 0
htdocs/includes/webklex/php-imap/src/Exceptions/AuthFailedException.php

@@ -0,0 +1,24 @@
+<?php
+/*
+* File:     AuthFailedException.php
+* Category: Exception
+* Author:   M. Goldenbaum
+* Created:  19.01.17 22:21
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Exceptions;
+
+use \Exception;
+
+/**
+ * Class AuthFailedException
+ *
+ * @package Webklex\PHPIMAP\Exceptions
+ */
+class AuthFailedException extends Exception {
+
+}

+ 24 - 0
htdocs/includes/webklex/php-imap/src/Exceptions/ConnectionFailedException.php

@@ -0,0 +1,24 @@
+<?php
+/*
+* File:     ConnectionFailedException.php
+* Category: Exception
+* Author:   M. Goldenbaum
+* Created:  19.01.17 22:21
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Exceptions;
+
+use \Exception;
+
+/**
+ * Class ConnectionFailedException
+ *
+ * @package Webklex\PHPIMAP\Exceptions
+ */
+class ConnectionFailedException extends Exception {
+
+}

+ 24 - 0
htdocs/includes/webklex/php-imap/src/Exceptions/EventNotFoundException.php

@@ -0,0 +1,24 @@
+<?php
+/*
+* File:     EventNotFoundException.php
+* Category: Exception
+* Author:   M. Goldenbaum
+* Created:  05.03.18 23:21
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Exceptions;
+
+use \Exception;
+
+/**
+ * Class EventNotFoundException
+ *
+ * @package Webklex\PHPIMAP\Exceptions
+ */
+class EventNotFoundException extends Exception {
+
+}

+ 24 - 0
htdocs/includes/webklex/php-imap/src/Exceptions/FolderFetchingException.php

@@ -0,0 +1,24 @@
+<?php
+/*
+* File:     FolderFetchingException.php
+* Category: Exception
+* Author:   M. Goldenbaum
+* Created:  05.03.18 23:21
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Exceptions;
+
+use \Exception;
+
+/**
+ * Class FolderFetchingException
+ *
+ * @package Webklex\PHPIMAP\Exceptions
+ */
+class FolderFetchingException extends Exception {
+
+}

+ 24 - 0
htdocs/includes/webklex/php-imap/src/Exceptions/GetMessagesFailedException.php

@@ -0,0 +1,24 @@
+<?php
+/*
+* File:     GetMessagesFailedException.php
+* Category: Exception
+* Author:   M. Goldenbaum
+* Created:  19.01.17 22:21
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Exceptions;
+
+use \Exception;
+
+/**
+ * Class GetMessagesFailedException
+ *
+ * @package Webklex\PHPIMAP\Exceptions
+ */
+class GetMessagesFailedException extends Exception {
+
+}

+ 24 - 0
htdocs/includes/webklex/php-imap/src/Exceptions/InvalidMessageDateException.php

@@ -0,0 +1,24 @@
+<?php
+/*
+* File:     InvalidMessageDateException.php
+* Category: Exception
+* Author:   M. Goldenbaum
+* Created:  10.03.19 04:31
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Exceptions;
+
+use \Exception;
+
+/**
+ * Class InvalidMessageDateException
+ *
+ * @package Webklex\PHPIMAP\Exceptions
+ */
+class InvalidMessageDateException extends Exception {
+
+}

+ 24 - 0
htdocs/includes/webklex/php-imap/src/Exceptions/InvalidWhereQueryCriteriaException.php

@@ -0,0 +1,24 @@
+<?php
+/*
+* File:     InvalidWhereQueryCriteriaException.php
+* Category: Exception
+* Author:   M. Goldenbaum
+* Created:  21.07.18 19:04
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Exceptions;
+
+use \Exception;
+
+/**
+ * Class InvalidWhereQueryCriteriaException
+ *
+ * @package Webklex\PHPIMAP\Exceptions
+ */
+class InvalidWhereQueryCriteriaException extends Exception {
+
+}

+ 24 - 0
htdocs/includes/webklex/php-imap/src/Exceptions/MaskNotFoundException.php

@@ -0,0 +1,24 @@
+<?php
+/*
+* File:     MaskNotFoundException.php
+* Category: Exception
+* Author:   M. Goldenbaum
+* Created:  05.03.18 23:21
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Exceptions;
+
+use \Exception;
+
+/**
+ * Class MaskNotFoundException
+ *
+ * @package Webklex\PHPIMAP\Exceptions
+ */
+class MaskNotFoundException extends Exception {
+
+}

+ 24 - 0
htdocs/includes/webklex/php-imap/src/Exceptions/MessageContentFetchingException.php

@@ -0,0 +1,24 @@
+<?php
+/*
+* File:     MessageContentFetchingException.php
+* Category: Exception
+* Author:   M. Goldenbaum
+* Created:  05.03.18 23:21
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Exceptions;
+
+use \Exception;
+
+/**
+ * Class MessageContentFetchingException
+ *
+ * @package Webklex\PHPIMAP\Exceptions
+ */
+class MessageContentFetchingException extends Exception {
+
+}

+ 24 - 0
htdocs/includes/webklex/php-imap/src/Exceptions/MessageFlagException.php

@@ -0,0 +1,24 @@
+<?php
+/*
+* File:     MessageFlagException.php
+* Category: Exception
+* Author:   M. Goldenbaum
+* Created:  02.01.21 02:47
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Exceptions;
+
+use \Exception;
+
+/**
+ * Class MessageFlagException
+ *
+ * @package Webklex\PHPIMAP\Exceptions
+ */
+class MessageFlagException extends Exception {
+
+}

+ 24 - 0
htdocs/includes/webklex/php-imap/src/Exceptions/MessageHeaderFetchingException.php

@@ -0,0 +1,24 @@
+<?php
+/*
+* File:     MessageHeaderFetchingException.php
+* Category: Exception
+* Author:   M. Goldenbaum
+* Created:  05.03.18 23:21
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Exceptions;
+
+use \Exception;
+
+/**
+ * Class MessageHeaderFetchingException
+ *
+ * @package Webklex\PHPIMAP\Exceptions
+ */
+class MessageHeaderFetchingException extends Exception {
+
+}

+ 24 - 0
htdocs/includes/webklex/php-imap/src/Exceptions/MessageNotFoundException.php

@@ -0,0 +1,24 @@
+<?php
+/*
+* File:     MessageNotFoundException.php
+* Category: Exception
+* Author:   M. Goldenbaum
+* Created:  25.01.21 18:19
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Exceptions;
+
+use \Exception;
+
+/**
+ * Class MessageNotFoundException
+ *
+ * @package Webklex\PHPIMAP\Exceptions
+ */
+class MessageNotFoundException extends Exception {
+
+}

+ 24 - 0
htdocs/includes/webklex/php-imap/src/Exceptions/MessageSearchValidationException.php

@@ -0,0 +1,24 @@
+<?php
+/*
+* File:     MessageSearchValidationException.php
+* Category: Exception
+* Author:   M. Goldenbaum
+* Created:  05.03.18 23:21
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Exceptions;
+
+use \Exception;
+
+/**
+ * Class MessageSearchValidationException
+ *
+ * @package Webklex\PHPIMAP\Exceptions
+ */
+class MessageSearchValidationException extends Exception {
+
+}

+ 24 - 0
htdocs/includes/webklex/php-imap/src/Exceptions/MethodNotFoundException.php

@@ -0,0 +1,24 @@
+<?php
+/*
+* File:     MethodNotFoundException.php
+* Category: Exception
+* Author:   M. Goldenbaum
+* Created:  05.03.18 23:21
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Exceptions;
+
+use \Exception;
+
+/**
+ * Class MethodNotFoundException
+ *
+ * @package Webklex\PHPIMAP\Exceptions
+ */
+class MethodNotFoundException extends Exception {
+
+}

+ 24 - 0
htdocs/includes/webklex/php-imap/src/Exceptions/MethodNotSupportedException.php

@@ -0,0 +1,24 @@
+<?php
+/*
+* File:     MethodNotSupportedException.php
+* Category: Exception
+* Author:   M. Goldenbaum
+* Created:  05.03.18 23:21
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Exceptions;
+
+use \Exception;
+
+/**
+ * Class MethodNotSupportedException
+ *
+ * @package Webklex\PHPIMAP\Exceptions
+ */
+class MethodNotSupportedException extends Exception {
+
+}

+ 24 - 0
htdocs/includes/webklex/php-imap/src/Exceptions/ProtocolNotSupportedException.php

@@ -0,0 +1,24 @@
+<?php
+/*
+* File:     ProtocolNotSupportedException.php
+* Category: Exception
+* Author:   M. Goldenbaum
+* Created:  19.01.17 22:21
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Exceptions;
+
+use \Exception;
+
+/**
+ * Class ProtocolNotSupportedException
+ *
+ * @package Webklex\PHPIMAP\Exceptions
+ */
+class ProtocolNotSupportedException extends Exception {
+
+}

+ 24 - 0
htdocs/includes/webklex/php-imap/src/Exceptions/RuntimeException.php

@@ -0,0 +1,24 @@
+<?php
+/*
+* File:     RuntimeException.php
+* Category: Exception
+* Author:   M. Goldenbaum
+* Created:  19.01.17 22:21
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Exceptions;
+
+use \Exception;
+
+/**
+ * Class RuntimeException
+ *
+ * @package Webklex\PHPIMAP\Exceptions
+ */
+class RuntimeException extends Exception {
+
+}

+ 444 - 0
htdocs/includes/webklex/php-imap/src/Folder.php

@@ -0,0 +1,444 @@
+<?php
+/*
+* File:     Folder.php
+* Category: -
+* Author:   M. Goldenbaum
+* Created:  19.01.17 22:21
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP;
+
+use Carbon\Carbon;
+use Webklex\PHPIMAP\Exceptions\ConnectionFailedException;
+use Webklex\PHPIMAP\Query\WhereQuery;
+use Webklex\PHPIMAP\Support\FolderCollection;
+use Webklex\PHPIMAP\Traits\HasEvents;
+
+/**
+ * Class Folder
+ *
+ * @package Webklex\PHPIMAP
+ */
+class Folder {
+    use HasEvents;
+
+    /**
+     * Client instance
+     *
+     * @var Client
+     */
+    protected $client;
+
+    /**
+     * Folder full path
+     *
+     * @var string
+     */
+    public $path;
+
+    /**
+     * Folder name
+     *
+     * @var string
+     */
+    public $name;
+
+    /**
+     * Folder fullname
+     *
+     * @var string
+     */
+    public $full_name;
+
+    /**
+     * Children folders
+     *
+     * @var FolderCollection|array
+     */
+    public $children = [];
+
+    /**
+     * Delimiter for folder
+     *
+     * @var string
+     */
+    public $delimiter;
+
+    /**
+     * Indicates if folder can't containg any "children".
+     * CreateFolder won't work on this folder.
+     *
+     * @var boolean
+     */
+    public $no_inferiors;
+
+    /**
+     * Indicates if folder is only container, not a mailbox - you can't open it.
+     *
+     * @var boolean
+     */
+    public $no_select;
+
+    /**
+     * Indicates if folder is marked. This means that it may contain new messages since the last time it was checked.
+     * Not provided by all IMAP servers.
+     *
+     * @var boolean
+     */
+    public $marked;
+
+    /**
+     * Indicates if folder containg any "children".
+     * Not provided by all IMAP servers.
+     *
+     * @var boolean
+     */
+    public $has_children;
+
+    /**
+     * Indicates if folder refers to other.
+     * Not provided by all IMAP servers.
+     *
+     * @var boolean
+     */
+    public $referral;
+
+    /**
+     * Folder constructor.
+     * @param Client $client
+     * @param string $folder_name
+     * @param string $delimiter
+     * @param string[] $attributes
+     */
+    public function __construct(Client $client, $folder_name, $delimiter, $attributes) {
+        $this->client = $client;
+
+        $this->events["message"] = $client->getDefaultEvents("message");
+        $this->events["folder"] = $client->getDefaultEvents("folder");
+
+        $this->setDelimiter($delimiter);
+        $this->path      = $folder_name;
+        $this->full_name  = $this->decodeName($folder_name);
+        $this->name      = $this->getSimpleName($this->delimiter, $this->full_name);
+
+        $this->parseAttributes($attributes);
+    }
+
+    /**
+     * Get a new search query instance
+     * @param string $charset
+     *
+     * @return WhereQuery
+     * @throws Exceptions\ConnectionFailedException
+     * @throws Exceptions\RuntimeException
+     */
+    public function query($charset = 'UTF-8'){
+        $this->getClient()->checkConnection();
+        $this->getClient()->openFolder($this->path);
+
+        return new WhereQuery($this->getClient(), $charset);
+    }
+
+    /**
+     * @inheritdoc self::query($charset = 'UTF-8')
+     * @throws Exceptions\ConnectionFailedException
+     * @throws Exceptions\RuntimeException
+     */
+    public function search($charset = 'UTF-8'){
+        return $this->query($charset);
+    }
+
+    /**
+     * @inheritdoc self::query($charset = 'UTF-8')
+     * @throws Exceptions\ConnectionFailedException
+     * @throws Exceptions\RuntimeException
+     */
+    public function messages($charset = 'UTF-8'){
+        return $this->query($charset);
+    }
+
+    /**
+     * Determine if folder has children.
+     *
+     * @return bool
+     */
+    public function hasChildren() {
+        return $this->has_children;
+    }
+
+    /**
+     * Set children.
+     * @param FolderCollection|array $children
+     *
+     * @return self
+     */
+    public function setChildren($children = []) {
+        $this->children = $children;
+
+        return $this;
+    }
+
+    /**
+     * Decode name.
+     * It converts UTF7-IMAP encoding to UTF-8.
+     * @param $name
+     *
+     * @return mixed|string
+     */
+    protected function decodeName($name) {
+        return mb_convert_encoding($name, "UTF-8", "UTF7-IMAP");
+    }
+
+    /**
+     * Get simple name (without parent folders).
+     * @param $delimiter
+     * @param $full_name
+     *
+     * @return mixed
+     */
+    protected function getSimpleName($delimiter, $full_name) {
+        $arr = explode($delimiter, $full_name);
+
+        return end($arr);
+    }
+
+    /**
+     * Parse attributes and set it to object properties.
+     * @param $attributes
+     */
+    protected function parseAttributes($attributes) {
+        $this->no_inferiors = in_array('\NoInferiors', $attributes) ? true : false;
+        $this->no_select    = in_array('\NoSelect', $attributes) ? true : false;
+        $this->marked       = in_array('\Marked', $attributes) ? true : false;
+        $this->referral     = in_array('\Referral', $attributes) ? true : false;
+        $this->has_children = in_array('\HasChildren', $attributes) ? true : false;
+    }
+
+    /**
+     * Move or rename the current folder
+     * @param string $new_name
+     * @param boolean $expunge
+     *
+     * @return bool
+     * @throws ConnectionFailedException
+     * @throws Exceptions\EventNotFoundException
+     * @throws Exceptions\FolderFetchingException
+     * @throws Exceptions\RuntimeException
+     */
+    public function move($new_name, $expunge = true) {
+        $this->client->checkConnection();
+        $status = $this->client->getConnection()->renameFolder($this->full_name, $new_name);
+        if($expunge) $this->client->expunge();
+
+        $folder = $this->client->getFolder($new_name);
+        $event = $this->getEvent("folder", "moved");
+        $event::dispatch($this, $folder);
+
+        return $status;
+    }
+
+    /**
+     * Get a message overview
+     * @param string|null $sequence uid sequence
+     *
+     * @return array
+     * @throws ConnectionFailedException
+     * @throws Exceptions\InvalidMessageDateException
+     * @throws Exceptions\MessageNotFoundException
+     * @throws Exceptions\RuntimeException
+     */
+    public function overview($sequence = null){
+        $this->client->openFolder($this->path);
+        $sequence = $sequence === null ? "1:*" : $sequence;
+        $uid = ClientManager::get('options.sequence', IMAP::ST_MSGN) == IMAP::ST_UID;
+        return $this->client->getConnection()->overview($sequence, $uid);
+    }
+
+    /**
+     * Append a string message to the current mailbox
+     * @param string $message
+     * @param string $options
+     * @param string $internal_date
+     *
+     * @return bool
+     * @throws Exceptions\ConnectionFailedException
+     * @throws Exceptions\RuntimeException
+     */
+    public function appendMessage($message, $options = null, $internal_date = null) {
+        /**
+         * Check if $internal_date is parsed. If it is null it should not be set. Otherwise the message can't be stored.
+         * If this parameter is set, it will set the INTERNALDATE on the appended message. The parameter should be a
+         * date string that conforms to the rfc2060 specifications for a date_time value or be a Carbon object.
+         */
+
+        if ($internal_date != null) {
+            if ($internal_date instanceof Carbon){
+                $internal_date = $internal_date->format('d-M-Y H:i:s O');
+            }
+        }
+
+        return $this->client->getConnection()->appendMessage($this->full_name, $message, $options, $internal_date);
+    }
+
+    /**
+     * Rename the current folder
+     * @param string $new_name
+     * @param boolean $expunge
+     *
+     * @return bool
+     * @throws ConnectionFailedException
+     * @throws Exceptions\EventNotFoundException
+     * @throws Exceptions\FolderFetchingException
+     * @throws Exceptions\RuntimeException
+     */
+    public function rename($new_name, $expunge = true) {
+        return $this->move($new_name, $expunge);
+    }
+
+    /**
+     * Delete the current folder
+     * @param boolean $expunge
+     *
+     * @return bool
+     * @throws Exceptions\ConnectionFailedException
+     * @throws Exceptions\RuntimeException
+     * @throws Exceptions\EventNotFoundException
+     */
+    public function delete($expunge = true) {
+        $status = $this->client->getConnection()->deleteFolder($this->path);
+        if($expunge) $this->client->expunge();
+
+        $event = $this->getEvent("folder", "deleted");
+        $event::dispatch($this);
+
+        return $status;
+    }
+
+    /**
+     * Subscribe the current folder
+     *
+     * @return bool
+     * @throws Exceptions\ConnectionFailedException
+     * @throws Exceptions\RuntimeException
+     */
+    public function subscribe() {
+        $this->client->openFolder($this->path);
+        return $this->client->getConnection()->subscribeFolder($this->path);
+    }
+
+    /**
+     * Unsubscribe the current folder
+     *
+     * @return bool
+     * @throws Exceptions\ConnectionFailedException
+     * @throws Exceptions\RuntimeException
+     */
+    public function unsubscribe() {
+        $this->client->openFolder($this->path);
+        return $this->client->getConnection()->unsubscribeFolder($this->path);
+    }
+
+    /**
+     * Idle the current connection
+     * @param callable $callback
+     * @param integer $timeout max 1740 seconds - recommended by rfc2177 §3
+     * @param boolean $auto_reconnect try to reconnect on connection close
+     *
+     * @throws ConnectionFailedException
+     * @throws Exceptions\InvalidMessageDateException
+     * @throws Exceptions\MessageContentFetchingException
+     * @throws Exceptions\MessageHeaderFetchingException
+     * @throws Exceptions\RuntimeException
+     * @throws Exceptions\EventNotFoundException
+     * @throws Exceptions\MessageFlagException
+     * @throws Exceptions\MessageNotFoundException
+     */
+    public function idle(callable $callback, $timeout = 1200, $auto_reconnect = false) {
+        $this->client->getConnection()->setConnectionTimeout($timeout);
+
+        $this->client->reconnect();
+        $this->client->openFolder($this->path, true);
+        $connection = $this->client->getConnection();
+
+        $sequence = ClientManager::get('options.sequence', IMAP::ST_MSGN);
+        $connection->idle();
+
+        while (true) {
+            try {
+                $line = $connection->nextLine();
+                if (($pos = strpos($line, "EXISTS")) !== false) {
+                    $msgn = (int) substr($line, 2, $pos -2);
+                    $connection->done();
+
+                    $this->client->openFolder($this->path, true);
+                    $message = $this->query()->getMessageByMsgn($msgn);
+                    $message->setSequence($sequence);
+                    $callback($message);
+
+                    $event = $this->getEvent("message", "new");
+                    $event::dispatch($message);
+
+                    $connection->idle();
+                }
+            }catch (Exceptions\RuntimeException $e) {
+                if(strpos($e->getMessage(), "connection closed") === false) {
+                    throw $e;
+                }
+                if ($auto_reconnect === true) {
+                    $this->client->reconnect();
+                    $this->client->openFolder($this->path, true);
+
+                    $connection = $this->client->getConnection();
+                    $connection->idle();
+                }
+            }
+        }
+    }
+
+    /**
+     * Get folder status information
+     *
+     * @return array|bool
+     * @throws Exceptions\ConnectionFailedException
+     * @throws Exceptions\RuntimeException
+     */
+    public function getStatus() {
+        return $this->examine();
+    }
+
+    /**
+     * Examine the current folder
+     *
+     * @return array
+     * @throws Exceptions\ConnectionFailedException
+     * @throws Exceptions\RuntimeException
+     */
+    public function examine() {
+        return $this->client->getConnection()->examineFolder($this->path);
+    }
+
+    /**
+     * Get the current Client instance
+     *
+     * @return Client
+     */
+    public function getClient() {
+        return $this->client;
+    }
+
+    /**
+     * Set the delimiter
+     * @param $delimiter
+     */
+    public function setDelimiter($delimiter){
+        if(in_array($delimiter, [null, '', ' ', false]) === true) {
+            $delimiter = ClientManager::get('options.delimiter', '/');
+        }
+
+        $this->delimiter = $delimiter;
+    }
+}

+ 753 - 0
htdocs/includes/webklex/php-imap/src/Header.php

@@ -0,0 +1,753 @@
+<?php
+/*
+* File: Header.php
+* Category: -
+* Author: M.Goldenbaum
+* Created: 17.09.20 20:38
+* Updated: -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP;
+
+
+use Carbon\Carbon;
+use Webklex\PHPIMAP\Exceptions\InvalidMessageDateException;
+use Webklex\PHPIMAP\Exceptions\MethodNotFoundException;
+
+/**
+ * Class Header
+ *
+ * @package Webklex\PHPIMAP
+ */
+class Header {
+
+    /**
+     * Raw header
+     *
+     * @var string $raw
+     */
+    public $raw = "";
+
+    /**
+     * Attribute holder
+     *
+     * @var Attribute[]|array $attributes
+     */
+    protected $attributes = [];
+
+    /**
+     * Config holder
+     *
+     * @var array $config
+     */
+    protected $config = [];
+
+    /**
+     * Fallback Encoding
+     *
+     * @var string
+     */
+    public $fallback_encoding = 'UTF-8';
+
+    /**
+     * Convert parsed values to attributes
+     *
+     * @var bool
+     */
+    protected $attributize = false;
+
+    /**
+     * Header constructor.
+     * @param string $raw_header
+     * @param boolean $attributize
+     *
+     * @throws InvalidMessageDateException
+     */
+    public function __construct($raw_header, $attributize = true) {
+        $this->raw = $raw_header;
+        $this->config = ClientManager::get('options');
+        $this->attributize = $attributize;
+        $this->parse();
+    }
+
+    /**
+     * Call dynamic attribute setter and getter methods
+     * @param string $method
+     * @param array $arguments
+     *
+     * @return Attribute|mixed
+     * @throws MethodNotFoundException
+     */
+    public function __call($method, $arguments) {
+        if(strtolower(substr($method, 0, 3)) === 'get') {
+            $name = preg_replace('/(.)(?=[A-Z])/u', '$1_', substr(strtolower($method), 3));
+
+            if(in_array($name, array_keys($this->attributes))) {
+                return $this->attributes[$name];
+            }
+
+        }
+
+        throw new MethodNotFoundException("Method ".self::class.'::'.$method.'() is not supported');
+    }
+
+    /**
+     * Magic getter
+     * @param $name
+     *
+     * @return Attribute|null
+     */
+    public function __get($name) {
+        return $this->get($name);
+    }
+
+    /**
+     * Get a specific header attribute
+     * @param $name
+     *
+     * @return Attribute|mixed
+     */
+    public function get($name) {
+        if(isset($this->attributes[$name])) {
+            return $this->attributes[$name];
+        }
+
+        return null;
+    }
+
+    /**
+     * Set a specific attribute
+     * @param string $name
+     * @param array|mixed $value
+     * @param boolean $strict
+     *
+     * @return Attribute
+     */
+    public function set($name, $value, $strict = false) {
+        if(isset($this->attributes[$name]) && $strict === false) {
+            if ($this->attributize) {
+                $this->attributes[$name]->add($value, true);
+            }else{
+                if(isset($this->attributes[$name])) {
+                    if (is_array($this->attributes[$name]) == false) {
+                        $this->attributes[$name] = [$this->attributes[$name], $value];
+                    }else{
+                        $this->attributes[$name][] = $value;
+                    }
+                }else{
+                    $this->attributes[$name] = $value;
+                }
+            }
+        }elseif($this->attributize == false){
+            $this->attributes[$name] = $value;
+        }else{
+            $this->attributes[$name] = new Attribute($name, $value);
+        }
+
+        return $this->attributes[$name];
+    }
+
+    /**
+     * Perform a regex match all on the raw header and return the first result
+     * @param $pattern
+     *
+     * @return mixed|null
+     */
+    public function find($pattern) {
+        if (preg_match_all($pattern, $this->raw, $matches)) {
+            if (isset($matches[1])) {
+                if(count($matches[1]) > 0) {
+                    return $matches[1][0];
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Try to find a boundary if possible
+     *
+     * @return string|null
+     */
+    public function getBoundary(){
+        $boundary = $this->find("/boundary\=(.*)/i");
+
+        if ($boundary === null) {
+            return null;
+        }
+
+        return $this->clearBoundaryString($boundary);
+    }
+
+    /**
+     * Remove all unwanted chars from a given boundary
+     * @param string $str
+     *
+     * @return string
+     */
+    private function clearBoundaryString($str) {
+        return str_replace(['"', '\r', '\n', "\n", "\r", ";", "\s"], "", $str);
+    }
+
+    /**
+     * Parse the raw headers
+     *
+     * @throws InvalidMessageDateException
+     */
+    protected function parse(){
+        $header = $this->rfc822_parse_headers($this->raw);
+
+        $this->extractAddresses($header);
+
+        if (property_exists($header, 'subject')) {
+            $this->set("subject", $this->decode($header->subject));
+        }
+        if (property_exists($header, 'references')) {
+            $this->set("references", $this->decode($header->references));
+        }
+        if (property_exists($header, 'message_id')) {
+            $this->set("message_id", str_replace(['<', '>'], '', $header->message_id));
+        }
+
+        $this->parseDate($header);
+        foreach ($header as $key => $value) {
+            $key = trim(rtrim(strtolower($key)));
+            if(!isset($this->attributes[$key])){
+                $this->set($key, $value);
+            }
+        }
+
+        $this->extractHeaderExtensions();
+        $this->findPriority();
+    }
+
+    /**
+     * Parse mail headers from a string
+     * @link https://php.net/manual/en/function.imap-rfc822-parse-headers.php
+     * @param $raw_headers
+     *
+     * @return object
+     */
+    public function rfc822_parse_headers($raw_headers){
+        $headers = [];
+        $imap_headers = [];
+        if (extension_loaded('imap') && $this->config["rfc822"]) {
+            $raw_imap_headers = (array) \imap_rfc822_parse_headers($this->raw);
+            foreach($raw_imap_headers as $key => $values) {
+                $key = str_replace("-", "_", $key);
+                $imap_headers[$key] = $values;
+            }
+        }
+        $lines = explode("\r\n", str_replace("\r\n\t", ' ', $raw_headers));
+        $prev_header = null;
+        foreach($lines as $line) {
+            if (substr($line, 0, 1) === "\n") {
+                $line = substr($line, 1);
+            }
+
+            if (substr($line, 0, 1) === "\t") {
+                $line = substr($line, 1);
+                $line = trim(rtrim($line));
+                if ($prev_header !== null) {
+                    $headers[$prev_header][] = $line;
+                }
+            }elseif (substr($line, 0, 1) === " ") {
+                $line = substr($line, 1);
+                $line = trim(rtrim($line));
+                if ($prev_header !== null) {
+                    if (!isset($headers[$prev_header])) {
+                        $headers[$prev_header] = "";
+                    }
+                    if (is_array($headers[$prev_header])) {
+                        $headers[$prev_header][] = $line;
+                    }else{
+                        $headers[$prev_header] .= $line;
+                    }
+                }
+            }else{
+                if (($pos = strpos($line, ":")) > 0) {
+                    $key = trim(rtrim(strtolower(substr($line, 0, $pos))));
+                    $key = str_replace("-", "_", $key);
+
+                    $value = trim(rtrim(substr($line, $pos + 1)));
+                    if (isset($headers[$key])) {
+                        $headers[$key][]  = $value;
+                    }else{
+                        $headers[$key] = [$value];
+                    }
+                    $prev_header = $key;
+                }
+            }
+        }
+
+        foreach($headers as $key => $values) {
+            if (isset($imap_headers[$key])) continue;
+            $value = null;
+            switch($key){
+                case 'from':
+                case 'to':
+                case 'cc':
+                case 'bcc':
+                case 'reply_to':
+                case 'sender':
+                    $value = $this->decodeAddresses($values);
+                    $headers[$key."address"] = implode(", ", $values);
+                    break;
+                case 'subject':
+                    $value = implode(" ", $values);
+                    break;
+                default:
+                    if (is_array($values)) {
+                        foreach($values as $k => $v) {
+                            if ($v == "") {
+                                unset($values[$k]);
+                            }
+                        }
+                        $available_values = count($values);
+                        if ($available_values === 1) {
+                            $value = array_pop($values);
+                        } elseif ($available_values === 2) {
+                            $value = implode(" ", $values);
+                        } elseif ($available_values > 2) {
+                            $value = array_values($values);
+                        } else {
+                            $value = "";
+                        }
+                    }
+                    break;
+            }
+            $headers[$key] = $value;
+        }
+
+        return (object) array_merge($headers, $imap_headers);
+    }
+
+    /**
+     * Decode MIME header elements
+     * @link https://php.net/manual/en/function.imap-mime-header-decode.php
+     * @param string $text The MIME text
+     *
+     * @return array The decoded elements are returned in an array of objects, where each
+     * object has two properties, charset and text.
+     */
+    public function mime_header_decode($text){
+        if (extension_loaded('imap')) {
+            return \imap_mime_header_decode($text);
+        }
+        $charset = $this->getEncoding($text);
+        return [(object)[
+            "charset" => $charset,
+            "text" => $this->convertEncoding($text, $charset)
+        ]];
+    }
+
+    /**
+     * Check if a given pair of strings has ben decoded
+     * @param $encoded
+     * @param $decoded
+     *
+     * @return bool
+     */
+    private function notDecoded($encoded, $decoded) {
+        return 0 === strpos($decoded, '=?')
+            && strlen($decoded) - 2 === strpos($decoded, '?=')
+            && false !== strpos($encoded, $decoded);
+    }
+
+    /**
+     * Convert the encoding
+     * @param $str
+     * @param string $from
+     * @param string $to
+     *
+     * @return mixed|string
+     */
+    public function convertEncoding($str, $from = "ISO-8859-2", $to = "UTF-8") {
+
+        $from = EncodingAliases::get($from, $this->fallback_encoding);
+        $to = EncodingAliases::get($to, $this->fallback_encoding);
+
+        if ($from === $to) {
+            return $str;
+        }
+
+        // We don't need to do convertEncoding() if charset is ASCII (us-ascii):
+        //     ASCII is a subset of UTF-8, so all ASCII files are already UTF-8 encoded
+        //     https://stackoverflow.com/a/11303410
+        //
+        // us-ascii is the same as ASCII:
+        //     ASCII is the traditional name for the encoding system; the Internet Assigned Numbers Authority (IANA)
+        //     prefers the updated name US-ASCII, which clarifies that this system was developed in the US and
+        //     based on the typographical symbols predominantly in use there.
+        //     https://en.wikipedia.org/wiki/ASCII
+        //
+        // convertEncoding() function basically means convertToUtf8(), so when we convert ASCII string into UTF-8 it gets broken.
+        if (strtolower($from) == 'us-ascii' && $to == 'UTF-8') {
+            return $str;
+        }
+
+        try {
+            if (function_exists('iconv') && $from != 'UTF-7' && $to != 'UTF-7') {
+                return iconv($from, $to, $str);
+            } else {
+                if (!$from) {
+                    return mb_convert_encoding($str, $to);
+                }
+                return mb_convert_encoding($str, $to, $from);
+            }
+        } catch (\Exception $e) {
+            if (strstr($from, '-')) {
+                $from = str_replace('-', '', $from);
+                return $this->convertEncoding($str, $from, $to);
+            } else {
+                return $str;
+            }
+        }
+    }
+
+    /**
+     * Get the encoding of a given abject
+     * @param object|string $structure
+     *
+     * @return string
+     */
+    public function getEncoding($structure) {
+        if (property_exists($structure, 'parameters')) {
+            foreach ($structure->parameters as $parameter) {
+                if (strtolower($parameter->attribute) == "charset") {
+                    return EncodingAliases::get($parameter->value, $this->fallback_encoding);
+                }
+            }
+        }elseif (property_exists($structure, 'charset')) {
+            return EncodingAliases::get($structure->charset, $this->fallback_encoding);
+        }elseif (is_string($structure) === true){
+            return mb_detect_encoding($structure);
+        }
+
+        return $this->fallback_encoding;
+    }
+
+    /**
+     * Test if a given value is utf-8 encoded
+     * @param $value
+     *
+     * @return bool
+     */
+    private function is_uft8($value) {
+        return strpos(strtolower($value), '=?utf-8?') === 0;
+    }
+
+    /**
+     * Try to decode a specific header
+     * @param mixed $value
+     *
+     * @return mixed
+     */
+    private function decode($value) {
+        if (is_array($value)) {
+            return $this->decodeArray($value);
+        }
+        $original_value = $value;
+        $decoder = $this->config['decoder']['message'];
+
+        if ($value !== null) {
+            $is_utf8_base = $this->is_uft8($value);
+
+            if($decoder === 'utf-8' && extension_loaded('imap')) {
+                $value = \imap_utf8($value);
+                $is_utf8_base = $this->is_uft8($value);
+                if ($is_utf8_base) {
+                    $value = mb_decode_mimeheader($value);
+                }
+                if ($this->notDecoded($original_value, $value)) {
+                    $decoded_value = $this->mime_header_decode($value);
+                    if (count($decoded_value) > 0) {
+                        if(property_exists($decoded_value[0], "text")) {
+                            $value = $decoded_value[0]->text;
+                        }
+                    }
+                }
+            }elseif($decoder === 'iconv' && $is_utf8_base) {
+                $value = iconv_mime_decode($value);
+            }elseif($is_utf8_base){
+                $value = mb_decode_mimeheader($value);
+            }
+
+            if ($this->is_uft8($value)) {
+                $value = mb_decode_mimeheader($value);
+            }
+
+            if ($this->notDecoded($original_value, $value)) {
+                $value = $this->convertEncoding($original_value, $this->getEncoding($original_value));
+            }
+        }
+
+        return $value;
+    }
+
+    /**
+     * Decode a given array
+     * @param array $values
+     *
+     * @return array
+     */
+    private function decodeArray($values) {
+        foreach($values as $key => $value) {
+            $values[$key] = $this->decode($value);
+        }
+        return $values;
+    }
+
+    /**
+     * Try to extract the priority from a given raw header string
+     */
+    private function findPriority() {
+        if(($priority = $this->get("x_priority")) === null) return;
+        switch((int)"$priority"){
+            case IMAP::MESSAGE_PRIORITY_HIGHEST;
+                $priority = IMAP::MESSAGE_PRIORITY_HIGHEST;
+                break;
+            case IMAP::MESSAGE_PRIORITY_HIGH;
+                $priority = IMAP::MESSAGE_PRIORITY_HIGH;
+                break;
+            case IMAP::MESSAGE_PRIORITY_NORMAL;
+                $priority = IMAP::MESSAGE_PRIORITY_NORMAL;
+                break;
+            case IMAP::MESSAGE_PRIORITY_LOW;
+                $priority = IMAP::MESSAGE_PRIORITY_LOW;
+                break;
+            case IMAP::MESSAGE_PRIORITY_LOWEST;
+                $priority = IMAP::MESSAGE_PRIORITY_LOWEST;
+                break;
+            default:
+                $priority = IMAP::MESSAGE_PRIORITY_UNKNOWN;
+                break;
+        }
+
+        $this->set("priority", $priority);
+    }
+
+    /**
+     * Extract a given part as address array from a given header
+     * @param $values
+     *
+     * @return array
+     */
+    private function decodeAddresses($values) {
+        $addresses = [];
+
+        if (extension_loaded('mailparse') && $this->config["rfc822"]) {
+            foreach ($values as $address) {
+                foreach (\mailparse_rfc822_parse_addresses($address) as $parsed_address) {
+                    if (isset($parsed_address['address'])) {
+                        $mail_address = explode('@', $parsed_address['address']);
+                        if (count($mail_address) == 2) {
+                            $addresses[] = (object)[
+                                "personal" => isset($parsed_address['display']) ? $parsed_address['display'] : '',
+                                "mailbox" => $mail_address[0],
+                                "host" => $mail_address[1],
+                            ];
+                        }
+                    }
+                }
+            }
+
+            return $addresses;
+        }
+
+        foreach($values as $address) {
+            foreach (preg_split('/, (?=(?:[^"]*"[^"]*")*[^"]*$)/', $address) as $split_address) {
+                $split_address = trim(rtrim($split_address));
+
+                if (strpos($split_address, ",") == strlen($split_address) - 1) {
+                    $split_address = substr($split_address, 0, -1);
+                }
+                if (preg_match(
+                    '/^(?:(?P<name>.+)\s)?(?(name)<|<?)(?P<email>[^\s]+?)(?(name)>|>?)$/',
+                    $split_address,
+                    $matches
+                )) {
+                    $name = trim(rtrim($matches["name"]));
+                    $email = trim(rtrim($matches["email"]));
+                    list($mailbox, $host) = array_pad(explode("@", $email), 2, null);
+                    $addresses[] = (object)[
+                        "personal" => $name,
+                        "mailbox" => $mailbox,
+                        "host" => $host,
+                    ];
+                }
+            }
+        }
+
+        return $addresses;
+    }
+
+    /**
+     * Extract a given part as address array from a given header
+     * @param object $header
+     */
+    private function extractAddresses($header) {
+        foreach(['from', 'to', 'cc', 'bcc', 'reply_to', 'sender'] as $key){
+            if (property_exists($header, $key)) {
+                $this->set($key, $this->parseAddresses($header->$key));
+            }
+        }
+    }
+
+    /**
+     * Parse Addresses
+     * @param $list
+     *
+     * @return array
+     */
+    private function parseAddresses($list) {
+        $addresses = [];
+
+        if (is_array($list) === false) {
+            return $addresses;
+        }
+
+        foreach ($list as $item) {
+            $address = (object) $item;
+
+            if (!property_exists($address, 'mailbox')) {
+                $address->mailbox = false;
+            }
+            if (!property_exists($address, 'host')) {
+                $address->host = false;
+            }
+            if (!property_exists($address, 'personal')) {
+                $address->personal = false;
+            } else {
+                $personalParts = $this->mime_header_decode($address->personal);
+
+                if(is_array($personalParts)) {
+                    $address->personal = '';
+                    foreach ($personalParts as $p) {
+                        $address->personal .= $this->convertEncoding($p->text, $this->getEncoding($p));
+                    }
+                }
+
+                if (strpos($address->personal, "'") === 0) {
+                    $address->personal = str_replace("'", "", $address->personal);
+                }
+            }
+
+            $address->mail = ($address->mailbox && $address->host) ? $address->mailbox.'@'.$address->host : false;
+            $address->full = ($address->personal) ? $address->personal.' <'.$address->mail.'>' : $address->mail;
+
+            $addresses[] = new Address($address);
+        }
+
+        return $addresses;
+    }
+
+    /**
+     * Search and extract potential header extensions
+     */
+    private function extractHeaderExtensions(){
+        foreach ($this->attributes as $key => $value) {
+            if (is_array($value)) {
+                $value = implode(", ", $value);
+            }else{
+                $value = (string)$value;
+            }
+            // Only parse strings and don't parse any attributes like the user-agent
+            if (in_array($key, ["user_agent"]) === false) {
+                if (($pos = strpos($value, ";")) !== false){
+                    $original = substr($value, 0, $pos);
+                    $this->set($key, trim(rtrim($original)), true);
+
+                    // Get all potential extensions
+                    $extensions = explode(";", substr($value, $pos + 1));
+                    foreach($extensions as $extension) {
+                        if (($pos = strpos($extension, "=")) !== false){
+                            $key = substr($extension, 0, $pos);
+                            $key = trim(rtrim(strtolower($key)));
+
+                            if (isset($this->attributes[$key]) === false) {
+                                $value = substr($extension, $pos + 1);
+                                $value = str_replace('"', "", $value);
+                                $value = trim(rtrim($value));
+
+                                $this->set($key, $value);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Exception handling for invalid dates
+     *
+     * Currently known invalid formats:
+     * ^ Datetime                                   ^ Problem                           ^ Cause
+     * | Mon, 20 Nov 2017 20:31:31 +0800 (GMT+8:00) | Double timezone specification     | A Windows feature
+     * | Thu, 8 Nov 2018 08:54:58 -0200 (-02)       |
+     * |                                            | and invalid timezone (max 6 char) |
+     * | 04 Jan 2018 10:12:47 UT                    | Missing letter "C"                | Unknown
+     * | Thu, 31 May 2018 18:15:00 +0800 (added by) | Non-standard details added by the | Unknown
+     * |                                            | mail server                       |
+     * | Sat, 31 Aug 2013 20:08:23 +0580            | Invalid timezone                  | PHPMailer bug https://sourceforge.net/p/phpmailer/mailman/message/6132703/
+     *
+     * Please report any new invalid timestamps to [#45](https://github.com/Webklex/php-imap/issues)
+     *
+     * @param object $header
+     *
+     * @throws InvalidMessageDateException
+     */
+    private function parseDate($header) {
+
+        if (property_exists($header, 'date')) {
+            $parsed_date = null;
+            $date = $header->date;
+
+            if(preg_match('/\+0580/', $date)) {
+                $date = str_replace('+0580', '+0530', $date);
+            }
+
+            $date = trim(rtrim($date));
+            try {
+                $parsed_date = Carbon::parse($date);
+            } catch (\Exception $e) {
+                switch (true) {
+                    case preg_match('/([0-9]{1,2}\ [A-Z]{2,3}\ [0-9]{4}\ [0-9]{1,2}\:[0-9]{1,2}\:[0-9]{1,2}\ UT)+$/i', $date) > 0:
+                    case preg_match('/([A-Z]{2,3}\,\ [0-9]{1,2}\ [A-Z]{2,3}\ [0-9]{4}\ [0-9]{1,2}\:[0-9]{1,2}\:[0-9]{1,2}\ UT)+$/i', $date) > 0:
+                        $date .= 'C';
+                        break;
+                    case preg_match('/([A-Z]{2,3}\,\ [0-9]{1,2}\ [A-Z]{2,3}\ [0-9]{4}\ [0-9]{1,2}\:[0-9]{1,2}\:[0-9]{1,2}\ \+[0-9]{2,4}\ \(\+[0-9]{1,2}\))+$/i', $date) > 0:
+                    case preg_match('/([A-Z]{2,3}[\,|\ \,]\ [0-9]{1,2}\ [A-Z]{2,3}\ [0-9]{4}\ [0-9]{1,2}\:[0-9]{1,2}\:[0-9]{1,2}.*)+$/i', $date) > 0:
+                    case preg_match('/([A-Z]{2,3}\,\ [0-9]{1,2}\ [A-Z]{2,3}\ [0-9]{4}\ [0-9]{1,2}\:[0-9]{1,2}\:[0-9]{1,2}\ [\-|\+][0-9]{4}\ \(.*)\)+$/i', $date) > 0:
+                    case preg_match('/([A-Z]{2,3}\, \ [0-9]{1,2}\ [A-Z]{2,3}\ [0-9]{4}\ [0-9]{1,2}\:[0-9]{1,2}\:[0-9]{1,2}\ [\-|\+][0-9]{4}\ \(.*)\)+$/i', $date) > 0:
+                    case preg_match('/([0-9]{1,2}\ [A-Z]{2,3}\ [0-9]{2,4}\ [0-9]{2}\:[0-9]{2}\:[0-9]{2}\ [A-Z]{2}\ \-[0-9]{2}\:[0-9]{2}\ \([A-Z]{2,3}\ \-[0-9]{2}:[0-9]{2}\))+$/i', $date) > 0:
+                        $array = explode('(', $date);
+                        $array = array_reverse($array);
+                        $date = trim(array_pop($array));
+                        break;
+                }
+                try{
+                    $parsed_date = Carbon::parse($date);
+                } catch (\Exception $_e) {
+                    throw new InvalidMessageDateException("Invalid message date. ID:".$this->get("message_id"), 1100, $e);
+                }
+            }
+
+            $this->set("date", $parsed_date);
+        }
+    }
+
+    /**
+     * Get all available attributes
+     *
+     * @return array
+     */
+    public function getAttributes() {
+        return $this->attributes;
+    }
+
+}

+ 375 - 0
htdocs/includes/webklex/php-imap/src/IMAP.php

@@ -0,0 +1,375 @@
+<?php
+/*
+* File: IMAP.php
+* Category: -
+* Author: M.Goldenbaum
+* Created: 14.03.19 18:22
+* Updated: -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP;
+
+/**
+ * Class IMAP
+ *
+ * Independent imap const holder
+ */
+class IMAP {
+
+    /**
+     * Message const
+     *
+     * @const integer   TYPE_TEXT
+     * @const integer   TYPE_MULTIPART
+     *
+     * @const integer   ENC_7BIT
+     * @const integer   ENC_8BIT
+     * @const integer   ENC_BINARY
+     * @const integer   ENC_BASE64
+     * @const integer   ENC_QUOTED_PRINTABLE
+     * @const integer   ENC_OTHER
+     */
+    const MESSAGE_TYPE_TEXT = 0;
+    const MESSAGE_TYPE_MULTIPART = 1;
+
+    const MESSAGE_ENC_7BIT = 0;
+    const MESSAGE_ENC_8BIT = 1;
+    const MESSAGE_ENC_BINARY = 2;
+    const MESSAGE_ENC_BASE64 = 3;
+    const MESSAGE_ENC_QUOTED_PRINTABLE = 4;
+    const MESSAGE_ENC_OTHER = 5;
+
+    const MESSAGE_PRIORITY_UNKNOWN = 0;
+    const MESSAGE_PRIORITY_HIGHEST = 1;
+    const MESSAGE_PRIORITY_HIGH = 2;
+    const MESSAGE_PRIORITY_NORMAL = 3;
+    const MESSAGE_PRIORITY_LOW = 4;
+    const MESSAGE_PRIORITY_LOWEST = 5;
+
+    /**
+     * Attachment const
+     *
+     * @const integer   TYPE_TEXT
+     * @const integer   TYPE_MULTIPART
+     * @const integer   TYPE_MESSAGE
+     * @const integer   TYPE_APPLICATION
+     * @const integer   TYPE_AUDIO
+     * @const integer   TYPE_IMAGE
+     * @const integer   TYPE_VIDEO
+     * @const integer   TYPE_MODEL
+     * @const integer   TYPE_OTHER
+     */
+    const ATTACHMENT_TYPE_TEXT = 0;
+    const ATTACHMENT_TYPE_MULTIPART = 1;
+    const ATTACHMENT_TYPE_MESSAGE = 2;
+    const ATTACHMENT_TYPE_APPLICATION = 3;
+    const ATTACHMENT_TYPE_AUDIO = 4;
+    const ATTACHMENT_TYPE_IMAGE = 5;
+    const ATTACHMENT_TYPE_VIDEO = 6;
+    const ATTACHMENT_TYPE_MODEL = 7;
+    const ATTACHMENT_TYPE_OTHER = 8;
+
+    /**
+     * Client const
+     *
+     * @const integer   CLIENT_OPENTIMEOUT
+     * @const integer   CLIENT_READTIMEOUT
+     * @const integer   CLIENT_WRITETIMEOUT
+     * @const integer   CLIENT_CLOSETIMEOUT
+     */
+    const CLIENT_OPENTIMEOUT = 1;
+    const CLIENT_READTIMEOUT = 2;
+    const CLIENT_WRITETIMEOUT = 3;
+    const CLIENT_CLOSETIMEOUT = 4;
+
+    /**
+     * Generic imap const
+     *
+     * @const integer NIL
+     * @const integer IMAP_OPENTIMEOUT
+     * @const integer IMAP_READTIMEOUT
+     * @const integer IMAP_WRITETIMEOUT
+     * @const integer IMAP_CLOSETIMEOUT
+     * @const integer OP_DEBUG
+     * @const integer OP_READONLY
+     * @const integer OP_ANONYMOUS
+     * @const integer OP_SHORTCACHE
+     * @const integer OP_SILENT
+     * @const integer OP_PROTOTYPE
+     * @const integer OP_HALFOPEN
+     * @const integer OP_EXPUNGE
+     * @const integer OP_SECURE
+     * @const integer CL_EXPUNGE
+     * @const integer FT_UID
+     * @const integer FT_PEEK
+     * @const integer FT_NOT
+     * @const integer FT_INTERNAL
+     * @const integer FT_PREFETCHTEXT
+     * @const integer ST_UID
+     * @const integer ST_SILENT
+     * @const integer ST_SET
+     * @const integer CP_UID
+     * @const integer CP_MOVE
+     * @const integer SE_UID
+     * @const integer SE_FREE
+     * @const integer SE_NOPREFETCH
+     * @const integer SO_FREE
+     * @const integer SO_NOSERVER
+     * @const integer SA_MESSAGES
+     * @const integer SA_RECENT
+     * @const integer SA_UNSEEN
+     * @const integer SA_UIDNEXT
+     * @const integer SA_UIDVALIDITY
+     * @const integer SA_ALL
+     * @const integer LATT_NOINFERIORS
+     * @const integer LATT_NOSELECT
+     * @const integer LATT_MARKED
+     * @const integer LATT_UNMARKED
+     * @const integer LATT_REFERRAL
+     * @const integer LATT_HASCHILDREN
+     * @const integer LATT_HASNOCHILDREN
+     * @const integer SORTDATE
+     * @const integer SORTARRIVAL
+     * @const integer SORTFROM
+     * @const integer SORTSUBJECT
+     * @const integer SORTTO
+     * @const integer SORTCC
+     * @const integer SORTSIZE
+     * @const integer TYPETEXT
+     * @const integer TYPEMULTIPART
+     * @const integer TYPEMESSAGE
+     * @const integer TYPEAPPLICATION
+     * @const integer TYPEAUDIO
+     * @const integer TYPEIMAGE
+     * @const integer TYPEVIDEO
+     * @const integer TYPEMODEL
+     * @const integer TYPEOTHER
+     * @const integer ENC7BIT
+     * @const integer ENC8BIT
+     * @const integer ENCBINARY
+     * @const integer ENCBASE64
+     * @const integer ENCQUOTEDPRINTABLE
+     * @const integer ENCOTHER
+     * @const integer IMAP_GC_ELT
+     * @const integer IMAP_GC_ENV
+     * @const integer IMAP_GC_TEXTS
+     */
+    
+    const NIL = 0;
+    const IMAP_OPENTIMEOUT = 1;
+    const IMAP_READTIMEOUT = 2;
+    const IMAP_WRITETIMEOUT = 3;
+    const IMAP_CLOSETIMEOUT = 4;
+    const OP_DEBUG = 1;
+
+    /**
+     * Open mailbox read-only
+     * @link http://php.net/manual/en/imap.constants.php
+     */
+    const OP_READONLY = 2;
+
+    /**
+     * Don't use or update a .newsrc for news
+     * (NNTP only)
+     * @link http://php.net/manual/en/imap.constants.php
+     */
+    const OP_ANONYMOUS = 4;
+    const OP_SHORTCACHE = 8;
+    const OP_SILENT = 16;
+    const OP_PROTOTYPE = 32;
+
+    /**
+     * For IMAP and NNTP
+     * names, open a connection but don't open a mailbox.
+     * @link http://php.net/manual/en/imap.constants.php
+     */
+    const OP_HALFOPEN = 64;
+    const OP_EXPUNGE = 128;
+    const OP_SECURE = 256;
+
+    /**
+     * silently expunge the mailbox before closing when
+     * calling <b>imap_close</b>
+     * @link http://php.net/manual/en/imap.constants.php
+     */
+    const CL_EXPUNGE = 32768;
+
+    /**
+     * The parameter is a UID
+     * @link http://php.net/manual/en/imap.constants.php
+     */
+    const FT_UID = 1;
+
+    /**
+     * Do not set the \Seen flag if not already set
+     * @link http://php.net/manual/en/imap.constants.php
+     */
+    const FT_PEEK = 2;
+    const FT_NOT = 4;
+
+    /**
+     * The return string is in internal format, will not canonicalize to CRLF.
+     * @link http://php.net/manual/en/imap.constants.php
+     */
+    const FT_INTERNAL = 8;
+    const FT_PREFETCHTEXT = 32;
+
+    /**
+     * The sequence argument contains UIDs instead of sequence numbers
+     * @link http://php.net/manual/en/imap.constants.php
+     */
+    const ST_UID = 1;
+    const ST_SILENT = 2;
+    const ST_MSGN = 3;
+    const ST_SET = 4;
+
+    /**
+     * the sequence numbers contain UIDS
+     * @link http://php.net/manual/en/imap.constants.php
+     */
+    const CP_UID = 1;
+
+    /**
+     * Delete the messages from the current mailbox after copying
+     * with <b>imap_mail_copy</b>
+     * @link http://php.net/manual/en/imap.constants.php
+     */
+    const CP_MOVE = 2;
+
+    /**
+     * Return UIDs instead of sequence numbers
+     * @link http://php.net/manual/en/imap.constants.php
+     */
+    const SE_UID = 1;
+    const SE_FREE = 2;
+
+    /**
+     * Don't prefetch searched messages
+     * @link http://php.net/manual/en/imap.constants.php
+     */
+    const SE_NOPREFETCH = 4;
+    const SO_FREE = 8;
+    const SO_NOSERVER = 16;
+    const SA_MESSAGES = 1;
+    const SA_RECENT = 2;
+    const SA_UNSEEN = 4;
+    const SA_UIDNEXT = 8;
+    const SA_UIDVALIDITY = 16;
+    const SA_ALL = 31;
+
+    /**
+     * This mailbox has no "children" (there are no
+     * mailboxes below this one).
+     * @link http://php.net/manual/en/imap.constants.php
+     */
+    const LATT_NOINFERIORS = 1;
+
+    /**
+     * This is only a container, not a mailbox - you
+     * cannot open it.
+     * @link http://php.net/manual/en/imap.constants.php
+     */
+    const LATT_NOSELECT = 2;
+
+    /**
+     * This mailbox is marked. Only used by UW-IMAPD.
+     * @link http://php.net/manual/en/imap.constants.php
+     */
+    const LATT_MARKED = 4;
+
+    /**
+     * This mailbox is not marked. Only used by
+     * UW-IMAPD.
+     * @link http://php.net/manual/en/imap.constants.php
+     */
+    const LATT_UNMARKED = 8;
+    const LATT_REFERRAL = 16;
+    const LATT_HASCHILDREN = 32;
+    const LATT_HASNOCHILDREN = 64;
+
+    /**
+     * Sort criteria for <b>imap_sort</b>:
+     * message Date
+     * @link http://php.net/manual/en/imap.constants.php
+     */
+    const SORTDATE = 0;
+
+    /**
+     * Sort criteria for <b>imap_sort</b>:
+     * arrival date
+     * @link http://php.net/manual/en/imap.constants.php
+     */
+    const SORTARRIVAL = 1;
+
+    /**
+     * Sort criteria for <b>imap_sort</b>:
+     * mailbox in first From address
+     * @link http://php.net/manual/en/imap.constants.php
+     */
+    const SORTFROM = 2;
+
+    /**
+     * Sort criteria for <b>imap_sort</b>:
+     * message subject
+     * @link http://php.net/manual/en/imap.constants.php
+     */
+    const SORTSUBJECT = 3;
+
+    /**
+     * Sort criteria for <b>imap_sort</b>:
+     * mailbox in first To address
+     * @link http://php.net/manual/en/imap.constants.php
+     */
+    const SORTTO = 4;
+
+    /**
+     * Sort criteria for <b>imap_sort</b>:
+     * mailbox in first cc address
+     * @link http://php.net/manual/en/imap.constants.php
+     */
+    const SORTCC = 5;
+
+    /**
+     * Sort criteria for <b>imap_sort</b>:
+     * size of message in octets
+     * @link http://php.net/manual/en/imap.constants.php
+     */
+    const SORTSIZE = 6;
+    const TYPETEXT = 0;
+    const TYPEMULTIPART = 1;
+    const TYPEMESSAGE = 2;
+    const TYPEAPPLICATION = 3;
+    const TYPEAUDIO = 4;
+    const TYPEIMAGE = 5;
+    const TYPEVIDEO = 6;
+    const TYPEMODEL = 7;
+    const TYPEOTHER = 8;
+    const ENC7BIT = 0;
+    const ENC8BIT = 1;
+    const ENCBINARY = 2;
+    const ENCBASE64 = 3;
+    const ENCQUOTEDPRINTABLE = 4;
+    const ENCOTHER = 5;
+
+    /**
+     * Garbage collector, clear message cache elements.
+     * @link http://php.net/manual/en/imap.constants.php
+     */
+    const IMAP_GC_ELT = 1;
+
+    /**
+     * Garbage collector, clear envelopes and bodies.
+     * @link http://php.net/manual/en/imap.constants.php
+     */
+    const IMAP_GC_ENV = 2;
+
+    /**
+     * Garbage collector, clear texts.
+     * @link http://php.net/manual/en/imap.constants.php
+     */
+    const IMAP_GC_TEXTS = 4;
+    
+}

+ 1419 - 0
htdocs/includes/webklex/php-imap/src/Message.php

@@ -0,0 +1,1419 @@
+<?php
+/*
+* File:     Message.php
+* Category: -
+* Author:   M. Goldenbaum
+* Created:  19.01.17 22:21
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP;
+
+use ReflectionClass;
+use ReflectionException;
+use Webklex\PHPIMAP\Exceptions\InvalidMessageDateException;
+use Webklex\PHPIMAP\Exceptions\MaskNotFoundException;
+use Webklex\PHPIMAP\Exceptions\MessageContentFetchingException;
+use Webklex\PHPIMAP\Exceptions\MessageFlagException;
+use Webklex\PHPIMAP\Exceptions\MessageHeaderFetchingException;
+use Webklex\PHPIMAP\Exceptions\MethodNotFoundException;
+use Webklex\PHPIMAP\Support\AttachmentCollection;
+use Webklex\PHPIMAP\Support\FlagCollection;
+use Webklex\PHPIMAP\Support\Masks\MessageMask;
+use Illuminate\Support\Str;
+use Webklex\PHPIMAP\Support\MessageCollection;
+use Webklex\PHPIMAP\Traits\HasEvents;
+
+/**
+ * Class Message
+ *
+ * @package Webklex\PHPIMAP
+ *
+ * @property integer msglist
+ * @property integer uid
+ * @property integer msgn
+ * @property Attribute subject
+ * @property Attribute message_id
+ * @property Attribute message_no
+ * @property Attribute references
+ * @property Attribute date
+ * @property Attribute from
+ * @property Attribute to
+ * @property Attribute cc
+ * @property Attribute bcc
+ * @property Attribute reply_to
+ * @property Attribute in_reply_to
+ * @property Attribute sender
+ *
+ * @method integer getMsglist()
+ * @method integer setMsglist($msglist)
+ * @method integer getUid()
+ * @method integer getMsgn()
+ * @method Attribute getPriority()
+ * @method Attribute getSubject()
+ * @method Attribute getMessageId()
+ * @method Attribute getMessageNo()
+ * @method Attribute getReferences()
+ * @method Attribute getDate()
+ * @method Attribute getFrom()
+ * @method Attribute getTo()
+ * @method Attribute getCc()
+ * @method Attribute getBcc()
+ * @method Attribute getReplyTo()
+ * @method Attribute getInReplyTo()
+ * @method Attribute getSender()
+ */
+class Message {
+    use HasEvents;
+
+    /**
+     * Client instance
+     *
+     * @var Client
+     */
+    private $client = Client::class;
+
+    /**
+     * Default mask
+     *
+     * @var string $mask
+     */
+    protected $mask = MessageMask::class;
+
+    /**
+     * Used config
+     *
+     * @var array $config
+     */
+    protected $config = [];
+
+    /**
+     * Attribute holder
+     *
+     * @var Attribute[]|mixed[] $attributes
+     */
+    protected $attributes = [];
+
+    /**
+     * The message folder path
+     *
+     * @var string $folder_path
+     */
+    protected $folder_path;
+
+    /**
+     * Fetch body options
+     *
+     * @var integer
+     */
+    public $fetch_options = null;
+
+    /**
+     * @var integer
+     */
+    protected $sequence = IMAP::NIL;
+
+    /**
+     * Fetch body options
+     *
+     * @var bool
+     */
+    public $fetch_body = null;
+
+    /**
+     * Fetch flags options
+     *
+     * @var bool
+     */
+    public $fetch_flags = null;
+
+    /**
+     * @var Header $header
+     */
+    public $header = null;
+
+    /**
+     * Raw message body
+     *
+     * @var null|string $raw_body
+     */
+    public $raw_body = null;
+
+    /**
+     * Message structure
+     *
+     * @var Structure $structure
+     */
+    protected $structure = null;
+
+    /**
+     * Message body components
+     *
+     * @var array   $bodies
+     */
+    public $bodies = [];
+
+    /** @var AttachmentCollection $attachments */
+    public $attachments;
+
+    /** @var FlagCollection $flags */
+    public $flags;
+
+    /**
+     * A list of all available and supported flags
+     *
+     * @var array $available_flags
+     */
+    private $available_flags = null;
+
+    /**
+     * Message constructor.
+     * @param integer $uid
+     * @param integer|null $msglist
+     * @param Client $client
+     * @param integer|null $fetch_options
+     * @param boolean $fetch_body
+     * @param boolean $fetch_flags
+     * @param integer $sequence
+     *
+     * @throws Exceptions\ConnectionFailedException
+     * @throws InvalidMessageDateException
+     * @throws Exceptions\RuntimeException
+     * @throws MessageHeaderFetchingException
+     * @throws MessageContentFetchingException
+     * @throws Exceptions\EventNotFoundException
+     * @throws MessageFlagException
+     * @throws Exceptions\MessageNotFoundException
+     */
+    public function __construct($uid, $msglist, Client $client, $fetch_options = null, $fetch_body = false, $fetch_flags = false, $sequence = null) {
+        $this->boot();
+
+        $default_mask = $client->getDefaultMessageMask();
+        if($default_mask != null) {
+            $this->mask = $default_mask;
+        }
+        $this->events["message"] = $client->getDefaultEvents("message");
+        $this->events["flag"] = $client->getDefaultEvents("flag");
+
+        $this->folder_path = $client->getFolderPath();
+
+        $this->setSequence($sequence);
+        $this->setFetchOption($fetch_options);
+        $this->setFetchBodyOption($fetch_body);
+        $this->setFetchFlagsOption($fetch_flags);
+
+        $this->client = $client;
+        $this->client->openFolder($this->folder_path);
+
+        $this->setSequenceId($uid, $msglist);
+
+        if ($this->fetch_options == IMAP::FT_PEEK) {
+            $this->parseFlags();
+        }
+
+        $this->parseHeader();
+
+        if ($this->getFetchBodyOption() === true) {
+            $this->parseBody();
+        }
+
+        if ($this->getFetchFlagsOption() === true && $this->fetch_options !== IMAP::FT_PEEK) {
+            $this->parseFlags();
+        }
+    }
+
+    /**
+     * Create a new instance without fetching the message header and providing them raw instead
+     * @param int $uid
+     * @param int|null $msglist
+     * @param Client $client
+     * @param string $raw_header
+     * @param string $raw_body
+     * @param array $raw_flags
+     * @param null $fetch_options
+     * @param null $sequence
+     *
+     * @return Message
+     * @throws Exceptions\ConnectionFailedException
+     * @throws Exceptions\EventNotFoundException
+     * @throws InvalidMessageDateException
+     * @throws MessageContentFetchingException
+     * @throws ReflectionException
+     * @throws MessageFlagException
+     * @throws Exceptions\RuntimeException
+     * @throws Exceptions\MessageNotFoundException
+     */
+    public static function make($uid, $msglist, Client $client, $raw_header, $raw_body, $raw_flags, $fetch_options = null, $sequence = null){
+        $reflection = new ReflectionClass(self::class);
+        /** @var self $instance */
+        $instance = $reflection->newInstanceWithoutConstructor();
+        $instance->boot();
+
+        $default_mask = $client->getDefaultMessageMask();
+        if($default_mask != null) {
+            $instance->setMask($default_mask);
+        }
+        $instance->setEvents([
+            "message" => $client->getDefaultEvents("message"),
+            "flag" => $client->getDefaultEvents("flag"),
+        ]);
+        $instance->setFolderPath($client->getFolderPath());
+        $instance->setSequence($sequence);
+        $instance->setFetchOption($fetch_options);
+
+        $instance->setClient($client);
+        $instance->setSequenceId($uid, $msglist);
+
+        $instance->parseRawHeader($raw_header);
+        $instance->parseRawFlags($raw_flags);
+        $instance->parseRawBody($raw_body);
+        $instance->peek();
+
+        return $instance;
+    }
+
+    /**
+     * Boot a new instance
+     */
+    public function boot(){
+        $this->attributes = [];
+
+        $this->config = ClientManager::get('options');
+        $this->available_flags = ClientManager::get('flags');
+
+        $this->attachments = AttachmentCollection::make([]);
+        $this->flags = FlagCollection::make([]);
+    }
+
+    /**
+     * Call dynamic attribute setter and getter methods
+     * @param string $method
+     * @param array $arguments
+     *
+     * @return mixed
+     * @throws MethodNotFoundException
+     */
+    public function __call($method, $arguments) {
+        if(strtolower(substr($method, 0, 3)) === 'get') {
+            $name = Str::snake(substr($method, 3));
+            return $this->get($name);
+        }elseif (strtolower(substr($method, 0, 3)) === 'set') {
+            $name = Str::snake(substr($method, 3));
+
+            if(in_array($name, array_keys($this->attributes))) {
+                return $this->__set($name, array_pop($arguments));
+            }
+
+        }
+
+        throw new MethodNotFoundException("Method ".self::class.'::'.$method.'() is not supported');
+    }
+
+    /**
+     * Magic setter
+     * @param $name
+     * @param $value
+     *
+     * @return mixed
+     */
+    public function __set($name, $value) {
+        $this->attributes[$name] = $value;
+
+        return $this->attributes[$name];
+    }
+
+    /**
+     * Magic getter
+     * @param $name
+     *
+     * @return Attribute|mixed|null
+     */
+    public function __get($name) {
+        return $this->get($name);
+    }
+
+    /**
+     * Get an available message or message header attribute
+     * @param $name
+     *
+     * @return Attribute|mixed|null
+     */
+    public function get($name) {
+        if(isset($this->attributes[$name])) {
+            return $this->attributes[$name];
+        }
+
+        return $this->header->get($name);
+    }
+
+    /**
+     * Check if the Message has a text body
+     *
+     * @return bool
+     */
+    public function hasTextBody() {
+        return isset($this->bodies['text']);
+    }
+
+    /**
+     * Get the Message text body
+     *
+     * @return mixed
+     */
+    public function getTextBody() {
+        if (!isset($this->bodies['text'])) {
+            return null;
+        }
+
+        return $this->bodies['text'];
+    }
+
+    /**
+     * Check if the Message has a html body
+     *
+     * @return bool
+     */
+    public function hasHTMLBody() {
+        return isset($this->bodies['html']);
+    }
+
+    /**
+     * Get the Message html body
+     *
+     * @return string|null
+     */
+    public function getHTMLBody() {
+        if (!isset($this->bodies['html'])) {
+            return null;
+        }
+
+        return $this->bodies['html'];
+    }
+
+    /**
+     * Parse all defined headers
+     *
+     * @throws Exceptions\ConnectionFailedException
+     * @throws Exceptions\RuntimeException
+     * @throws InvalidMessageDateException
+     * @throws MessageHeaderFetchingException
+     */
+    private function parseHeader() {
+        $sequence_id = $this->getSequenceId();
+        $headers = $this->client->getConnection()->headers([$sequence_id], "RFC822", $this->sequence === IMAP::ST_UID);
+        if (!isset($headers[$sequence_id])) {
+            throw new MessageHeaderFetchingException("no headers found", 0);
+        }
+
+        $this->parseRawHeader($headers[$sequence_id]);
+    }
+
+    /**
+     * @param string $raw_header
+     *
+     * @throws InvalidMessageDateException
+     */
+    public function parseRawHeader($raw_header){
+        $this->header = new Header($raw_header);
+    }
+
+    /**
+     * Parse additional raw flags
+     * @param array $raw_flags
+     */
+    public function parseRawFlags($raw_flags) {
+        $this->flags = FlagCollection::make([]);
+
+        foreach($raw_flags as $flag) {
+            if (strpos($flag, "\\") === 0){
+                $flag = substr($flag, 1);
+            }
+            $flag_key = strtolower($flag);
+            if ($this->available_flags === null || in_array($flag_key, $this->available_flags)) {
+                $this->flags->put($flag_key, $flag);
+            }
+        }
+    }
+
+    /**
+     * Parse additional flags
+     *
+     * @return void
+     * @throws Exceptions\ConnectionFailedException
+     * @throws MessageFlagException
+     * @throws Exceptions\RuntimeException
+     */
+    private function parseFlags() {
+        $this->client->openFolder($this->folder_path);
+        $this->flags = FlagCollection::make([]);
+
+        $sequence_id = $this->getSequenceId();
+        try {
+            $flags = $this->client->getConnection()->flags([$sequence_id], $this->sequence === IMAP::ST_UID);
+        } catch (Exceptions\RuntimeException $e) {
+            throw new MessageFlagException("flag could not be fetched", 0, $e);
+        }
+
+        if (isset($flags[$sequence_id])) {
+            $this->parseRawFlags($flags[$sequence_id]);
+        }
+    }
+
+    /**
+     * Parse the Message body
+     *
+     * @return $this
+     * @throws Exceptions\ConnectionFailedException
+     * @throws Exceptions\MessageContentFetchingException
+     * @throws InvalidMessageDateException
+     * @throws Exceptions\EventNotFoundException
+     * @throws MessageFlagException
+     * @throws Exceptions\RuntimeException
+     */
+    public function parseBody() {
+        $this->client->openFolder($this->folder_path);
+
+        $sequence_id = $this->getSequenceId();
+        try {
+            $contents = $this->client->getConnection()->content([$sequence_id], "RFC822", $this->sequence === IMAP::ST_UID);
+        } catch (Exceptions\RuntimeException $e) {
+            throw new MessageContentFetchingException("failed to fetch content", 0);
+        }
+        if (!isset($contents[$sequence_id])) {
+            throw new MessageContentFetchingException("no content found", 0);
+        }
+        $content = $contents[$sequence_id];
+
+        $body = $this->parseRawBody($content);
+        $this->peek();
+
+        return $body;
+    }
+
+    /**
+     * Handle auto "Seen" flag handling
+     *
+     * @throws Exceptions\ConnectionFailedException
+     * @throws Exceptions\EventNotFoundException
+     * @throws MessageFlagException
+     * @throws Exceptions\RuntimeException
+     */
+    public function peek(){
+        if ($this->fetch_options == IMAP::FT_PEEK) {
+            if ($this->getFlags()->get("seen") == null) {
+                $this->unsetFlag("Seen");
+            }
+        }elseif ($this->getFlags()->get("seen") != null) {
+            $this->setFlag("Seen");
+        }
+    }
+
+    /**
+     * Parse a given message body
+     * @param string $raw_body
+     *
+     * @return $this
+     * @throws Exceptions\ConnectionFailedException
+     * @throws InvalidMessageDateException
+     * @throws MessageContentFetchingException
+     * @throws Exceptions\RuntimeException
+     */
+    public function parseRawBody($raw_body) {
+        $this->structure = new Structure($raw_body, $this->header);
+        $this->fetchStructure($this->structure);
+
+        return $this;
+    }
+
+    /**
+     * Fetch the Message structure
+     * @param Structure $structure
+     *
+     * @throws Exceptions\ConnectionFailedException
+     * @throws Exceptions\RuntimeException
+     */
+    private function fetchStructure($structure) {
+        $this->client->openFolder($this->folder_path);
+
+        foreach ($structure->parts as $part) {
+            $this->fetchPart($part);
+        }
+    }
+
+    /**
+     * Fetch a given part
+     * @param Part $part
+     */
+    private function fetchPart(Part $part) {
+        if ($part->isAttachment()) {
+            $this->fetchAttachment($part);
+        }else{
+            $encoding = $this->getEncoding($part);
+
+            $content = $this->decodeString($part->content, $part->encoding);
+
+            // We don't need to do convertEncoding() if charset is ASCII (us-ascii):
+            //     ASCII is a subset of UTF-8, so all ASCII files are already UTF-8 encoded
+            //     https://stackoverflow.com/a/11303410
+            //
+            // us-ascii is the same as ASCII:
+            //     ASCII is the traditional name for the encoding system; the Internet Assigned Numbers Authority (IANA)
+            //     prefers the updated name US-ASCII, which clarifies that this system was developed in the US and
+            //     based on the typographical symbols predominantly in use there.
+            //     https://en.wikipedia.org/wiki/ASCII
+            //
+            // convertEncoding() function basically means convertToUtf8(), so when we convert ASCII string into UTF-8 it gets broken.
+            if ($encoding != 'us-ascii') {
+                $content = $this->convertEncoding($content, $encoding);
+            }
+
+            $subtype = strtolower($part->subtype);
+            $subtype = $subtype == "plain" || $subtype == "" ? "text" : $subtype;
+
+            if (isset($this->bodies[$subtype])) {
+                $this->bodies[$subtype] .= "\n".$content;
+            }else{
+                $this->bodies[$subtype] = $content;
+            }
+        }
+    }
+
+    /**
+     * Fetch the Message attachment
+     * @param Part $part
+     */
+    protected function fetchAttachment($part) {
+        $oAttachment = new Attachment($this, $part);
+
+        if ($oAttachment->getName() !== null && $oAttachment->getSize() > 0) {
+            if ($oAttachment->getId() !== null) {
+                $this->attachments->put($oAttachment->getId(), $oAttachment);
+            } else {
+                $this->attachments->push($oAttachment);
+            }
+        }
+    }
+
+    /**
+     * Fail proof setter for $fetch_option
+     * @param $option
+     *
+     * @return $this
+     */
+    public function setFetchOption($option) {
+        if (is_long($option) === true) {
+            $this->fetch_options = $option;
+        } elseif (is_null($option) === true) {
+            $config = ClientManager::get('options.fetch', IMAP::FT_UID);
+            $this->fetch_options = is_long($config) ? $config : 1;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Set the sequence type
+     * @param int $sequence
+     *
+     * @return $this
+     */
+    public function setSequence($sequence) {
+        if (is_long($sequence)) {
+            $this->sequence = $sequence;
+        } elseif (is_null($sequence)) {
+            $config = ClientManager::get('options.sequence', IMAP::ST_MSGN);
+            $this->sequence = is_long($config) ? $config : IMAP::ST_MSGN;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Fail proof setter for $fetch_body
+     * @param $option
+     *
+     * @return $this
+     */
+    public function setFetchBodyOption($option) {
+        if (is_bool($option)) {
+            $this->fetch_body = $option;
+        } elseif (is_null($option)) {
+            $config = ClientManager::get('options.fetch_body', true);
+            $this->fetch_body = is_bool($config) ? $config : true;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Fail proof setter for $fetch_flags
+     * @param $option
+     *
+     * @return $this
+     */
+    public function setFetchFlagsOption($option) {
+        if (is_bool($option)) {
+            $this->fetch_flags = $option;
+        } elseif (is_null($option)) {
+            $config = ClientManager::get('options.fetch_flags', true);
+            $this->fetch_flags = is_bool($config) ? $config : true;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Decode a given string
+     * @param $string
+     * @param $encoding
+     *
+     * @return string
+     */
+    public function decodeString($string, $encoding) {
+        switch ($encoding) {
+            case IMAP::MESSAGE_ENC_BINARY:
+                if (extension_loaded('imap')) {
+                    return base64_decode(\imap_binary($string));
+                }
+                return base64_decode($string);
+            case IMAP::MESSAGE_ENC_BASE64:
+                return base64_decode($string);
+            case IMAP::MESSAGE_ENC_QUOTED_PRINTABLE:
+                return quoted_printable_decode($string);
+            case IMAP::MESSAGE_ENC_8BIT:
+            case IMAP::MESSAGE_ENC_7BIT:
+            case IMAP::MESSAGE_ENC_OTHER:
+            default:
+                return $string;
+        }
+    }
+
+    /**
+     * Convert the encoding
+     * @param $str
+     * @param string $from
+     * @param string $to
+     *
+     * @return mixed|string
+     */
+    public function convertEncoding($str, $from = "ISO-8859-2", $to = "UTF-8") {
+
+        $from = EncodingAliases::get($from);
+        $to = EncodingAliases::get($to);
+
+        if ($from === $to) {
+            return $str;
+        }
+
+        // We don't need to do convertEncoding() if charset is ASCII (us-ascii):
+        //     ASCII is a subset of UTF-8, so all ASCII files are already UTF-8 encoded
+        //     https://stackoverflow.com/a/11303410
+        //
+        // us-ascii is the same as ASCII:
+        //     ASCII is the traditional name for the encoding system; the Internet Assigned Numbers Authority (IANA)
+        //     prefers the updated name US-ASCII, which clarifies that this system was developed in the US and
+        //     based on the typographical symbols predominantly in use there.
+        //     https://en.wikipedia.org/wiki/ASCII
+        //
+        // convertEncoding() function basically means convertToUtf8(), so when we convert ASCII string into UTF-8 it gets broken.
+        if (strtolower($from) == 'us-ascii' && $to == 'UTF-8') {
+            return $str;
+        }
+
+        if (function_exists('iconv') && $from != 'UTF-7' && $to != 'UTF-7') {
+            return @iconv($from, $to.'//IGNORE', $str);
+        } else {
+            if (!$from) {
+                return mb_convert_encoding($str, $to);
+            }
+            return mb_convert_encoding($str, $to, $from);
+        }
+    }
+
+    /**
+     * Get the encoding of a given abject
+     * @param object|string $structure
+     *
+     * @return string
+     */
+    public function getEncoding($structure) {
+        if (property_exists($structure, 'parameters')) {
+            foreach ($structure->parameters as $parameter) {
+                if (strtolower($parameter->attribute) == "charset") {
+                    return EncodingAliases::get($parameter->value);
+                }
+            }
+        }elseif (property_exists($structure, 'charset')){
+            return EncodingAliases::get($structure->charset);
+        }elseif (is_string($structure) === true){
+            return mb_detect_encoding($structure);
+        }
+
+        return 'UTF-8';
+    }
+
+    /**
+     * Get the messages folder
+     *
+     * @return mixed
+     * @throws Exceptions\ConnectionFailedException
+     * @throws Exceptions\FolderFetchingException
+     * @throws Exceptions\RuntimeException
+     */
+    public function getFolder(){
+        return $this->client->getFolderByPath($this->folder_path);
+    }
+
+    /**
+     * Create a message thread based on the current message
+     * @param Folder|null $sent_folder
+     * @param MessageCollection|null $thread
+     * @param Folder|null $folder
+     *
+     * @return MessageCollection|null
+     * @throws Exceptions\ConnectionFailedException
+     * @throws Exceptions\FolderFetchingException
+     * @throws Exceptions\GetMessagesFailedException
+     * @throws Exceptions\RuntimeException
+     */
+    public function thread($sent_folder = null, &$thread = null, $folder = null){
+        $thread = $thread ? $thread : MessageCollection::make([]);
+        $folder = $folder ? $folder :  $this->getFolder();
+        $sent_folder = $sent_folder ? $sent_folder : $this->client->getFolderByPath(ClientManager::get("options.common_folders.sent", "INBOX/Sent"));
+
+        /** @var Message $message */
+        foreach($thread as $message) {
+            if ($message->message_id->first() == $this->message_id->first()) {
+                return $thread;
+            }
+        }
+        $thread->push($this);
+
+        $this->fetchThreadByInReplyTo($thread, $this->message_id, $folder, $folder, $sent_folder);
+        $this->fetchThreadByInReplyTo($thread, $this->message_id, $sent_folder, $folder, $sent_folder);
+
+        if (is_array($this->in_reply_to)) {
+            foreach($this->in_reply_to as $in_reply_to) {
+                $this->fetchThreadByMessageId($thread, $in_reply_to, $folder, $folder, $sent_folder);
+                $this->fetchThreadByMessageId($thread, $in_reply_to, $sent_folder, $folder, $sent_folder);
+            }
+        }
+
+        return $thread;
+    }
+
+    /**
+     * Fetch a partial thread by message id
+     * @param MessageCollection $thread
+     * @param string $in_reply_to
+     * @param Folder $primary_folder
+     * @param Folder $secondary_folder
+     * @param Folder $sent_folder
+     *
+     * @throws Exceptions\ConnectionFailedException
+     * @throws Exceptions\GetMessagesFailedException
+     * @throws Exceptions\RuntimeException
+     */
+    protected function fetchThreadByInReplyTo(&$thread, $in_reply_to, $primary_folder, $secondary_folder, $sent_folder){
+        $primary_folder->query()->inReplyTo($in_reply_to)
+        ->setFetchBody($this->getFetchBodyOption())
+        ->leaveUnread()->get()->each(function($message) use(&$thread, $secondary_folder, $sent_folder){
+            /** @var Message $message */
+            $message->thread($sent_folder, $thread, $secondary_folder);
+        });
+    }
+
+    /**
+     * Fetch a partial thread by message id
+     * @param MessageCollection $thread
+     * @param string $message_id
+     * @param Folder $primary_folder
+     * @param Folder $secondary_folder
+     * @param Folder $sent_folder
+     *
+     * @throws Exceptions\ConnectionFailedException
+     * @throws Exceptions\GetMessagesFailedException
+     * @throws Exceptions\RuntimeException
+     */
+    protected function fetchThreadByMessageId(&$thread, $message_id, $primary_folder, $secondary_folder, $sent_folder){
+        $primary_folder->query()->messageId($message_id)
+        ->setFetchBody($this->getFetchBodyOption())
+        ->leaveUnread()->get()->each(function($message) use(&$thread, $secondary_folder, $sent_folder){
+            /** @var Message $message */
+            $message->thread($sent_folder, $thread, $secondary_folder);
+        });
+    }
+
+    /**
+     * Copy the current Messages to a mailbox
+     * @param string $folder_path
+     * @param boolean $expunge
+     *
+     * @return null|Message
+     * @throws Exceptions\ConnectionFailedException
+     * @throws Exceptions\FolderFetchingException
+     * @throws Exceptions\RuntimeException
+     * @throws InvalidMessageDateException
+     * @throws MessageContentFetchingException
+     * @throws MessageHeaderFetchingException
+     * @throws Exceptions\EventNotFoundException
+     * @throws MessageFlagException
+     * @throws Exceptions\MessageNotFoundException
+     */
+    public function copy($folder_path, $expunge = false) {
+        $this->client->openFolder($folder_path);
+        $status = $this->client->getConnection()->examineFolder($folder_path);
+
+        if (isset($status["uidnext"])) {
+            $next_uid = $status["uidnext"];
+
+            /** @var Folder $folder */
+            $folder = $this->client->getFolderByPath($folder_path);
+
+            $this->client->openFolder($this->folder_path);
+            if ($this->client->getConnection()->copyMessage($folder->path, $this->getSequenceId(), null, $this->sequence === IMAP::ST_UID) == true) {
+                return $this->fetchNewMail($folder, $next_uid, "copied", $expunge);
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Move the current Messages to a mailbox
+     * @param string $folder_path
+     * @param boolean $expunge
+     *
+     * @return Message|null
+     * @throws Exceptions\ConnectionFailedException
+     * @throws Exceptions\FolderFetchingException
+     * @throws Exceptions\RuntimeException
+     * @throws InvalidMessageDateException
+     * @throws MessageContentFetchingException
+     * @throws MessageHeaderFetchingException
+     * @throws Exceptions\EventNotFoundException
+     * @throws MessageFlagException
+     * @throws Exceptions\MessageNotFoundException
+     */
+    public function move($folder_path, $expunge = false) {
+        $this->client->openFolder($folder_path);
+        $status = $this->client->getConnection()->examineFolder($folder_path);
+
+        if (isset($status["uidnext"])) {
+            $next_uid = $status["uidnext"];
+
+            /** @var Folder $folder */
+            $folder = $this->client->getFolderByPath($folder_path);
+
+            $this->client->openFolder($this->folder_path);
+            if ($this->client->getConnection()->moveMessage($folder->path, $this->getSequenceId(), null, $this->sequence === IMAP::ST_UID) == true) {
+                return $this->fetchNewMail($folder, $next_uid, "moved", $expunge);
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Fetch a new message and fire a given event
+     * @param Folder $folder
+     * @param int $next_uid
+     * @param string $event
+     * @param boolean $expunge
+     *
+     * @return mixed
+     * @throws Exceptions\ConnectionFailedException
+     * @throws Exceptions\EventNotFoundException
+     * @throws Exceptions\MessageNotFoundException
+     * @throws Exceptions\RuntimeException
+     * @throws InvalidMessageDateException
+     * @throws MessageContentFetchingException
+     * @throws MessageFlagException
+     * @throws MessageHeaderFetchingException
+     */
+    protected function fetchNewMail($folder, $next_uid, $event, $expunge){
+        if($expunge) $this->client->expunge();
+
+        $this->client->openFolder($folder->path);
+
+        if ($this->sequence === IMAP::ST_UID) {
+            $sequence_id = $next_uid;
+        }else{
+            $sequence_id = $this->client->getConnection()->getMessageNumber($next_uid);
+        }
+
+        $message = $folder->query()->getMessage($sequence_id, null, $this->sequence);
+        $event = $this->getEvent("message", $event);
+        $event::dispatch($this, $message);
+
+        return $message;
+    }
+
+    /**
+     * Delete the current Message
+     * @param bool $expunge
+     *
+     * @return bool
+     * @throws Exceptions\ConnectionFailedException
+     * @throws Exceptions\EventNotFoundException
+     * @throws MessageFlagException
+     * @throws Exceptions\RuntimeException
+     */
+    public function delete($expunge = true) {
+        $status = $this->setFlag("Deleted");
+        if($expunge) $this->client->expunge();
+
+        $event = $this->getEvent("message", "deleted");
+        $event::dispatch($this);
+
+        return $status;
+    }
+
+    /**
+     * Restore a deleted Message
+     * @param boolean $expunge
+     *
+     * @return bool
+     * @throws Exceptions\ConnectionFailedException
+     * @throws Exceptions\EventNotFoundException
+     * @throws MessageFlagException
+     * @throws Exceptions\RuntimeException
+     */
+    public function restore($expunge = true) {
+        $status = $this->unsetFlag("Deleted");
+        if($expunge) $this->client->expunge();
+
+        $event = $this->getEvent("message", "restored");
+        $event::dispatch($this);
+
+        return $status;
+    }
+
+    /**
+     * Set a given flag
+     * @param string|array $flag
+     *
+     * @return bool
+     * @throws Exceptions\ConnectionFailedException
+     * @throws MessageFlagException
+     * @throws Exceptions\EventNotFoundException
+     * @throws Exceptions\RuntimeException
+     */
+    public function setFlag($flag) {
+        $this->client->openFolder($this->folder_path);
+        $flag = "\\".trim(is_array($flag) ? implode(" \\", $flag) : $flag);
+        $sequence_id = $this->getSequenceId();
+        try {
+            $status = $this->client->getConnection()->store([$flag], $sequence_id, $sequence_id, "+", true, $this->sequence === IMAP::ST_UID);
+        } catch (Exceptions\RuntimeException $e) {
+            throw new MessageFlagException("flag could not be set", 0, $e);
+        }
+        $this->parseFlags();
+
+        $event = $this->getEvent("flag", "new");
+        $event::dispatch($this, $flag);
+
+        return $status;
+    }
+
+    /**
+     * Unset a given flag
+     * @param string|array $flag
+     *
+     * @return bool
+     * @throws Exceptions\ConnectionFailedException
+     * @throws Exceptions\EventNotFoundException
+     * @throws MessageFlagException
+     * @throws Exceptions\RuntimeException
+     */
+    public function unsetFlag($flag) {
+        $this->client->openFolder($this->folder_path);
+
+        $flag = "\\".trim(is_array($flag) ? implode(" \\", $flag) : $flag);
+        $sequence_id = $this->getSequenceId();
+        try {
+            $status = $this->client->getConnection()->store([$flag], $sequence_id, $sequence_id, "-", true, $this->sequence === IMAP::ST_UID);
+        } catch (Exceptions\RuntimeException $e) {
+            throw new MessageFlagException("flag could not be removed", 0, $e);
+        }
+        $this->parseFlags();
+
+        $event = $this->getEvent("flag", "deleted");
+        $event::dispatch($this, $flag);
+
+        return $status;
+    }
+
+    /**
+     * Set a given flag
+     * @param string|array $flag
+     *
+     * @return bool
+     * @throws Exceptions\ConnectionFailedException
+     * @throws MessageFlagException
+     * @throws Exceptions\EventNotFoundException
+     * @throws Exceptions\RuntimeException
+     */
+    public function addFlag($flag) {
+        return $this->setFlag($flag);
+    }
+
+    /**
+     * Unset a given flag
+     * @param string|array $flag
+     *
+     * @return bool
+     * @throws Exceptions\ConnectionFailedException
+     * @throws Exceptions\EventNotFoundException
+     * @throws MessageFlagException
+     * @throws Exceptions\RuntimeException
+     */
+    public function removeFlag($flag) {
+        return $this->unsetFlag($flag);
+    }
+
+    /**
+     * Get all message attachments.
+     *
+     * @return AttachmentCollection
+     */
+    public function getAttachments() {
+        return $this->attachments;
+    }
+
+    /**
+     * Get all message attachments.
+     *
+     * @return AttachmentCollection
+     */
+    public function attachments(){
+        return $this->getAttachments();
+    }
+
+    /**
+     * Checks if there are any attachments present
+     *
+     * @return boolean
+     */
+    public function hasAttachments() {
+        return $this->attachments->isEmpty() === false;
+    }
+
+    /**
+     * Get the raw body
+     *
+     * @return string
+     * @throws Exceptions\ConnectionFailedException
+     * @throws Exceptions\RuntimeException
+     */
+    public function getRawBody() {
+        if ($this->raw_body === null) {
+            $this->client->openFolder($this->folder_path);
+
+            $this->raw_body = $this->structure->raw;
+        }
+
+        return $this->raw_body;
+    }
+
+    /**
+     * Get the message header
+     *
+     * @return Header
+     */
+    public function getHeader() {
+        return $this->header;
+    }
+
+    /**
+     * Get the current client
+     *
+     * @return Client
+     */
+    public function getClient() {
+        return $this->client;
+    }
+
+    /**
+     * Get the used fetch option
+     *
+     * @return integer
+     */
+    public function getFetchOptions() {
+        return $this->fetch_options;
+    }
+
+    /**
+     * Get the used fetch body option
+     *
+     * @return boolean
+     */
+    public function getFetchBodyOption() {
+        return $this->fetch_body;
+    }
+
+    /**
+     * Get the used fetch flags option
+     *
+     * @return boolean
+     */
+    public function getFetchFlagsOption() {
+        return $this->fetch_flags;
+    }
+
+    /**
+     * Get all available bodies
+     *
+     * @return array
+     */
+    public function getBodies() {
+        return $this->bodies;
+    }
+
+    /**
+     * Get all set flags
+     *
+     * @return FlagCollection
+     */
+    public function getFlags() {
+        return $this->flags;
+    }
+
+    /**
+     * Get all set flags
+     *
+     * @return FlagCollection
+     */
+    public function flags(){
+        return $this->getFlags();
+    }
+
+    /**
+     * Get the fetched structure
+     *
+     * @return Structure|null
+     */
+    public function getStructure(){
+        return $this->structure;
+    }
+
+    /**
+     * Check if a message matches an other by comparing basic attributes
+     *
+     * @param  null|Message $message
+     * @return boolean
+     */
+    public function is(Message $message = null) {
+        if (is_null($message)) {
+            return false;
+        }
+
+        return $this->uid == $message->uid
+            && $this->message_id->first() == $message->message_id->first()
+            && $this->subject->first() == $message->subject->first()
+            && $this->date->toDate()->eq($message->date);
+    }
+
+    /**
+     * Get all message attributes
+     *
+     * @return array
+     */
+    public function getAttributes(){
+        return array_merge($this->attributes, $this->header->getAttributes());
+    }
+
+    /**
+     * Set the message mask
+     * @param $mask
+     *
+     * @return $this
+     */
+    public function setMask($mask){
+        if(class_exists($mask)){
+            $this->mask = $mask;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get the used message mask
+     *
+     * @return string
+     */
+    public function getMask(){
+        return $this->mask;
+    }
+
+    /**
+     * Get a masked instance by providing a mask name
+     * @param string|null $mask
+     *
+     * @return mixed
+     * @throws MaskNotFoundException
+     */
+    public function mask($mask = null){
+        $mask = $mask !== null ? $mask : $this->mask;
+        if(class_exists($mask)){
+            return new $mask($this);
+        }
+
+        throw new MaskNotFoundException("Unknown mask provided: ".$mask);
+    }
+
+    /**
+     * Get the message path aka folder path
+     *
+     * @return string
+     */
+    public function getFolderPath(){
+        return $this->folder_path;
+    }
+
+    /**
+     * Set the message path aka folder path
+     * @param $folder_path
+     *
+     * @return $this
+     */
+    public function setFolderPath($folder_path){
+        $this->folder_path = $folder_path;
+
+        return $this;
+    }
+
+    /**
+     * Set the config
+     * @param $config
+     *
+     * @return $this
+     */
+    public function setConfig($config){
+        $this->config = $config;
+
+        return $this;
+    }
+
+    /**
+     * Set the available flags
+     * @param $available_flags
+     *
+     * @return $this
+     */
+    public function setAvailableFlags($available_flags){
+        $this->available_flags = $available_flags;
+
+        return $this;
+    }
+
+    /**
+     * Set the attachment collection
+     * @param $attachments
+     *
+     * @return $this
+     */
+    public function setAttachments($attachments){
+        $this->attachments = $attachments;
+
+        return $this;
+    }
+
+    /**
+     * Set the flag collection
+     * @param $flags
+     *
+     * @return $this
+     */
+    public function setFlags($flags){
+        $this->flags = $flags;
+
+        return $this;
+    }
+
+    /**
+     * Set the client
+     * @param $client
+     *
+     * @return $this
+     * @throws Exceptions\RuntimeException
+     * @throws Exceptions\ConnectionFailedException
+     */
+    public function setClient($client){
+        $this->client = $client;
+        $this->client->openFolder($this->folder_path);
+
+        return $this;
+    }
+
+    /**
+     * Set the message number
+     * @param int $uid
+     *
+     * @return $this
+     * @throws Exceptions\MessageNotFoundException
+     * @throws Exceptions\ConnectionFailedException
+     */
+    public function setUid($uid){
+        $this->uid = $uid;
+        $this->msgn = $this->client->getConnection()->getMessageNumber($this->uid);
+        $this->msglist = null;
+
+        return $this;
+    }
+
+    /**
+     * Set the message number
+     * @param $msgn
+     * @param int|null $msglist
+     *
+     * @return $this
+     * @throws Exceptions\MessageNotFoundException
+     * @throws Exceptions\ConnectionFailedException
+     */
+    public function setMsgn($msgn, $msglist = null){
+        $this->msgn = $msgn;
+        $this->msglist = $msglist;
+        $this->uid = $this->client->getConnection()->getUid($this->msgn);
+
+        return $this;
+    }
+
+    /**
+     * Get the current sequence type
+     *
+     * @return int
+     */
+    public function getSequence(){
+        return $this->sequence;
+    }
+
+    /**
+     * Set the sequence type
+     *
+     * @return int
+     */
+    public function getSequenceId(){
+        return $this->sequence === IMAP::ST_UID ? $this->uid : $this->msgn;
+    }
+
+    /**
+     * Set the sequence id
+     * @param $uid
+     * @param int|null $msglist
+     *
+     * @throws Exceptions\ConnectionFailedException
+     * @throws Exceptions\MessageNotFoundException
+     */
+    public function setSequenceId($uid, $msglist = null){
+        if ($this->getSequence() === IMAP::ST_UID) {
+            $this->setUid($uid);
+            $this->setMsglist($msglist);
+        }else{
+            $this->setMsgn($uid, $msglist);
+        }
+    }
+}

+ 312 - 0
htdocs/includes/webklex/php-imap/src/Part.php

@@ -0,0 +1,312 @@
+<?php
+/*
+* File: Part.php
+* Category: -
+* Author: M.Goldenbaum
+* Created: 17.09.20 20:38
+* Updated: -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP;
+
+
+use Webklex\PHPIMAP\Exceptions\InvalidMessageDateException;
+
+/**
+ * Class Part
+ *
+ * @package Webklex\PHPIMAP
+ */
+class Part {
+
+    /**
+     * Raw part
+     *
+     * @var string $raw
+     */
+    public $raw = "";
+
+    /**
+     * Part type
+     *
+     * @var int $type
+     */
+    public $type = IMAP::MESSAGE_TYPE_TEXT;
+
+    /**
+     * Part content
+     *
+     * @var string $content
+     */
+    public $content = "";
+
+    /**
+     * Part subtype
+     *
+     * @var string $subtype
+     */
+    public $subtype = null;
+
+    /**
+     * Part charset - if available
+     *
+     * @var string $charset
+     */
+    public $charset = "utf-8";
+
+    /**
+     * Part encoding method
+     *
+     * @var int $encoding
+     */
+    public $encoding = IMAP::MESSAGE_ENC_OTHER;
+
+    /**
+     * Alias to check if the part is an attachment
+     *
+     * @var boolean $ifdisposition
+     */
+    public $ifdisposition = false;
+
+    /**
+     * Indicates if the part is an attachment
+     *
+     * @var string $disposition
+     */
+    public $disposition = null;
+
+    /**
+     * Alias to check if the part has a description
+     *
+     * @var boolean $ifdescription
+     */
+    public $ifdescription = false;
+
+    /**
+     * Part description if available
+     *
+     * @var string $description
+     */
+    public $description = null;
+
+    /**
+     * Part filename if available
+     *
+     * @var string $filename
+     */
+    public $filename = null;
+
+    /**
+     * Part name if available
+     *
+     * @var string $name
+     */
+    public $name = null;
+
+    /**
+     * Part id if available
+     *
+     * @var string $id
+     */
+    public $id = null;
+
+    /**
+     * The part number of the current part
+     *
+     * @var integer $part_number
+     */
+    public $part_number = 0;
+
+    /**
+     * Part length in bytes
+     *
+     * @var integer $bytes
+     */
+    public $bytes = null;
+
+    /**
+     * Part content type
+     *
+     * @var string|null $content_type
+     */
+    public $content_type = null;
+
+    /**
+     * @var Header $header
+     */
+    private $header = null;
+
+    /**
+     * Part constructor.
+     * @param $raw_part
+     * @param Header $header
+     * @param integer $part_number
+     *
+     * @throws InvalidMessageDateException
+     */
+    public function __construct($raw_part, $header = null, $part_number = 0) {
+        $this->raw = $raw_part;
+        $this->header = $header;
+        $this->part_number = $part_number;
+        $this->parse();
+    }
+
+    /**
+     * Parse the raw parts
+     *
+     * @throws InvalidMessageDateException
+     */
+    protected function parse(){
+        if ($this->header === null) {
+            $body = $this->findHeaders();
+        }else{
+            $body = $this->raw;
+        }
+
+        $this->parseDisposition();
+        $this->parseDescription();
+        $this->parseEncoding();
+
+        $this->charset = $this->header->get("charset");
+        $this->name = $this->header->get("name");
+        $this->filename = $this->header->get("filename");
+
+        if(!empty($this->header->get("id"))) {
+            $this->id = $this->header->get("id");
+        } else if(!empty($this->header->get("x_attachment_id"))){
+            $this->id = $this->header->get("x_attachment_id");
+        } else if(!empty($this->header->get("content_id"))){
+            $this->id = strtr($this->header->get("content_id"), [
+                '<' => '',
+                '>' => ''
+            ]);
+        }
+
+        $content_types = $this->header->get("content_type");
+        if(!empty($content_types)){
+            $this->subtype = $this->parseSubtype($content_types);
+            $content_type = $content_types;
+            if (is_array($content_types)) {
+                $content_type = $content_types[0];
+            }
+            $parts = explode(';', $content_type);
+            $this->content_type = trim($parts[0]);
+        }
+
+
+        $this->content = trim(rtrim($body));
+        $this->bytes = strlen($this->content);
+    }
+
+    /**
+     * Find all available headers and return the left over body segment
+     *
+     * @return string
+     * @throws InvalidMessageDateException
+     */
+    private function findHeaders(){
+        $body = $this->raw;
+        while (($pos = strpos($body, "\r\n")) > 0) {
+            $body = substr($body, $pos + 2);
+        }
+        $headers = substr($this->raw, 0, strlen($body) * -1);
+        $body = substr($body, 0, -2);
+
+        $this->header = new Header($headers);
+
+        return (string) $body;
+    }
+
+    /**
+     * Try to parse the subtype if any is present
+     * @param $content_type
+     *
+     * @return string
+     */
+    private function parseSubtype($content_type){
+        if (is_array($content_type)) {
+            foreach ($content_type as $part){
+                if ((strpos($part, "/")) !== false){
+                    return $this->parseSubtype($part);
+                }
+            }
+            return null;
+        }
+        if (($pos = strpos($content_type, "/")) !== false){
+            return substr($content_type, $pos + 1);
+        }
+        return null;
+    }
+
+    /**
+     * Try to parse the disposition if any is present
+     */
+    private function parseDisposition(){
+        $content_disposition = $this->header->get("content_disposition");
+        if($content_disposition !== null) {
+            $this->ifdisposition = true;
+            $this->disposition = (is_array($content_disposition)) ? implode(' ', $content_disposition) : $content_disposition;
+        }
+    }
+
+    /**
+     * Try to parse the description if any is present
+     */
+    private function parseDescription(){
+        $content_description = $this->header->get("content_description");
+        if($content_description !== null) {
+            $this->ifdescription = true;
+            $this->description = $content_description;
+        }
+    }
+
+    /**
+     * Try to parse the encoding if any is present
+     */
+    private function parseEncoding(){
+        $encoding = $this->header->get("content_transfer_encoding");
+        if($encoding !== null) {
+            switch (strtolower($encoding)) {
+                case "quoted-printable":
+                    $this->encoding = IMAP::MESSAGE_ENC_QUOTED_PRINTABLE;
+                    break;
+                case "base64":
+                    $this->encoding = IMAP::MESSAGE_ENC_BASE64;
+                    break;
+                case "7bit":
+                    $this->encoding = IMAP::MESSAGE_ENC_7BIT;
+                    break;
+                case "8bit":
+                    $this->encoding = IMAP::MESSAGE_ENC_8BIT;
+                    break;
+                case "binary":
+                    $this->encoding = IMAP::MESSAGE_ENC_BINARY;
+                    break;
+                default:
+                    $this->encoding = IMAP::MESSAGE_ENC_OTHER;
+                    break;
+
+            }
+        }
+    }
+
+    /**
+     * Check if the current part represents an attachment
+     *
+     * @return bool
+     */
+    public function isAttachment(){
+        $valid_disposition = in_array(strtolower($this->disposition), ClientManager::get('options.dispositions'));
+
+        if ($this->type == IMAP::MESSAGE_TYPE_TEXT && ($this->ifdisposition == 0 || (empty($this->disposition))) && !$valid_disposition) {
+            if (($this->subtype == null || in_array((strtolower($this->subtype)), ["plain", "html"])) && $this->filename == null && $this->name == null) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+}

+ 842 - 0
htdocs/includes/webklex/php-imap/src/Query/Query.php

@@ -0,0 +1,842 @@
+<?php
+/*
+* File:     Query.php
+* Category: -
+* Author:   M. Goldenbaum
+* Created:  21.07.18 18:54
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Query;
+
+use Carbon\Carbon;
+use Exception;
+use Illuminate\Pagination\LengthAwarePaginator;
+use Illuminate\Support\Collection;
+use ReflectionException;
+use Webklex\PHPIMAP\Client;
+use Webklex\PHPIMAP\ClientManager;
+use Webklex\PHPIMAP\Exceptions\ConnectionFailedException;
+use Webklex\PHPIMAP\Exceptions\EventNotFoundException;
+use Webklex\PHPIMAP\Exceptions\GetMessagesFailedException;
+use Webklex\PHPIMAP\Exceptions\InvalidMessageDateException;
+use Webklex\PHPIMAP\Exceptions\MessageContentFetchingException;
+use Webklex\PHPIMAP\Exceptions\MessageFlagException;
+use Webklex\PHPIMAP\Exceptions\MessageHeaderFetchingException;
+use Webklex\PHPIMAP\Exceptions\MessageNotFoundException;
+use Webklex\PHPIMAP\Exceptions\MessageSearchValidationException;
+use Webklex\PHPIMAP\Exceptions\RuntimeException;
+use Webklex\PHPIMAP\IMAP;
+use Webklex\PHPIMAP\Message;
+use Webklex\PHPIMAP\Support\MessageCollection;
+
+/**
+ * Class Query
+ *
+ * @package Webklex\PHPIMAP\Query
+ */
+class Query {
+
+    /** @var Collection $query */
+    protected $query;
+
+    /** @var string $raw_query */
+    protected $raw_query;
+
+    /** @var string $charset */
+    protected $charset;
+
+    /** @var Client $client */
+    protected $client;
+
+    /** @var int $limit */
+    protected $limit = null;
+
+    /** @var int $page */
+    protected $page = 1;
+
+    /** @var int $fetch_options */
+    protected $fetch_options = null;
+
+    /** @var int $fetch_body */
+    protected $fetch_body = true;
+
+    /** @var int $fetch_flags */
+    protected $fetch_flags = true;
+
+    /** @var int $sequence */
+    protected $sequence = IMAP::NIL;
+
+    /** @var string $fetch_order */
+    protected $fetch_order;
+
+    /** @var string $date_format */
+    protected $date_format;
+
+    /** @var bool $soft_fail */
+    protected $soft_fail = false;
+
+    /** @var array $errors */
+    protected $errors = [];
+
+    /**
+     * Query constructor.
+     * @param Client $client
+     * @param string $charset
+     */
+    public function __construct(Client $client, $charset = 'UTF-8') {
+        $this->setClient($client);
+
+        $this->sequence = ClientManager::get('options.sequence', IMAP::ST_MSGN);
+        if (ClientManager::get('options.fetch') === IMAP::FT_PEEK) $this->leaveUnread();
+
+        if (ClientManager::get('options.fetch_order') === 'desc') {
+            $this->fetch_order = 'desc';
+        } else {
+            $this->fetch_order = 'asc';
+        }
+
+        $this->date_format = ClientManager::get('date_format', 'd M y');
+        $this->soft_fail = ClientManager::get('options.soft_fail', false);
+
+        $this->charset = $charset;
+        $this->query = new Collection();
+        $this->boot();
+    }
+
+    /**
+     * Instance boot method for additional functionality
+     */
+    protected function boot() {
+    }
+
+    /**
+     * Parse a given value
+     * @param mixed $value
+     *
+     * @return string
+     */
+    protected function parse_value($value) {
+        switch (true) {
+            case $value instanceof Carbon:
+                $value = $value->format($this->date_format);
+                break;
+        }
+
+        return (string)$value;
+    }
+
+    /**
+     * Check if a given date is a valid carbon object and if not try to convert it
+     * @param string|Carbon $date
+     *
+     * @return Carbon
+     * @throws MessageSearchValidationException
+     */
+    protected function parse_date($date) {
+        if ($date instanceof Carbon) return $date;
+
+        try {
+            $date = Carbon::parse($date);
+        } catch (Exception $e) {
+            throw new MessageSearchValidationException();
+        }
+
+        return $date;
+    }
+
+    /**
+     * Get the raw IMAP search query
+     *
+     * @return string
+     */
+    public function generate_query() {
+        $query = '';
+        $this->query->each(function($statement) use (&$query) {
+            if (count($statement) == 1) {
+                $query .= $statement[0];
+            } else {
+                if ($statement[1] === null) {
+                    $query .= $statement[0];
+                } else {
+                    $query .= $statement[0] . ' "' . $statement[1] . '"';
+                }
+            }
+            $query .= ' ';
+
+        });
+
+        $this->raw_query = trim($query);
+
+        return $this->raw_query;
+    }
+
+    /**
+     * Perform an imap search request
+     *
+     * @return Collection
+     * @throws GetMessagesFailedException
+     */
+    protected function search() {
+        $this->generate_query();
+
+        try {
+            $available_messages = $this->client->getConnection()->search([$this->getRawQuery()], $this->sequence == IMAP::ST_UID);
+            return $available_messages !== false ? new Collection($available_messages) : new Collection();
+        } catch (RuntimeException $e) {
+            throw new GetMessagesFailedException("failed to fetch messages", 0, $e);
+        } catch (ConnectionFailedException $e) {
+            throw new GetMessagesFailedException("failed to fetch messages", 0, $e);
+        }
+    }
+
+    /**
+     * Count all available messages matching the current search criteria
+     *
+     * @return int
+     * @throws GetMessagesFailedException
+     */
+    public function count() {
+        return $this->search()->count();
+    }
+
+    /**
+     * Fetch a given id collection
+     * @param Collection $available_messages
+     *
+     * @return array
+     * @throws ConnectionFailedException
+     * @throws RuntimeException
+     */
+    protected function fetch($available_messages) {
+        if ($this->fetch_order === 'desc') {
+            $available_messages = $available_messages->reverse();
+        }
+
+        $uids = $available_messages->forPage($this->page, $this->limit)->toArray();
+        $flags = $this->client->getConnection()->flags($uids, $this->sequence == IMAP::ST_UID);
+        $headers = $this->client->getConnection()->headers($uids, "RFC822", $this->sequence == IMAP::ST_UID);
+
+        $contents = [];
+        if ($this->getFetchBody()) {
+            $contents = $this->client->getConnection()->content($uids, "RFC822", $this->sequence == IMAP::ST_UID);
+        }
+
+        return [
+            "uids"     => $uids,
+            "flags"    => $flags,
+            "headers"  => $headers,
+            "contents" => $contents,
+        ];
+    }
+
+    /**
+     * Make a new message from given raw components
+     * @param integer $uid
+     * @param integer $msglist
+     * @param string $header
+     * @param string $content
+     * @param array $flags
+     *
+     * @return Message|null
+     * @throws ConnectionFailedException
+     * @throws EventNotFoundException
+     * @throws GetMessagesFailedException
+     * @throws ReflectionException
+     */
+    protected function make($uid, $msglist, $header, $content, $flags){
+        try {
+            return Message::make($uid, $msglist, $this->getClient(), $header, $content, $flags, $this->getFetchOptions(), $this->sequence);
+        }catch (MessageNotFoundException $e) {
+            $this->setError($uid, $e);
+        }catch (RuntimeException $e) {
+            $this->setError($uid, $e);
+        }catch (MessageFlagException $e) {
+            $this->setError($uid, $e);
+        }catch (InvalidMessageDateException $e) {
+            $this->setError($uid, $e);
+        }catch (MessageContentFetchingException $e) {
+            $this->setError($uid, $e);
+        }
+
+        $this->handleException($uid);
+
+        return null;
+    }
+
+    /**
+     * Get the message key for a given message
+     * @param string $message_key
+     * @param integer $msglist
+     * @param Message $message
+     *
+     * @return string
+     */
+    protected function getMessageKey($message_key, $msglist, $message){
+        switch ($message_key) {
+            case 'number':
+                $key = $message->getMessageNo();
+                break;
+            case 'list':
+                $key = $msglist;
+                break;
+            case 'uid':
+                $key = $message->getUid();
+                break;
+            default:
+                $key = $message->getMessageId();
+                break;
+        }
+        return (string)$key;
+    }
+
+    /**
+     * Populate a given id collection and receive a fully fetched message collection
+     * @param Collection $available_messages
+     *
+     * @return MessageCollection
+     * @throws ConnectionFailedException
+     * @throws EventNotFoundException
+     * @throws GetMessagesFailedException
+     * @throws ReflectionException
+     * @throws RuntimeException
+     */
+    protected function populate($available_messages) {
+        $messages = MessageCollection::make([]);
+
+        $messages->total($available_messages->count());
+
+        $message_key = ClientManager::get('options.message_key');
+
+        $raw_messages = $this->fetch($available_messages);
+
+        $msglist = 0;
+        foreach ($raw_messages["headers"] as $uid => $header) {
+            $content = isset($raw_messages["contents"][$uid]) ? $raw_messages["contents"][$uid] : "";
+            $flag = isset($raw_messages["flags"][$uid]) ? $raw_messages["flags"][$uid] : [];
+
+            $message = $this->make($uid, $msglist, $header, $content, $flag);
+            if ($message !== null) {
+                $key = $this->getMessageKey($message_key, $msglist, $message);
+                $messages->put("$key", $message);
+            }
+            $msglist++;
+        }
+
+        return $messages;
+    }
+
+    /**
+     * Fetch the current query and return all found messages
+     *
+     * @return MessageCollection
+     * @throws GetMessagesFailedException
+     */
+    public function get() {
+        $available_messages = $this->search();
+
+        try {
+            if ($available_messages->count() > 0) {
+                return $this->populate($available_messages);
+            }
+            return MessageCollection::make([]);
+        } catch (Exception $e) {
+            throw new GetMessagesFailedException($e->getMessage(), 0, $e);
+        }
+    }
+
+    /**
+     * Fetch the current query as chunked requests
+     * @param callable $callback
+     * @param int $chunk_size
+     * @param int $start_chunk
+     *
+     * @throws ConnectionFailedException
+     * @throws EventNotFoundException
+     * @throws GetMessagesFailedException
+     * @throws ReflectionException
+     * @throws RuntimeException
+     */
+    public function chunked($callback, $chunk_size = 10, $start_chunk = 1) {
+        $available_messages = $this->search();
+        if (($available_messages_count = $available_messages->count()) > 0) {
+            $old_limit = $this->limit;
+            $old_page = $this->page;
+
+            $this->limit = $chunk_size;
+            $this->page = $start_chunk;
+            do {
+                $messages = $this->populate($available_messages);
+                $callback($messages, $this->page);
+                $this->page++;
+            } while ($this->limit * $this->page <= $available_messages_count);
+            $this->limit = $old_limit;
+            $this->page = $old_page;
+        }
+    }
+
+    /**
+     * Paginate the current query
+     * @param int $per_page Results you which to receive per page
+     * @param int|null $page The current page you are on (e.g. 0, 1, 2, ...) use `null` to enable auto mode
+     * @param string $page_name The page name / uri parameter used for the generated links and the auto mode
+     *
+     * @return LengthAwarePaginator
+     * @throws GetMessagesFailedException
+     */
+    public function paginate($per_page = 5, $page = null, $page_name = 'imap_page') {
+        if (
+            $page === null
+            && isset($_GET[$page_name])
+            && $_GET[$page_name] > 0
+        ) {
+            $this->page = intval($_GET[$page_name]);
+        } elseif ($page > 0) {
+            $this->page = $page;
+        }
+
+        $this->limit = $per_page;
+
+        return $this->get()->paginate($per_page, $this->page, $page_name, true);
+    }
+
+    /**
+     * Get a new Message instance
+     * @param int $uid
+     * @param int|null $msglist
+     * @param int|null $sequence
+     *
+     * @return Message
+     * @throws ConnectionFailedException
+     * @throws RuntimeException
+     * @throws InvalidMessageDateException
+     * @throws MessageContentFetchingException
+     * @throws MessageHeaderFetchingException
+     * @throws EventNotFoundException
+     * @throws MessageFlagException
+     * @throws MessageNotFoundException
+     */
+    public function getMessage($uid, $msglist = null, $sequence = null) {
+        return new Message($uid, $msglist, $this->getClient(), $this->getFetchOptions(), $this->getFetchBody(), $this->getFetchFlags(), $sequence ? $sequence : $this->sequence);
+    }
+
+    /**
+     * Get a message by its message number
+     * @param $msgn
+     * @param int|null $msglist
+     *
+     * @return Message
+     * @throws ConnectionFailedException
+     * @throws InvalidMessageDateException
+     * @throws MessageContentFetchingException
+     * @throws MessageHeaderFetchingException
+     * @throws RuntimeException
+     * @throws EventNotFoundException
+     * @throws MessageFlagException
+     * @throws MessageNotFoundException
+     */
+    public function getMessageByMsgn($msgn, $msglist = null) {
+        return $this->getMessage($msgn, $msglist, IMAP::ST_MSGN);
+    }
+
+    /**
+     * Get a message by its uid
+     * @param $uid
+     *
+     * @return Message
+     * @throws ConnectionFailedException
+     * @throws InvalidMessageDateException
+     * @throws MessageContentFetchingException
+     * @throws MessageHeaderFetchingException
+     * @throws RuntimeException
+     * @throws EventNotFoundException
+     * @throws MessageFlagException
+     * @throws MessageNotFoundException
+     */
+    public function getMessageByUid($uid) {
+        return $this->getMessage($uid, null, IMAP::ST_UID);
+    }
+
+    /**
+     * Don't mark messages as read when fetching
+     *
+     * @return $this
+     */
+    public function leaveUnread() {
+        $this->setFetchOptions(IMAP::FT_PEEK);
+
+        return $this;
+    }
+
+    /**
+     * Mark all messages as read when fetching
+     *
+     * @return $this
+     */
+    public function markAsRead() {
+        $this->setFetchOptions(IMAP::FT_UID);
+
+        return $this;
+    }
+
+    /**
+     * Set the sequence type
+     * @param int $sequence
+     *
+     * @return $this
+     */
+    public function setSequence($sequence) {
+        $this->sequence = $sequence != IMAP::ST_MSGN ? IMAP::ST_UID : $sequence;
+
+        return $this;
+    }
+
+    /**
+     * @return Client
+     * @throws ConnectionFailedException
+     */
+    public function getClient() {
+        $this->client->checkConnection();
+        return $this->client;
+    }
+
+    /**
+     * Set the limit and page for the current query
+     * @param int $limit
+     * @param int $page
+     *
+     * @return $this
+     */
+    public function limit($limit, $page = 1) {
+        if ($page >= 1) $this->page = $page;
+        $this->limit = $limit;
+
+        return $this;
+    }
+
+    /**
+     * @return Collection
+     */
+    public function getQuery() {
+        return $this->query;
+    }
+
+    /**
+     * @param array $query
+     * @return Query
+     */
+    public function setQuery($query) {
+        $this->query = new Collection($query);
+        return $this;
+    }
+
+    /**
+     * @return string
+     */
+    public function getRawQuery() {
+        return $this->raw_query;
+    }
+
+    /**
+     * @param string $raw_query
+     * @return Query
+     */
+    public function setRawQuery($raw_query) {
+        $this->raw_query = $raw_query;
+        return $this;
+    }
+
+    /**
+     * @return string
+     */
+    public function getCharset() {
+        return $this->charset;
+    }
+
+    /**
+     * @param string $charset
+     * @return Query
+     */
+    public function setCharset($charset) {
+        $this->charset = $charset;
+        return $this;
+    }
+
+    /**
+     * @param Client $client
+     * @return Query
+     */
+    public function setClient(Client $client) {
+        $this->client = $client;
+        return $this;
+    }
+
+    /**
+     * @return int
+     */
+    public function getLimit() {
+        return $this->limit;
+    }
+
+    /**
+     * @param int $limit
+     * @return Query
+     */
+    public function setLimit($limit) {
+        $this->limit = $limit <= 0 ? null : $limit;
+        return $this;
+    }
+
+    /**
+     * @return int
+     */
+    public function getPage() {
+        return $this->page;
+    }
+
+    /**
+     * @param int $page
+     * @return Query
+     */
+    public function setPage($page) {
+        $this->page = $page;
+        return $this;
+    }
+
+    /**
+     * @param boolean $fetch_options
+     * @return Query
+     */
+    public function setFetchOptions($fetch_options) {
+        $this->fetch_options = $fetch_options;
+        return $this;
+    }
+
+    /**
+     * @param boolean $fetch_options
+     * @return Query
+     */
+    public function fetchOptions($fetch_options) {
+        return $this->setFetchOptions($fetch_options);
+    }
+
+    /**
+     * @return int
+     */
+    public function getFetchOptions() {
+        return $this->fetch_options;
+    }
+
+    /**
+     * @return boolean
+     */
+    public function getFetchBody() {
+        return $this->fetch_body;
+    }
+
+    /**
+     * @param boolean $fetch_body
+     * @return Query
+     */
+    public function setFetchBody($fetch_body) {
+        $this->fetch_body = $fetch_body;
+        return $this;
+    }
+
+    /**
+     * @param boolean $fetch_body
+     * @return Query
+     */
+    public function fetchBody($fetch_body) {
+        return $this->setFetchBody($fetch_body);
+    }
+
+    /**
+     * @return int
+     */
+    public function getFetchFlags() {
+        return $this->fetch_flags;
+    }
+
+    /**
+     * @param int $fetch_flags
+     * @return Query
+     */
+    public function setFetchFlags($fetch_flags) {
+        $this->fetch_flags = $fetch_flags;
+        return $this;
+    }
+
+    /**
+     * @param string $fetch_order
+     * @return Query
+     */
+    public function setFetchOrder($fetch_order) {
+        $fetch_order = strtolower($fetch_order);
+
+        if (in_array($fetch_order, ['asc', 'desc'])) {
+            $this->fetch_order = $fetch_order;
+        }
+
+        return $this;
+    }
+
+    /**
+     * @param string $fetch_order
+     * @return Query
+     */
+    public function fetchOrder($fetch_order) {
+        return $this->setFetchOrder($fetch_order);
+    }
+
+    /**
+     * @return string
+     */
+    public function getFetchOrder() {
+        return $this->fetch_order;
+    }
+
+    /**
+     * @return Query
+     */
+    public function setFetchOrderAsc() {
+        return $this->setFetchOrder('asc');
+    }
+
+    /**
+     * @return Query
+     */
+    public function fetchOrderAsc() {
+        return $this->setFetchOrderAsc();
+    }
+
+    /**
+     * @return Query
+     */
+    public function setFetchOrderDesc() {
+        return $this->setFetchOrder('desc');
+    }
+
+    /**
+     * @return Query
+     */
+    public function fetchOrderDesc() {
+        return $this->setFetchOrderDesc();
+    }
+
+    /**
+     * @var boolean $state
+     *
+     * @return Query
+     */
+    public function softFail($state = true) {
+        return $this->setSoftFail($state);
+    }
+
+    /**
+     * @var boolean $state
+     *
+     * @return Query
+     */
+    public function setSoftFail($state = true) {
+        $this->soft_fail = $state;
+
+        return $this;
+    }
+
+    /**
+     * @return boolean
+     */
+    public function getSoftFail() {
+        return $this->soft_fail;
+    }
+
+    /**
+     * Handle the exception for a given uid
+     * @param integer $uid
+     *
+     * @throws GetMessagesFailedException
+     */
+    protected function handleException($uid) {
+        if ($this->soft_fail === false && $this->hasError($uid)) {
+            $error = $this->getError($uid);
+            throw new GetMessagesFailedException($error->getMessage(), 0, $error);
+        }
+    }
+
+    /**
+     * Add a new error to the error holder
+     * @param integer $uid
+     * @param Exception $error
+     */
+    protected function setError($uid, $error) {
+        $this->errors[$uid] = $error;
+    }
+
+    /**
+     * Check if there are any errors / exceptions present
+     * @var integer|null $uid
+     *
+     * @return boolean
+     */
+    public function hasErrors($uid = null){
+        if ($uid !== null) {
+            return $this->hasError($uid);
+        }
+        return count($this->errors) > 0;
+    }
+
+    /**
+     * Check if there is an error / exception present
+     * @var integer $uid
+     *
+     * @return boolean
+     */
+    public function hasError($uid){
+        return isset($this->errors[$uid]);
+    }
+
+    /**
+     * Get all available errors / exceptions
+     *
+     * @return array
+     */
+    public function errors(){
+        return $this->getErrors();
+    }
+
+    /**
+     * Get all available errors / exceptions
+     *
+     * @return array
+     */
+    public function getErrors(){
+        return $this->errors;
+    }
+
+    /**
+     * Get a specific error / exception
+     * @var integer $uid
+     *
+     * @return Exception|null
+     */
+    public function error($uid){
+        return $this->getError($uid);
+    }
+
+    /**
+     * Get a specific error / exception
+     * @var integer $uid
+     *
+     * @return Exception|null
+     */
+    public function getError($uid){
+        if ($this->hasError($uid)) {
+            return $this->errors[$uid];
+        }
+        return null;
+    }
+}

+ 551 - 0
htdocs/includes/webklex/php-imap/src/Query/WhereQuery.php

@@ -0,0 +1,551 @@
+<?php
+/*
+* File:     Query.php
+* Category: -
+* Author:   M. Goldenbaum
+* Created:  21.07.18 18:54
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Query;
+
+use Closure;
+use Illuminate\Support\Str;
+use Webklex\PHPIMAP\Exceptions\InvalidWhereQueryCriteriaException;
+use Webklex\PHPIMAP\Exceptions\MethodNotFoundException;
+use Webklex\PHPIMAP\Exceptions\MessageSearchValidationException;
+
+/**
+ * Class WhereQuery
+ *
+ * @package Webklex\PHPIMAP\Query
+ *
+ * @method WhereQuery all()
+ * @method WhereQuery answered()
+ * @method WhereQuery deleted()
+ * @method WhereQuery new()
+ * @method WhereQuery old()
+ * @method WhereQuery recent()
+ * @method WhereQuery seen()
+ * @method WhereQuery unanswered()
+ * @method WhereQuery undeleted()
+ * @method WhereQuery unflagged()
+ * @method WhereQuery unseen()
+ * @method WhereQuery not()
+ * @method WhereQuery unkeyword($value)
+ * @method WhereQuery to($value)
+ * @method WhereQuery text($value)
+ * @method WhereQuery subject($value)
+ * @method WhereQuery since($date)
+ * @method WhereQuery on($date)
+ * @method WhereQuery keyword($value)
+ * @method WhereQuery from($value)
+ * @method WhereQuery flagged()
+ * @method WhereQuery cc($value)
+ * @method WhereQuery body($value)
+ * @method WhereQuery before($date)
+ * @method WhereQuery bcc($value)
+ * @method WhereQuery inReplyTo($value)
+ * @method WhereQuery messageId($value)
+ *
+ * @mixin Query
+ */
+class WhereQuery extends Query {
+
+    /**
+     * @var array $available_criteria
+     */
+    protected $available_criteria = [
+        'OR', 'AND',
+        'ALL', 'ANSWERED', 'BCC', 'BEFORE', 'BODY', 'CC', 'DELETED', 'FLAGGED', 'FROM', 'KEYWORD',
+        'NEW', 'NOT', 'OLD', 'ON', 'RECENT', 'SEEN', 'SINCE', 'SUBJECT', 'TEXT', 'TO',
+        'UNANSWERED', 'UNDELETED', 'UNFLAGGED', 'UNKEYWORD', 'UNSEEN', 'UID'
+    ];
+
+    /**
+     * Magic method in order to allow alias usage of all "where" methods in an optional connection with "NOT"
+     * @param string $name
+     * @param array|null $arguments
+     *
+     * @return mixed
+     * @throws InvalidWhereQueryCriteriaException
+     * @throws MethodNotFoundException
+     */
+    public function __call($name, $arguments) {
+        $that = $this;
+
+        $name = Str::camel($name);
+
+        if (strtolower(substr($name, 0, 3)) === 'not') {
+            $that = $that->whereNot();
+            $name = substr($name, 3);
+        }
+
+        if (strpos(strtolower($name), "where") === false) {
+            $method = 'where' . ucfirst($name);
+        } else {
+            $method = lcfirst($name);
+        }
+
+        if (method_exists($this, $method) === true) {
+            return call_user_func_array([$that, $method], $arguments);
+        }
+
+        throw new MethodNotFoundException("Method " . self::class . '::' . $method . '() is not supported');
+    }
+
+    /**
+     * Validate a given criteria
+     * @param $criteria
+     *
+     * @return string
+     * @throws InvalidWhereQueryCriteriaException
+     */
+    protected function validate_criteria($criteria) {
+        $criteria = strtoupper($criteria);
+        if (substr($criteria, 0, 7) === "CUSTOM ") {
+            return substr($criteria, 7);
+        }
+        if (in_array($criteria, $this->available_criteria) === false) {
+            throw new InvalidWhereQueryCriteriaException();
+        }
+
+        return $criteria;
+    }
+
+
+    /**
+     * Register search parameters
+     * @param mixed $criteria
+     * @param null $value
+     *
+     * @return $this
+     * @throws InvalidWhereQueryCriteriaException
+     *
+     * Examples:
+     * $query->from("someone@email.tld")->seen();
+     * $query->whereFrom("someone@email.tld")->whereSeen();
+     * $query->where([["FROM" => "someone@email.tld"], ["SEEN"]]);
+     * $query->where(["FROM" => "someone@email.tld"])->where(["SEEN"]);
+     * $query->where(["FROM" => "someone@email.tld", "SEEN"]);
+     * $query->where("FROM", "someone@email.tld")->where("SEEN");
+     */
+    public function where($criteria, $value = null): WhereQuery {
+        if (is_array($criteria)) {
+            foreach ($criteria as $key => $value) {
+                if (is_numeric($key)) {
+                    $this->where($value);
+                }else{
+                    $this->where($key, $value);
+                }
+            }
+        } else {
+            $this->push_search_criteria($criteria, $value);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Push a given search criteria and value pair to the search query
+     * @param $criteria string
+     * @param $value mixed
+     *
+     * @throws InvalidWhereQueryCriteriaException
+     */
+    protected function push_search_criteria(string $criteria, $value){
+        $criteria = $this->validate_criteria($criteria);
+        $value = $this->parse_value($value);
+
+        if ($value === null || $value === '') {
+            $this->query->push([$criteria]);
+        } else {
+            $this->query->push([$criteria, $value]);
+        }
+    }
+
+    /**
+     * @param Closure $closure
+     *
+     * @return $this
+     */
+    public function orWhere(Closure $closure = null) {
+        $this->query->push(['OR']);
+        if ($closure !== null) $closure($this);
+
+        return $this;
+    }
+
+    /**
+     * @param Closure $closure
+     *
+     * @return $this
+     */
+    public function andWhere(Closure $closure = null) {
+        $this->query->push(['AND']);
+        if ($closure !== null) $closure($this);
+
+        return $this;
+    }
+
+    /**
+     * @return WhereQuery
+     * @throws InvalidWhereQueryCriteriaException
+     */
+    public function whereAll() {
+        return $this->where('ALL');
+    }
+
+    /**
+     * @return WhereQuery
+     * @throws InvalidWhereQueryCriteriaException
+     */
+    public function whereAnswered() {
+        return $this->where('ANSWERED');
+    }
+
+    /**
+     * @param string $value
+     *
+     * @return WhereQuery
+     * @throws InvalidWhereQueryCriteriaException
+     */
+    public function whereBcc($value) {
+        return $this->where('BCC', $value);
+    }
+
+    /**
+     * @param mixed $value
+     * @return WhereQuery
+     * @throws InvalidWhereQueryCriteriaException
+     * @throws MessageSearchValidationException
+     */
+    public function whereBefore($value) {
+        $date = $this->parse_date($value);
+        return $this->where('BEFORE', $date);
+    }
+
+    /**
+     * @param string $value
+     *
+     * @return WhereQuery
+     * @throws InvalidWhereQueryCriteriaException
+     */
+    public function whereBody($value) {
+        return $this->where('BODY', $value);
+    }
+
+    /**
+     * @param string $value
+     *
+     * @return WhereQuery
+     * @throws InvalidWhereQueryCriteriaException
+     */
+    public function whereCc($value) {
+        return $this->where('CC', $value);
+    }
+
+    /**
+     * @return WhereQuery
+     * @throws InvalidWhereQueryCriteriaException
+     */
+    public function whereDeleted() {
+        return $this->where('DELETED');
+    }
+
+    /**
+     * @param string $value
+     *
+     * @return WhereQuery
+     * @throws InvalidWhereQueryCriteriaException
+     */
+    public function whereFlagged($value) {
+        return $this->where('FLAGGED', $value);
+    }
+
+    /**
+     * @param string $value
+     *
+     * @return WhereQuery
+     * @throws InvalidWhereQueryCriteriaException
+     */
+    public function whereFrom($value) {
+        return $this->where('FROM', $value);
+    }
+
+    /**
+     * @param string $value
+     *
+     * @return WhereQuery
+     * @throws InvalidWhereQueryCriteriaException
+     */
+    public function whereKeyword($value) {
+        return $this->where('KEYWORD', $value);
+    }
+
+    /**
+     * @return WhereQuery
+     * @throws InvalidWhereQueryCriteriaException
+     */
+    public function whereNew() {
+        return $this->where('NEW');
+    }
+
+    /**
+     * @return WhereQuery
+     * @throws InvalidWhereQueryCriteriaException
+     */
+    public function whereNot() {
+        return $this->where('NOT');
+    }
+
+    /**
+     * @return WhereQuery
+     * @throws InvalidWhereQueryCriteriaException
+     */
+    public function whereOld() {
+        return $this->where('OLD');
+    }
+
+    /**
+     * @param mixed $value
+     *
+     * @return WhereQuery
+     * @throws MessageSearchValidationException
+     * @throws InvalidWhereQueryCriteriaException
+     */
+    public function whereOn($value) {
+        $date = $this->parse_date($value);
+        return $this->where('ON', $date);
+    }
+
+    /**
+     * @return WhereQuery
+     * @throws InvalidWhereQueryCriteriaException
+     */
+    public function whereRecent() {
+        return $this->where('RECENT');
+    }
+
+    /**
+     * @return WhereQuery
+     * @throws InvalidWhereQueryCriteriaException
+     */
+    public function whereSeen() {
+        return $this->where('SEEN');
+    }
+
+    /**
+     * @param mixed $value
+     *
+     * @return WhereQuery
+     * @throws MessageSearchValidationException
+     * @throws InvalidWhereQueryCriteriaException
+     */
+    public function whereSince($value) {
+        $date = $this->parse_date($value);
+        return $this->where('SINCE', $date);
+    }
+
+    /**
+     * @param string $value
+     *
+     * @return WhereQuery
+     * @throws InvalidWhereQueryCriteriaException
+     */
+    public function whereSubject($value) {
+        return $this->where('SUBJECT', $value);
+    }
+
+    /**
+     * @param string $value
+     *
+     * @return WhereQuery
+     * @throws InvalidWhereQueryCriteriaException
+     */
+    public function whereText($value) {
+        return $this->where('TEXT', $value);
+    }
+
+    /**
+     * @param string $value
+     *
+     * @return WhereQuery
+     * @throws InvalidWhereQueryCriteriaException
+     */
+    public function whereTo($value) {
+        return $this->where('TO', $value);
+    }
+
+    /**
+     * @param string $value
+     *
+     * @return WhereQuery
+     * @throws InvalidWhereQueryCriteriaException
+     */
+    public function whereUnkeyword($value) {
+        return $this->where('UNKEYWORD', $value);
+    }
+
+    /**
+     * @return WhereQuery
+     * @throws InvalidWhereQueryCriteriaException
+     */
+    public function whereUnanswered() {
+        return $this->where('UNANSWERED');
+    }
+
+    /**
+     * @return WhereQuery
+     * @throws InvalidWhereQueryCriteriaException
+     */
+    public function whereUndeleted() {
+        return $this->where('UNDELETED');
+    }
+
+    /**
+     * @return WhereQuery
+     * @throws InvalidWhereQueryCriteriaException
+     */
+    public function whereUnflagged() {
+        return $this->where('UNFLAGGED');
+    }
+
+    /**
+     * @return WhereQuery
+     * @throws InvalidWhereQueryCriteriaException
+     */
+    public function whereUnseen() {
+        return $this->where('UNSEEN');
+    }
+
+    /**
+     * @return WhereQuery
+     * @throws InvalidWhereQueryCriteriaException
+     */
+    public function whereNoXSpam() {
+        return $this->where("CUSTOM X-Spam-Flag NO");
+    }
+
+    /**
+     * @return WhereQuery
+     * @throws InvalidWhereQueryCriteriaException
+     */
+    public function whereIsXSpam() {
+        return $this->where("CUSTOM X-Spam-Flag YES");
+    }
+
+    /**
+     * Search for a specific header value
+     * @param $header
+     * @param $value
+     *
+     * @return WhereQuery
+     * @throws InvalidWhereQueryCriteriaException
+     */
+    public function whereHeader($header, $value) {
+        return $this->where("CUSTOM HEADER $header $value");
+    }
+
+    /**
+     * Search for a specific message id
+     * @param $messageId
+     *
+     * @return WhereQuery
+     * @throws InvalidWhereQueryCriteriaException
+     */
+    public function whereMessageId($messageId) {
+        return $this->whereHeader("Message-ID", $messageId);
+    }
+
+    /**
+     * Search for a specific message id
+     * @param $messageId
+     *
+     * @return WhereQuery
+     * @throws InvalidWhereQueryCriteriaException
+     */
+    public function whereInReplyTo($messageId) {
+        return $this->whereHeader("In-Reply-To", $messageId);
+    }
+
+    /**
+     * @param $country_code
+     *
+     * @return WhereQuery
+     * @throws InvalidWhereQueryCriteriaException
+     */
+    public function whereLanguage($country_code) {
+        return $this->where("Content-Language $country_code");
+    }
+
+    /**
+     * Get message be it UID.
+     *
+     * @param int|string $uid
+     *
+     * @return WhereQuery
+     * @throws InvalidWhereQueryCriteriaException
+     */
+    public function whereUid($uid)
+    {
+        return $this->where('UID', $uid);
+    }
+
+    /**
+     * Get messages by their UIDs.
+     *
+     * @param array<int, int> $uids
+     *
+     * @return WhereQuery
+     * @throws InvalidWhereQueryCriteriaException
+     */
+    public function whereUidIn($uids)
+    {
+        $uids = implode(',', $uids);
+        return $this->where('UID', $uids);
+    }
+
+    /**
+     * Apply the callback if the given "value" is truthy.
+     * copied from @url https://github.com/laravel/framework/blob/8.x/src/Illuminate/Support/Traits/Conditionable.php
+     *
+     * @param mixed  $value
+     * @param callable  $callback
+     * @param callable|null  $default
+
+     * @return $this|mixed
+     */
+    public function when($value, $callback, $default = null) {
+        if ($value) {
+            return $callback($this, $value) ?: $this;
+        } elseif ($default) {
+            return $default($this, $value) ?: $this;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Apply the callback if the given "value" is falsy.
+     * copied from @url https://github.com/laravel/framework/blob/8.x/src/Illuminate/Support/Traits/Conditionable.php
+     *
+     * @param mixed  $value
+     * @param callable  $callback
+     * @param callable|null  $default
+
+     * @return $this|mixed
+     */
+    public function unless($value, $callback, $default = null) {
+        if (! $value) {
+            return $callback($this, $value) ?: $this;
+        } elseif ($default) {
+            return $default($this, $value) ?: $this;
+        }
+
+        return $this;
+    }
+}

+ 174 - 0
htdocs/includes/webklex/php-imap/src/Structure.php

@@ -0,0 +1,174 @@
+<?php
+/*
+* File: Structure.php
+* Category: -
+* Author: M.Goldenbaum
+* Created: 17.09.20 20:38
+* Updated: -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP;
+
+
+use Webklex\PHPIMAP\Exceptions\InvalidMessageDateException;
+use Webklex\PHPIMAP\Exceptions\MessageContentFetchingException;
+
+/**
+ * Class Structure
+ *
+ * @package Webklex\PHPIMAP
+ */
+class Structure {
+
+    /**
+     * Raw structure
+     *
+     * @var string $raw
+     */
+    public $raw = "";
+
+    /**
+     * @var Header $header
+     */
+    private $header = null;
+
+    /**
+     * Message type (if multipart or not)
+     *
+     * @var int $type
+     */
+    public $type = IMAP::MESSAGE_TYPE_TEXT;
+
+    /**
+     * All available parts
+     *
+     * @var Part[] $parts
+     */
+    public $parts = [];
+
+    /**
+     * Config holder
+     *
+     * @var array $config
+     */
+    protected $config = [];
+
+    /**
+     * Structure constructor.
+     * @param $raw_structure
+     * @param Header $header
+     *
+     * @throws MessageContentFetchingException
+     * @throws InvalidMessageDateException
+     */
+    public function __construct($raw_structure, Header $header) {
+        $this->raw = $raw_structure;
+        $this->header = $header;
+        $this->config = ClientManager::get('options');
+        $this->parse();
+    }
+
+    /**
+     * Parse the given raw structure
+     *
+     * @throws MessageContentFetchingException
+     * @throws InvalidMessageDateException
+     */
+    protected function parse(){
+        $this->findContentType();
+        $this->parts = $this->find_parts();
+    }
+
+    /**
+     * Determine the message content type
+     */
+    public function findContentType(){
+        $content_type = $this->header->get("content_type");
+        $content_type = (is_array($content_type)) ? implode(' ', $content_type) : $content_type;
+        if(stripos($content_type, 'multipart') === 0) {
+            $this->type = IMAP::MESSAGE_TYPE_MULTIPART;
+        }else{
+            $this->type = IMAP::MESSAGE_TYPE_TEXT;
+        }
+    }
+
+    /**
+     * Find all available headers and return the left over body segment
+     * @var string $context
+     * @var integer $part_number
+     *
+     * @return Part[]
+     * @throws InvalidMessageDateException
+     */
+    private function parsePart($context, $part_number = 0){
+        $body = $context;
+        while (($pos = strpos($body, "\r\n")) > 0) {
+            $body = substr($body, $pos + 2);
+        }
+        $headers = substr($context, 0, strlen($body) * -1);
+        $body = substr($body, 0, -2);
+
+        $headers = new Header($headers);
+        if (($boundary = $headers->getBoundary()) !== null) {
+            return $this->detectParts($boundary, $body, $part_number);
+        }
+        return [new Part($body, $headers, $part_number)];
+    }
+
+    /**
+     * @param string $boundary
+     * @param string $context
+     * @param int $part_number
+     *
+     * @return array
+     * @throws InvalidMessageDateException
+     */
+    private function detectParts($boundary, $context, $part_number = 0){
+        $base_parts = explode( $boundary, $context);
+        $final_parts = [];
+        foreach($base_parts as $ctx) {
+            $ctx = substr($ctx, 2);
+            if ($ctx !== "--" && $ctx != "") {
+                $parts = $this->parsePart($ctx, $part_number);
+                foreach ($parts as $part) {
+                    $final_parts[] = $part;
+                    $part_number = $part->part_number;
+                }
+                $part_number++;
+            }
+        }
+        return $final_parts;
+    }
+
+    /**
+     * Find all available parts
+     *
+     * @return array
+     * @throws MessageContentFetchingException
+     * @throws InvalidMessageDateException
+     */
+    public function find_parts(){
+        if($this->type === IMAP::MESSAGE_TYPE_MULTIPART) {
+            if (($boundary = $this->header->getBoundary()) === null)  {
+                throw new MessageContentFetchingException("no content found", 0);
+            }
+
+            return $this->detectParts($boundary, $this->raw);
+        }
+
+        return [new Part($this->raw, $this->header)];
+    }
+
+    /**
+     * Try to find a boundary if possible
+     *
+     * @return string|null
+     * @Depricated since version 2.4.4
+     */
+    public function getBoundary(){
+        return $this->header->getBoundary();
+    }
+}

+ 22 - 0
htdocs/includes/webklex/php-imap/src/Support/AttachmentCollection.php

@@ -0,0 +1,22 @@
+<?php
+/*
+* File:     AttachmentCollection.php
+* Category: Collection
+* Author:   M. Goldenbaum
+* Created:  16.03.18 03:13
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Support;
+
+/**
+ * Class AttachmentCollection
+ *
+ * @package Webklex\PHPIMAP\Support
+ */
+class AttachmentCollection extends PaginatedCollection {
+
+}

+ 22 - 0
htdocs/includes/webklex/php-imap/src/Support/FlagCollection.php

@@ -0,0 +1,22 @@
+<?php
+/*
+* File:     FlagCollection.php
+* Category: Collection
+* Author:   M. Goldenbaum
+* Created:  21.07.18 23:10
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Support;
+
+/**
+ * Class FlagCollection
+ *
+ * @package Webklex\PHPIMAP\Support
+ */
+class FlagCollection extends PaginatedCollection {
+
+}

+ 22 - 0
htdocs/includes/webklex/php-imap/src/Support/FolderCollection.php

@@ -0,0 +1,22 @@
+<?php
+/*
+* File:     FolderCollection.php
+* Category: Collection
+* Author:   M. Goldenbaum
+* Created:  18.03.18 02:21
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Support;
+
+/**
+ * Class FolderCollection
+ *
+ * @package Webklex\PHPIMAP\Support
+ */
+class FolderCollection extends PaginatedCollection {
+
+}

+ 44 - 0
htdocs/includes/webklex/php-imap/src/Support/Masks/AttachmentMask.php

@@ -0,0 +1,44 @@
+<?php
+/*
+* File: AttachmentMask.php
+* Category: Mask
+* Author: M.Goldenbaum
+* Created: 14.03.19 20:49
+* Updated: -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Support\Masks;
+
+use Webklex\PHPIMAP\Attachment;
+
+/**
+ * Class AttachmentMask
+ *
+ * @package Webklex\PHPIMAP\Support\Masks
+ */
+class AttachmentMask extends Mask {
+
+    /** @var Attachment $parent */
+    protected $parent;
+
+    /**
+     * Get the attachment content base64 encoded
+     *
+     * @return string|null
+     */
+    public function getContentBase64Encoded() {
+        return base64_encode($this->parent->content);
+    }
+
+    /**
+     * Get an base64 image src string
+     *
+     * @return string|null
+     */
+    public function getImageSrc() {
+        return 'data:'.$this->parent->content_type.';base64,'.$this->getContentBase64Encoded();
+    }
+}

+ 137 - 0
htdocs/includes/webklex/php-imap/src/Support/Masks/Mask.php

@@ -0,0 +1,137 @@
+<?php
+/*
+* File: Mask.php
+* Category: Mask
+* Author: M.Goldenbaum
+* Created: 14.03.19 20:49
+* Updated: -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Support\Masks;
+
+use Illuminate\Support\Str;
+use Webklex\PHPIMAP\Exceptions\MethodNotFoundException;
+
+/**
+ * Class Mask
+ *
+ * @package Webklex\PHPIMAP\Support\Masks
+ */
+class Mask {
+
+    /**
+     * Available attributes
+     *
+     * @var array $attributes
+     */
+    protected $attributes = [];
+
+    /**
+     * Parent instance
+     *
+     * @var object $parent
+     */
+    protected $parent;
+
+    /**
+     * Mask constructor.
+     * @param $parent
+     */
+    public function __construct($parent) {
+        $this->parent = $parent;
+
+        if(method_exists($this->parent, 'getAttributes')){
+            $this->attributes = array_merge($this->attributes, $this->parent->getAttributes());
+        }
+
+        $this->boot();
+    }
+
+    /**
+     * Boot method made to be used by any custom mask
+     */
+    protected function boot(){}
+
+    /**
+     * Call dynamic attribute setter and getter methods and inherit the parent calls
+     * @param string $method
+     * @param array $arguments
+     *
+     * @return mixed
+     * @throws MethodNotFoundException
+     */
+    public function __call($method, $arguments) {
+        if(strtolower(substr($method, 0, 3)) === 'get') {
+            $name = Str::snake(substr($method, 3));
+
+            if(isset($this->attributes[$name])) {
+                return $this->attributes[$name];
+            }
+
+        }elseif (strtolower(substr($method, 0, 3)) === 'set') {
+            $name = Str::snake(substr($method, 3));
+
+            if(isset($this->attributes[$name])) {
+                $this->attributes[$name] = array_pop($arguments);
+
+                return $this->attributes[$name];
+            }
+
+        }
+
+        if(method_exists($this->parent, $method) === true){
+            return call_user_func_array([$this->parent, $method], $arguments);
+        }
+
+        throw new MethodNotFoundException("Method ".self::class.'::'.$method.'() is not supported');
+    }
+
+    /**
+     * Magic setter
+     * @param $name
+     * @param $value
+     *
+     * @return mixed
+     */
+    public function __set($name, $value) {
+        $this->attributes[$name] = $value;
+
+        return $this->attributes[$name];
+    }
+
+    /**
+     * Magic getter
+     * @param $name
+     *
+     * @return mixed|null
+     */
+    public function __get($name) {
+        if(isset($this->attributes[$name])) {
+            return $this->attributes[$name];
+        }
+
+        return null;
+    }
+
+    /**
+     * Get the parent instance
+     *
+     * @return mixed
+     */
+    public function getParent(){
+        return $this->parent;
+    }
+
+    /**
+     * Get all available attributes
+     *
+     * @return array
+     */
+    public function getAttributes(){
+        return $this->attributes;
+    }
+
+}

+ 86 - 0
htdocs/includes/webklex/php-imap/src/Support/Masks/MessageMask.php

@@ -0,0 +1,86 @@
+<?php
+/*
+* File: MessageMask.php
+* Category: Mask
+* Author: M.Goldenbaum
+* Created: 14.03.19 20:49
+* Updated: -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Support\Masks;
+
+use Webklex\PHPIMAP\Attachment;
+use Webklex\PHPIMAP\Message;
+
+/**
+ * Class MessageMask
+ *
+ * @package Webklex\PHPIMAP\Support\Masks
+ */
+class MessageMask extends Mask {
+
+    /** @var Message $parent */
+    protected $parent;
+
+    /**
+     * Get the message html body
+     *
+     * @return null
+     */
+    public function getHtmlBody(){
+        $bodies = $this->parent->getBodies();
+        if (!isset($bodies['html'])) {
+            return null;
+        }
+
+        if(is_object($bodies['html']) && property_exists($bodies['html'], 'content')) {
+            return $bodies['html']->content;
+        }
+        return $bodies['html'];
+    }
+
+    /**
+     * Get the Message html body filtered by an optional callback
+     * @param callable|bool $callback
+     *
+     * @return string|null
+     */
+    public function getCustomHTMLBody($callback = false) {
+        $body = $this->getHtmlBody();
+        if($body === null) return null;
+
+        if ($callback !== false) {
+            $aAttachment = $this->parent->getAttachments();
+            $aAttachment->each(function($oAttachment) use(&$body, $callback) {
+                /** @var Attachment $oAttachment */
+                if(is_callable($callback)) {
+                    $body = $callback($body, $oAttachment);
+                }elseif(is_string($callback)) {
+                    call_user_func($callback, [$body, $oAttachment]);
+                }
+            });
+        }
+
+        return $body;
+    }
+
+    /**
+     * Get the Message html body with embedded base64 images
+     * the resulting $body.
+     *
+     * @return string|null
+     */
+    public function getHTMLBodyWithEmbeddedBase64Images() {
+        return $this->getCustomHTMLBody(function($body, $oAttachment){
+            /** @var Attachment $oAttachment */
+            if ($oAttachment->id) {
+                $body = str_replace('cid:'.$oAttachment->id, 'data:'.$oAttachment->getContentType().';base64, '.base64_encode($oAttachment->getContent()), $body);
+            }
+
+            return $body;
+        });
+    }
+}

+ 22 - 0
htdocs/includes/webklex/php-imap/src/Support/MessageCollection.php

@@ -0,0 +1,22 @@
+<?php
+/*
+* File:     MessageCollection.php
+* Category: Collection
+* Author:   M. Goldenbaum
+* Created:  16.03.18 03:13
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Support;
+
+/**
+ * Class MessageCollection
+ *
+ * @package Webklex\PHPIMAP\Support
+ */
+class MessageCollection extends PaginatedCollection {
+
+}

+ 82 - 0
htdocs/includes/webklex/php-imap/src/Support/PaginatedCollection.php

@@ -0,0 +1,82 @@
+<?php
+/*
+* File:     PaginatedCollection.php
+* Category: Collection
+* Author:   M. Goldenbaum
+* Created:  16.03.18 03:13
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Support;
+
+use Illuminate\Pagination\LengthAwarePaginator;
+use Illuminate\Support\Collection;
+use Illuminate\Pagination\Paginator;
+
+/**
+ * Class PaginatedCollection
+ *
+ * @package Webklex\PHPIMAP\Support
+ */
+class PaginatedCollection extends Collection {
+
+    /**
+     * Number of total entries
+     *
+     * @var int $total
+     */
+    protected $total;
+
+    /**
+     * Paginate the current collection.
+     * @param int      $per_page
+     * @param int|null $page
+     * @param string   $page_name
+     * @param boolean  $prepaginated
+     *
+     * @return LengthAwarePaginator
+     */
+    public function paginate($per_page = 15, $page = null, $page_name = 'page', $prepaginated = false) {
+        $page = $page ?: Paginator::resolveCurrentPage($page_name);
+
+        $total = $this->total ? $this->total : $this->count();
+
+        $results = !$prepaginated && $total ? $this->forPage($page, $per_page) : $this->all();
+
+        return $this->paginator($results, $total, $per_page, $page, [
+            'path'      => Paginator::resolveCurrentPath(),
+            'pageName'  => $page_name,
+        ]);
+    }
+
+    /**
+     * Create a new length-aware paginator instance.
+     * @param  array    $items
+     * @param  int      $total
+     * @param  int      $per_page
+     * @param  int|null $current_page
+     * @param  array    $options
+     *
+     * @return LengthAwarePaginator
+     */
+    protected function paginator($items, $total, $per_page, $current_page, array $options) {
+        return new LengthAwarePaginator($items, $total, $per_page, $current_page, $options);
+    }
+
+    /**
+     * Get and set the total amount
+     * @param null $total
+     *
+     * @return int|null
+     */
+    public function total($total = null) {
+        if($total === null) {
+            return $this->total;
+        }
+
+        return $this->total = $total;
+    }
+}

+ 77 - 0
htdocs/includes/webklex/php-imap/src/Traits/HasEvents.php

@@ -0,0 +1,77 @@
+<?php
+/*
+* File: HasEvents.php
+* Category: -
+* Author: M.Goldenbaum
+* Created: 21.09.20 22:46
+* Updated: -
+*
+* Description:
+*  -
+*/
+
+namespace Webklex\PHPIMAP\Traits;
+
+
+use Webklex\PHPIMAP\Events\Event;
+use Webklex\PHPIMAP\Exceptions\EventNotFoundException;
+
+/**
+ * Trait HasEvents
+ *
+ * @package Webklex\PHPIMAP\Traits
+ */
+trait HasEvents {
+
+    /**
+     * Event holder
+     *
+     * @var array $events
+     */
+    protected $events = [];
+
+    /**
+     * Set a specific event
+     * @param $section
+     * @param $event
+     * @param $class
+     */
+    public function setEvent($section, $event, $class) {
+        if (isset($this->events[$section])) {
+            $this->events[$section][$event] = $class;
+        }
+    }
+
+    /**
+     * Set all events
+     * @param $events
+     */
+    public function setEvents($events) {
+        $this->events = $events;
+    }
+
+    /**
+     * Get a specific event callback
+     * @param $section
+     * @param $event
+     *
+     * @return Event
+     * @throws EventNotFoundException
+     */
+    public function getEvent($section, $event) {
+        if (isset($this->events[$section])) {
+            return $this->events[$section][$event];
+        }
+        throw new EventNotFoundException();
+    }
+
+    /**
+     * Get all events
+     *
+     * @return array
+     */
+    public function getEvents(){
+        return $this->events;
+    }
+
+}

+ 216 - 0
htdocs/includes/webklex/php-imap/src/config/imap.php

@@ -0,0 +1,216 @@
+<?php
+/*
+* File:     imap.php
+* Category: config
+* Author:   M. Goldenbaum
+* Created:  24.09.16 22:36
+* Updated:  -
+*
+* Description:
+*  -
+*/
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Default date format
+    |--------------------------------------------------------------------------
+    |
+    | The default date format is used to convert any given Carbon::class object into a valid date string.
+    | These are currently known working formats: "d-M-Y", "d-M-y", "d M y"
+    |
+    */
+    'date_format' => 'd-M-Y',
+
+    /*
+    |--------------------------------------------------------------------------
+    | Default account
+    |--------------------------------------------------------------------------
+    |
+    | The default account identifier. It will be used as default for any missing account parameters.
+    | If however the default account is missing a parameter the package default will be used.
+    | Set to 'false' [boolean] to disable this functionality.
+    |
+    */
+    'default' => 'default',
+
+    /*
+    |--------------------------------------------------------------------------
+    | Available accounts
+    |--------------------------------------------------------------------------
+    |
+    | Please list all IMAP accounts which you are planning to use within the
+    | array below.
+    |
+    */
+    'accounts' => [
+
+        'default' => [// account identifier
+            'host'  => 'localhost',
+            'port'  => 993,
+            'protocol'  => 'imap', //might also use imap, [pop3 or nntp (untested)]
+            'encryption'    => 'ssl', // Supported: false, 'ssl', 'tls'
+            'validate_cert' => true,
+            'username' => 'root@example.com',
+            'password' => '',
+            'authentication' => null,
+            'proxy' => [
+                'socket' => null,
+                'request_fulluri' => false,
+                'username' => null,
+                'password' => null,
+            ],
+            "timeout" => 30
+        ],
+
+        /*
+        'gmail' => [ // account identifier
+            'host' => 'imap.gmail.com',
+            'port' => 993,
+            'encryption' => 'ssl',
+            'validate_cert' => true,
+            'username' => 'example@gmail.com',
+            'password' => 'PASSWORD',
+            'authentication' => 'oauth',
+        ],
+
+        'another' => [ // account identifier
+            'host' => '',
+            'port' => 993,
+            'encryption' => false,
+            'validate_cert' => true,
+            'username' => '',
+            'password' => '',
+            'authentication' => null,
+        ]
+        */
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Available IMAP options
+    |--------------------------------------------------------------------------
+    |
+    | Available php imap config parameters are listed below
+    |   -Delimiter (optional):
+    |       This option is only used when calling $oClient->
+    |       You can use any supported char such as ".", "/", (...)
+    |   -Fetch option:
+    |       IMAP::FT_UID  - Message marked as read by fetching the body message
+    |       IMAP::FT_PEEK - Fetch the message without setting the "seen" flag
+    |   -Fetch sequence id:
+    |       IMAP::ST_UID  - Fetch message components using the message uid
+    |       IMAP::ST_MSGN - Fetch message components using the message number
+    |   -Body download option
+    |       Default TRUE
+    |   -Flag download option
+    |       Default TRUE
+    |   -Soft fail
+    |       Default FALSE - Set to TRUE if you want to ignore certain exception while fetching bulk messages
+    |   -RFC822
+    |       Default TRUE - Set to FALSE to prevent the usage of \imap_rfc822_parse_headers().
+    |                      See https://github.com/Webklex/php-imap/issues/115 for more information.
+    |   -Message key identifier option
+    |       You can choose between the following:
+    |       'id'     - Use the MessageID as array key (default, might cause hickups with yahoo mail)
+    |       'number' - Use the message number as array key (isn't always unique and can cause some interesting behavior)
+    |       'list'   - Use the message list number as array key (incrementing integer (does not always start at 0 or 1)
+    |       'uid'    - Use the message uid as array key (isn't always unique and can cause some interesting behavior)
+    |   -Fetch order
+    |       'asc'  - Order all messages ascending (probably results in oldest first)
+    |       'desc' - Order all messages descending (probably results in newest first)
+    |   -Disposition types potentially considered an attachment
+    |       Default ['attachment', 'inline']
+    |   -Common folders
+    |       Default folder locations and paths assumed if none is provided
+    |   -Open IMAP options:
+    |       DISABLE_AUTHENTICATOR - Disable authentication properties.
+    |                               Use 'GSSAPI' if you encounter the following
+    |                               error: "Kerberos error: No credentials cache
+    |                               file found (try running kinit) (...)"
+    |                               or ['GSSAPI','PLAIN'] if you are using outlook mail
+    |   -Decoder options (currently only the message subject and attachment name decoder can be set)
+    |       'utf-8' - Uses imap_utf8($string) to decode a string
+    |       'mimeheader' - Uses mb_decode_mimeheader($string) to decode a string
+    |
+    */
+    'options' => [
+        'delimiter' => '/',
+        'fetch' => \Webklex\PHPIMAP\IMAP::FT_PEEK,
+        'sequence' => \Webklex\PHPIMAP\IMAP::ST_MSGN,
+        'fetch_body' => true,
+        'fetch_flags' => true,
+        'soft_fail' => false,
+        'rfc822' => true,
+        'message_key' => 'list',
+        'fetch_order' => 'asc',
+        'dispositions' => ['attachment', 'inline'],
+        'common_folders' => [
+            "root" => "INBOX",
+            "junk" => "INBOX/Junk",
+            "draft" => "INBOX/Drafts",
+            "sent" => "INBOX/Sent",
+            "trash" => "INBOX/Trash",
+        ],
+        'decoder' => [
+            'message' => 'utf-8', // mimeheader
+            'attachment' => 'utf-8' // mimeheader
+        ],
+        'open' => [
+            // 'DISABLE_AUTHENTICATOR' => 'GSSAPI'
+        ]
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Available flags
+    |--------------------------------------------------------------------------
+    |
+    | List all available / supported flags. Set to null to accept all given flags.
+     */
+    'flags' => ['recent', 'flagged', 'answered', 'deleted', 'seen', 'draft'],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Available events
+    |--------------------------------------------------------------------------
+    |
+     */
+    'events' => [
+        "message" => [
+            'new' => \Webklex\PHPIMAP\Events\MessageNewEvent::class,
+            'moved' => \Webklex\PHPIMAP\Events\MessageMovedEvent::class,
+            'copied' => \Webklex\PHPIMAP\Events\MessageCopiedEvent::class,
+            'deleted' => \Webklex\PHPIMAP\Events\MessageDeletedEvent::class,
+            'restored' => \Webklex\PHPIMAP\Events\MessageRestoredEvent::class,
+        ],
+        "folder" => [
+            'new' => \Webklex\PHPIMAP\Events\FolderNewEvent::class,
+            'moved' => \Webklex\PHPIMAP\Events\FolderMovedEvent::class,
+            'deleted' => \Webklex\PHPIMAP\Events\FolderDeletedEvent::class,
+        ],
+        "flag" => [
+            'new' => \Webklex\PHPIMAP\Events\FlagNewEvent::class,
+            'deleted' => \Webklex\PHPIMAP\Events\FlagDeletedEvent::class,
+        ],
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Available masking options
+    |--------------------------------------------------------------------------
+    |
+    | By using your own custom masks you can implement your own methods for
+    | a better and faster access and less code to write.
+    |
+    | Checkout the two examples custom_attachment_mask and custom_message_mask
+    | for a quick start.
+    |
+    | The provided masks below are used as the default masks.
+     */
+    'masks' => [
+        'message' => \Webklex\PHPIMAP\Support\Masks\MessageMask::class,
+        'attachment' => \Webklex\PHPIMAP\Support\Masks\AttachmentMask::class
+    ]
+];

+ 74 - 0
htdocs/includes/webklex/php-imap/vendor/autoload.php

@@ -0,0 +1,74 @@
+<?php
+
+// autoload.php @generated by Composer
+
+/*
+if (PHP_VERSION_ID < 50600) {
+    echo 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
+    exit(1);
+}
+
+require_once __DIR__ . '/composer/autoload_real.php';
+
+return ComposerAutoloaderInit4da13270269c89a28e472e1f7324e6d1::getLoader();
+*/
+
+spl_autoload_register(function ($class_name) {
+	// Enable this to detect what we need for require_once
+	//var_dump($class_name);
+
+	$preg_match = preg_match('/^Webklex\\\PHPIMAP\\\/', $class_name);
+	if (1 === $preg_match) {
+		$class_name = preg_replace('/\\\/', '/', $class_name);
+		$class_name = preg_replace('/^Webklex\\/PHPIMAP\\//', '', $class_name);
+		require_once __DIR__ . '/../src/' . $class_name . '.php';
+	}
+
+	$preg_match = preg_match('/^Illuminate\\\Support\\\/', $class_name);
+	if (1 === $preg_match) {
+		$class_name = preg_replace('/\\\/', '/', $class_name);
+		$class_name = preg_replace('/^Illuminate\\/Support\\//', '', $class_name);
+		$listofdir = array(0 => __DIR__ . '/illuminate/macroable',
+							1 => __DIR__ . '/illuminate/collections',
+							2 => __DIR__ . '/illuminate/support',
+							3 => __DIR__ . '/illuminate/contracts'
+		);
+		foreach($listofdir as $dir) {
+			if (file_exists($dir . '/' . $class_name . '.php')) {
+				require_once $dir . '/' . $class_name . '.php';
+				break;
+			}
+		}
+	}
+
+	$preg_match = preg_match('/^Illuminate\\\Contracts\\\/', $class_name);
+	if (1 === $preg_match) {
+		$class_name = preg_replace('/\\\/', '/', $class_name);
+		$class_name = preg_replace('/^Illuminate\\/Contracts\\//', '', $class_name);
+		$listofdir = array(
+			0 => __DIR__ . '/illuminate/contracts'
+		);
+		foreach($listofdir as $dir) {
+			if (file_exists($dir . '/' . $class_name . '.php')) {
+				require_once $dir . '/' . $class_name . '.php';
+				break;
+			}
+		}
+	}
+
+	$preg_match = preg_match('/^Carbon\\\/', $class_name);
+	if (1 === $preg_match) {
+		$class_name = preg_replace('/\\\/', '/', $class_name);
+		$class_name = preg_replace('/^Carbon\\//', '', $class_name);
+		$listofdir = array(
+			0 => __DIR__ . '/nesbot/carbon/src/Carbon'
+		);
+		foreach($listofdir as $dir) {
+			if (file_exists($dir . '/' . $class_name . '.php')) {
+				require_once $dir . '/' . $class_name . '.php';
+				break;
+			}
+		}
+	}
+
+});

+ 572 - 0
htdocs/includes/webklex/php-imap/vendor/composer/ClassLoader.php

@@ -0,0 +1,572 @@
+<?php
+
+/*
+ * This file is part of Composer.
+ *
+ * (c) Nils Adermann <naderman@naderman.de>
+ *     Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Autoload;
+
+/**
+ * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
+ *
+ *     $loader = new \Composer\Autoload\ClassLoader();
+ *
+ *     // register classes with namespaces
+ *     $loader->add('Symfony\Component', __DIR__.'/component');
+ *     $loader->add('Symfony',           __DIR__.'/framework');
+ *
+ *     // activate the autoloader
+ *     $loader->register();
+ *
+ *     // to enable searching the include path (eg. for PEAR packages)
+ *     $loader->setUseIncludePath(true);
+ *
+ * In this example, if you try to use a class in the Symfony\Component
+ * namespace or one of its children (Symfony\Component\Console for instance),
+ * the autoloader will first look for the class under the component/
+ * directory, and it will then fallback to the framework/ directory if not
+ * found before giving up.
+ *
+ * This class is loosely based on the Symfony UniversalClassLoader.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ * @see    https://www.php-fig.org/psr/psr-0/
+ * @see    https://www.php-fig.org/psr/psr-4/
+ */
+class ClassLoader
+{
+    /** @var ?string */
+    private $vendorDir;
+
+    // PSR-4
+    /**
+     * @var array[]
+     * @psalm-var array<string, array<string, int>>
+     */
+    private $prefixLengthsPsr4 = array();
+    /**
+     * @var array[]
+     * @psalm-var array<string, array<int, string>>
+     */
+    private $prefixDirsPsr4 = array();
+    /**
+     * @var array[]
+     * @psalm-var array<string, string>
+     */
+    private $fallbackDirsPsr4 = array();
+
+    // PSR-0
+    /**
+     * @var array[]
+     * @psalm-var array<string, array<string, string[]>>
+     */
+    private $prefixesPsr0 = array();
+    /**
+     * @var array[]
+     * @psalm-var array<string, string>
+     */
+    private $fallbackDirsPsr0 = array();
+
+    /** @var bool */
+    private $useIncludePath = false;
+
+    /**
+     * @var string[]
+     * @psalm-var array<string, string>
+     */
+    private $classMap = array();
+
+    /** @var bool */
+    private $classMapAuthoritative = false;
+
+    /**
+     * @var bool[]
+     * @psalm-var array<string, bool>
+     */
+    private $missingClasses = array();
+
+    /** @var ?string */
+    private $apcuPrefix;
+
+    /**
+     * @var self[]
+     */
+    private static $registeredLoaders = array();
+
+    /**
+     * @param ?string $vendorDir
+     */
+    public function __construct($vendorDir = null)
+    {
+        $this->vendorDir = $vendorDir;
+    }
+
+    /**
+     * @return string[]
+     */
+    public function getPrefixes()
+    {
+        if (!empty($this->prefixesPsr0)) {
+            return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
+        }
+
+        return array();
+    }
+
+    /**
+     * @return array[]
+     * @psalm-return array<string, array<int, string>>
+     */
+    public function getPrefixesPsr4()
+    {
+        return $this->prefixDirsPsr4;
+    }
+
+    /**
+     * @return array[]
+     * @psalm-return array<string, string>
+     */
+    public function getFallbackDirs()
+    {
+        return $this->fallbackDirsPsr0;
+    }
+
+    /**
+     * @return array[]
+     * @psalm-return array<string, string>
+     */
+    public function getFallbackDirsPsr4()
+    {
+        return $this->fallbackDirsPsr4;
+    }
+
+    /**
+     * @return string[] Array of classname => path
+     * @psalm-return array<string, string>
+     */
+    public function getClassMap()
+    {
+        return $this->classMap;
+    }
+
+    /**
+     * @param string[] $classMap Class to filename map
+     * @psalm-param array<string, string> $classMap
+     *
+     * @return void
+     */
+    public function addClassMap(array $classMap)
+    {
+        if ($this->classMap) {
+            $this->classMap = array_merge($this->classMap, $classMap);
+        } else {
+            $this->classMap = $classMap;
+        }
+    }
+
+    /**
+     * Registers a set of PSR-0 directories for a given prefix, either
+     * appending or prepending to the ones previously set for this prefix.
+     *
+     * @param string          $prefix  The prefix
+     * @param string[]|string $paths   The PSR-0 root directories
+     * @param bool            $prepend Whether to prepend the directories
+     *
+     * @return void
+     */
+    public function add($prefix, $paths, $prepend = false)
+    {
+        if (!$prefix) {
+            if ($prepend) {
+                $this->fallbackDirsPsr0 = array_merge(
+                    (array) $paths,
+                    $this->fallbackDirsPsr0
+                );
+            } else {
+                $this->fallbackDirsPsr0 = array_merge(
+                    $this->fallbackDirsPsr0,
+                    (array) $paths
+                );
+            }
+
+            return;
+        }
+
+        $first = $prefix[0];
+        if (!isset($this->prefixesPsr0[$first][$prefix])) {
+            $this->prefixesPsr0[$first][$prefix] = (array) $paths;
+
+            return;
+        }
+        if ($prepend) {
+            $this->prefixesPsr0[$first][$prefix] = array_merge(
+                (array) $paths,
+                $this->prefixesPsr0[$first][$prefix]
+            );
+        } else {
+            $this->prefixesPsr0[$first][$prefix] = array_merge(
+                $this->prefixesPsr0[$first][$prefix],
+                (array) $paths
+            );
+        }
+    }
+
+    /**
+     * Registers a set of PSR-4 directories for a given namespace, either
+     * appending or prepending to the ones previously set for this namespace.
+     *
+     * @param string          $prefix  The prefix/namespace, with trailing '\\'
+     * @param string[]|string $paths   The PSR-4 base directories
+     * @param bool            $prepend Whether to prepend the directories
+     *
+     * @throws \InvalidArgumentException
+     *
+     * @return void
+     */
+    public function addPsr4($prefix, $paths, $prepend = false)
+    {
+        if (!$prefix) {
+            // Register directories for the root namespace.
+            if ($prepend) {
+                $this->fallbackDirsPsr4 = array_merge(
+                    (array) $paths,
+                    $this->fallbackDirsPsr4
+                );
+            } else {
+                $this->fallbackDirsPsr4 = array_merge(
+                    $this->fallbackDirsPsr4,
+                    (array) $paths
+                );
+            }
+        } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
+            // Register directories for a new namespace.
+            $length = strlen($prefix);
+            if ('\\' !== $prefix[$length - 1]) {
+                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+            }
+            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+            $this->prefixDirsPsr4[$prefix] = (array) $paths;
+        } elseif ($prepend) {
+            // Prepend directories for an already registered namespace.
+            $this->prefixDirsPsr4[$prefix] = array_merge(
+                (array) $paths,
+                $this->prefixDirsPsr4[$prefix]
+            );
+        } else {
+            // Append directories for an already registered namespace.
+            $this->prefixDirsPsr4[$prefix] = array_merge(
+                $this->prefixDirsPsr4[$prefix],
+                (array) $paths
+            );
+        }
+    }
+
+    /**
+     * Registers a set of PSR-0 directories for a given prefix,
+     * replacing any others previously set for this prefix.
+     *
+     * @param string          $prefix The prefix
+     * @param string[]|string $paths  The PSR-0 base directories
+     *
+     * @return void
+     */
+    public function set($prefix, $paths)
+    {
+        if (!$prefix) {
+            $this->fallbackDirsPsr0 = (array) $paths;
+        } else {
+            $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
+        }
+    }
+
+    /**
+     * Registers a set of PSR-4 directories for a given namespace,
+     * replacing any others previously set for this namespace.
+     *
+     * @param string          $prefix The prefix/namespace, with trailing '\\'
+     * @param string[]|string $paths  The PSR-4 base directories
+     *
+     * @throws \InvalidArgumentException
+     *
+     * @return void
+     */
+    public function setPsr4($prefix, $paths)
+    {
+        if (!$prefix) {
+            $this->fallbackDirsPsr4 = (array) $paths;
+        } else {
+            $length = strlen($prefix);
+            if ('\\' !== $prefix[$length - 1]) {
+                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+            }
+            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+            $this->prefixDirsPsr4[$prefix] = (array) $paths;
+        }
+    }
+
+    /**
+     * Turns on searching the include path for class files.
+     *
+     * @param bool $useIncludePath
+     *
+     * @return void
+     */
+    public function setUseIncludePath($useIncludePath)
+    {
+        $this->useIncludePath = $useIncludePath;
+    }
+
+    /**
+     * Can be used to check if the autoloader uses the include path to check
+     * for classes.
+     *
+     * @return bool
+     */
+    public function getUseIncludePath()
+    {
+        return $this->useIncludePath;
+    }
+
+    /**
+     * Turns off searching the prefix and fallback directories for classes
+     * that have not been registered with the class map.
+     *
+     * @param bool $classMapAuthoritative
+     *
+     * @return void
+     */
+    public function setClassMapAuthoritative($classMapAuthoritative)
+    {
+        $this->classMapAuthoritative = $classMapAuthoritative;
+    }
+
+    /**
+     * Should class lookup fail if not found in the current class map?
+     *
+     * @return bool
+     */
+    public function isClassMapAuthoritative()
+    {
+        return $this->classMapAuthoritative;
+    }
+
+    /**
+     * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
+     *
+     * @param string|null $apcuPrefix
+     *
+     * @return void
+     */
+    public function setApcuPrefix($apcuPrefix)
+    {
+        $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
+    }
+
+    /**
+     * The APCu prefix in use, or null if APCu caching is not enabled.
+     *
+     * @return string|null
+     */
+    public function getApcuPrefix()
+    {
+        return $this->apcuPrefix;
+    }
+
+    /**
+     * Registers this instance as an autoloader.
+     *
+     * @param bool $prepend Whether to prepend the autoloader or not
+     *
+     * @return void
+     */
+    public function register($prepend = false)
+    {
+        spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+
+        if (null === $this->vendorDir) {
+            return;
+        }
+
+        if ($prepend) {
+            self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
+        } else {
+            unset(self::$registeredLoaders[$this->vendorDir]);
+            self::$registeredLoaders[$this->vendorDir] = $this;
+        }
+    }
+
+    /**
+     * Unregisters this instance as an autoloader.
+     *
+     * @return void
+     */
+    public function unregister()
+    {
+        spl_autoload_unregister(array($this, 'loadClass'));
+
+        if (null !== $this->vendorDir) {
+            unset(self::$registeredLoaders[$this->vendorDir]);
+        }
+    }
+
+    /**
+     * Loads the given class or interface.
+     *
+     * @param  string    $class The name of the class
+     * @return true|null True if loaded, null otherwise
+     */
+    public function loadClass($class)
+    {
+        if ($file = $this->findFile($class)) {
+            includeFile($file);
+
+            return true;
+        }
+
+        return null;
+    }
+
+    /**
+     * Finds the path to the file where the class is defined.
+     *
+     * @param string $class The name of the class
+     *
+     * @return string|false The path if found, false otherwise
+     */
+    public function findFile($class)
+    {
+        // class map lookup
+        if (isset($this->classMap[$class])) {
+            return $this->classMap[$class];
+        }
+        if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
+            return false;
+        }
+        if (null !== $this->apcuPrefix) {
+            $file = apcu_fetch($this->apcuPrefix.$class, $hit);
+            if ($hit) {
+                return $file;
+            }
+        }
+
+        $file = $this->findFileWithExtension($class, '.php');
+
+        // Search for Hack files if we are running on HHVM
+        if (false === $file && defined('HHVM_VERSION')) {
+            $file = $this->findFileWithExtension($class, '.hh');
+        }
+
+        if (null !== $this->apcuPrefix) {
+            apcu_add($this->apcuPrefix.$class, $file);
+        }
+
+        if (false === $file) {
+            // Remember that this class does not exist.
+            $this->missingClasses[$class] = true;
+        }
+
+        return $file;
+    }
+
+    /**
+     * Returns the currently registered loaders indexed by their corresponding vendor directories.
+     *
+     * @return self[]
+     */
+    public static function getRegisteredLoaders()
+    {
+        return self::$registeredLoaders;
+    }
+
+    /**
+     * @param  string       $class
+     * @param  string       $ext
+     * @return string|false
+     */
+    private function findFileWithExtension($class, $ext)
+    {
+        // PSR-4 lookup
+        $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
+
+        $first = $class[0];
+        if (isset($this->prefixLengthsPsr4[$first])) {
+            $subPath = $class;
+            while (false !== $lastPos = strrpos($subPath, '\\')) {
+                $subPath = substr($subPath, 0, $lastPos);
+                $search = $subPath . '\\';
+                if (isset($this->prefixDirsPsr4[$search])) {
+                    $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
+                    foreach ($this->prefixDirsPsr4[$search] as $dir) {
+                        if (file_exists($file = $dir . $pathEnd)) {
+                            return $file;
+                        }
+                    }
+                }
+            }
+        }
+
+        // PSR-4 fallback dirs
+        foreach ($this->fallbackDirsPsr4 as $dir) {
+            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
+                return $file;
+            }
+        }
+
+        // PSR-0 lookup
+        if (false !== $pos = strrpos($class, '\\')) {
+            // namespaced class name
+            $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
+                . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
+        } else {
+            // PEAR-like class name
+            $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
+        }
+
+        if (isset($this->prefixesPsr0[$first])) {
+            foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
+                if (0 === strpos($class, $prefix)) {
+                    foreach ($dirs as $dir) {
+                        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+                            return $file;
+                        }
+                    }
+                }
+            }
+        }
+
+        // PSR-0 fallback dirs
+        foreach ($this->fallbackDirsPsr0 as $dir) {
+            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+                return $file;
+            }
+        }
+
+        // PSR-0 include paths.
+        if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
+            return $file;
+        }
+
+        return false;
+    }
+}
+
+/**
+ * Scope isolated include.
+ *
+ * Prevents access to $this/self from included files.
+ *
+ * @param  string $file
+ * @return void
+ * @private
+ */
+function includeFile($file)
+{
+    include $file;
+}

+ 352 - 0
htdocs/includes/webklex/php-imap/vendor/composer/InstalledVersions.php

@@ -0,0 +1,352 @@
+<?php
+
+/*
+ * This file is part of Composer.
+ *
+ * (c) Nils Adermann <naderman@naderman.de>
+ *     Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer;
+
+use Composer\Autoload\ClassLoader;
+use Composer\Semver\VersionParser;
+
+/**
+ * This class is copied in every Composer installed project and available to all
+ *
+ * See also https://getcomposer.org/doc/07-runtime.md#installed-versions
+ *
+ * To require its presence, you can require `composer-runtime-api ^2.0`
+ *
+ * @final
+ */
+class InstalledVersions
+{
+    /**
+     * @var mixed[]|null
+     * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
+     */
+    private static $installed;
+
+    /**
+     * @var bool|null
+     */
+    private static $canGetVendors;
+
+    /**
+     * @var array[]
+     * @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
+     */
+    private static $installedByVendor = array();
+
+    /**
+     * Returns a list of all package names which are present, either by being installed, replaced or provided
+     *
+     * @return string[]
+     * @psalm-return list<string>
+     */
+    public static function getInstalledPackages()
+    {
+        $packages = array();
+        foreach (self::getInstalled() as $installed) {
+            $packages[] = array_keys($installed['versions']);
+        }
+
+        if (1 === \count($packages)) {
+            return $packages[0];
+        }
+
+        return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
+    }
+
+    /**
+     * Returns a list of all package names with a specific type e.g. 'library'
+     *
+     * @param  string   $type
+     * @return string[]
+     * @psalm-return list<string>
+     */
+    public static function getInstalledPackagesByType($type)
+    {
+        $packagesByType = array();
+
+        foreach (self::getInstalled() as $installed) {
+            foreach ($installed['versions'] as $name => $package) {
+                if (isset($package['type']) && $package['type'] === $type) {
+                    $packagesByType[] = $name;
+                }
+            }
+        }
+
+        return $packagesByType;
+    }
+
+    /**
+     * Checks whether the given package is installed
+     *
+     * This also returns true if the package name is provided or replaced by another package
+     *
+     * @param  string $packageName
+     * @param  bool   $includeDevRequirements
+     * @return bool
+     */
+    public static function isInstalled($packageName, $includeDevRequirements = true)
+    {
+        foreach (self::getInstalled() as $installed) {
+            if (isset($installed['versions'][$packageName])) {
+                return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Checks whether the given package satisfies a version constraint
+     *
+     * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
+     *
+     *   Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
+     *
+     * @param  VersionParser $parser      Install composer/semver to have access to this class and functionality
+     * @param  string        $packageName
+     * @param  string|null   $constraint  A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
+     * @return bool
+     */
+    public static function satisfies(VersionParser $parser, $packageName, $constraint)
+    {
+        $constraint = $parser->parseConstraints($constraint);
+        $provided = $parser->parseConstraints(self::getVersionRanges($packageName));
+
+        return $provided->matches($constraint);
+    }
+
+    /**
+     * Returns a version constraint representing all the range(s) which are installed for a given package
+     *
+     * It is easier to use this via isInstalled() with the $constraint argument if you need to check
+     * whether a given version of a package is installed, and not just whether it exists
+     *
+     * @param  string $packageName
+     * @return string Version constraint usable with composer/semver
+     */
+    public static function getVersionRanges($packageName)
+    {
+        foreach (self::getInstalled() as $installed) {
+            if (!isset($installed['versions'][$packageName])) {
+                continue;
+            }
+
+            $ranges = array();
+            if (isset($installed['versions'][$packageName]['pretty_version'])) {
+                $ranges[] = $installed['versions'][$packageName]['pretty_version'];
+            }
+            if (array_key_exists('aliases', $installed['versions'][$packageName])) {
+                $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
+            }
+            if (array_key_exists('replaced', $installed['versions'][$packageName])) {
+                $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
+            }
+            if (array_key_exists('provided', $installed['versions'][$packageName])) {
+                $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
+            }
+
+            return implode(' || ', $ranges);
+        }
+
+        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+    }
+
+    /**
+     * @param  string      $packageName
+     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
+     */
+    public static function getVersion($packageName)
+    {
+        foreach (self::getInstalled() as $installed) {
+            if (!isset($installed['versions'][$packageName])) {
+                continue;
+            }
+
+            if (!isset($installed['versions'][$packageName]['version'])) {
+                return null;
+            }
+
+            return $installed['versions'][$packageName]['version'];
+        }
+
+        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+    }
+
+    /**
+     * @param  string      $packageName
+     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
+     */
+    public static function getPrettyVersion($packageName)
+    {
+        foreach (self::getInstalled() as $installed) {
+            if (!isset($installed['versions'][$packageName])) {
+                continue;
+            }
+
+            if (!isset($installed['versions'][$packageName]['pretty_version'])) {
+                return null;
+            }
+
+            return $installed['versions'][$packageName]['pretty_version'];
+        }
+
+        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+    }
+
+    /**
+     * @param  string      $packageName
+     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
+     */
+    public static function getReference($packageName)
+    {
+        foreach (self::getInstalled() as $installed) {
+            if (!isset($installed['versions'][$packageName])) {
+                continue;
+            }
+
+            if (!isset($installed['versions'][$packageName]['reference'])) {
+                return null;
+            }
+
+            return $installed['versions'][$packageName]['reference'];
+        }
+
+        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+    }
+
+    /**
+     * @param  string      $packageName
+     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
+     */
+    public static function getInstallPath($packageName)
+    {
+        foreach (self::getInstalled() as $installed) {
+            if (!isset($installed['versions'][$packageName])) {
+                continue;
+            }
+
+            return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
+        }
+
+        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+    }
+
+    /**
+     * @return array
+     * @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
+     */
+    public static function getRootPackage()
+    {
+        $installed = self::getInstalled();
+
+        return $installed[0]['root'];
+    }
+
+    /**
+     * Returns the raw installed.php data for custom implementations
+     *
+     * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
+     * @return array[]
+     * @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
+     */
+    public static function getRawData()
+    {
+        @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
+
+        if (null === self::$installed) {
+            // only require the installed.php file if this file is loaded from its dumped location,
+            // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
+            if (substr(__DIR__, -8, 1) !== 'C') {
+                self::$installed = include __DIR__ . '/installed.php';
+            } else {
+                self::$installed = array();
+            }
+        }
+
+        return self::$installed;
+    }
+
+    /**
+     * Returns the raw data of all installed.php which are currently loaded for custom implementations
+     *
+     * @return array[]
+     * @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
+     */
+    public static function getAllRawData()
+    {
+        return self::getInstalled();
+    }
+
+    /**
+     * Lets you reload the static array from another file
+     *
+     * This is only useful for complex integrations in which a project needs to use
+     * this class but then also needs to execute another project's autoloader in process,
+     * and wants to ensure both projects have access to their version of installed.php.
+     *
+     * A typical case would be PHPUnit, where it would need to make sure it reads all
+     * the data it needs from this class, then call reload() with
+     * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
+     * the project in which it runs can then also use this class safely, without
+     * interference between PHPUnit's dependencies and the project's dependencies.
+     *
+     * @param  array[] $data A vendor/composer/installed.php data set
+     * @return void
+     *
+     * @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
+     */
+    public static function reload($data)
+    {
+        self::$installed = $data;
+        self::$installedByVendor = array();
+    }
+
+    /**
+     * @return array[]
+     * @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
+     */
+    private static function getInstalled()
+    {
+        if (null === self::$canGetVendors) {
+            self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
+        }
+
+        $installed = array();
+
+        if (self::$canGetVendors) {
+            foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
+                if (isset(self::$installedByVendor[$vendorDir])) {
+                    $installed[] = self::$installedByVendor[$vendorDir];
+                } elseif (is_file($vendorDir.'/composer/installed.php')) {
+                    $installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
+                    if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
+                        self::$installed = $installed[count($installed) - 1];
+                    }
+                }
+            }
+        }
+
+        if (null === self::$installed) {
+            // only require the installed.php file if this file is loaded from its dumped location,
+            // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
+            if (substr(__DIR__, -8, 1) !== 'C') {
+                self::$installed = require __DIR__ . '/installed.php';
+            } else {
+                self::$installed = array();
+            }
+        }
+        $installed[] = self::$installed;
+
+        return $installed;
+    }
+}

+ 21 - 0
htdocs/includes/webklex/php-imap/vendor/composer/LICENSE

@@ -0,0 +1,21 @@
+
+Copyright (c) Nils Adermann, Jordi Boggiano
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+

+ 460 - 0
htdocs/includes/webklex/php-imap/vendor/composer/autoload_classmap.php

@@ -0,0 +1,460 @@
+<?php
+
+// autoload_classmap.php @generated by Composer
+
+$vendorDir = dirname(__DIR__);
+$baseDir = dirname($vendorDir);
+
+return array(
+    'Attribute' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
+    'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
+    'File_Iterator' => $vendorDir . '/phpunit/php-file-iterator/src/Iterator.php',
+    'File_Iterator_Facade' => $vendorDir . '/phpunit/php-file-iterator/src/Facade.php',
+    'File_Iterator_Factory' => $vendorDir . '/phpunit/php-file-iterator/src/Factory.php',
+    'PHPUnit\\Framework\\Assert' => $vendorDir . '/phpunit/phpunit/src/ForwardCompatibility/Assert.php',
+    'PHPUnit\\Framework\\AssertionFailedError' => $vendorDir . '/phpunit/phpunit/src/ForwardCompatibility/AssertionFailedError.php',
+    'PHPUnit\\Framework\\BaseTestListener' => $vendorDir . '/phpunit/phpunit/src/ForwardCompatibility/BaseTestListener.php',
+    'PHPUnit\\Framework\\Test' => $vendorDir . '/phpunit/phpunit/src/ForwardCompatibility/Test.php',
+    'PHPUnit\\Framework\\TestCase' => $vendorDir . '/phpunit/phpunit/src/ForwardCompatibility/TestCase.php',
+    'PHPUnit\\Framework\\TestListener' => $vendorDir . '/phpunit/phpunit/src/ForwardCompatibility/TestListener.php',
+    'PHPUnit\\Framework\\TestSuite' => $vendorDir . '/phpunit/phpunit/src/ForwardCompatibility/TestSuite.php',
+    'PHPUnit_Exception' => $vendorDir . '/phpunit/phpunit/src/Exception.php',
+    'PHPUnit_Extensions_GroupTestSuite' => $vendorDir . '/phpunit/phpunit/src/Extensions/GroupTestSuite.php',
+    'PHPUnit_Extensions_PhptTestCase' => $vendorDir . '/phpunit/phpunit/src/Extensions/PhptTestCase.php',
+    'PHPUnit_Extensions_PhptTestSuite' => $vendorDir . '/phpunit/phpunit/src/Extensions/PhptTestSuite.php',
+    'PHPUnit_Extensions_RepeatedTest' => $vendorDir . '/phpunit/phpunit/src/Extensions/RepeatedTest.php',
+    'PHPUnit_Extensions_TestDecorator' => $vendorDir . '/phpunit/phpunit/src/Extensions/TestDecorator.php',
+    'PHPUnit_Extensions_TicketListener' => $vendorDir . '/phpunit/phpunit/src/Extensions/TicketListener.php',
+    'PHPUnit_Framework_Assert' => $vendorDir . '/phpunit/phpunit/src/Framework/Assert.php',
+    'PHPUnit_Framework_AssertionFailedError' => $vendorDir . '/phpunit/phpunit/src/Framework/AssertionFailedError.php',
+    'PHPUnit_Framework_BaseTestListener' => $vendorDir . '/phpunit/phpunit/src/Framework/BaseTestListener.php',
+    'PHPUnit_Framework_CodeCoverageException' => $vendorDir . '/phpunit/phpunit/src/Framework/CodeCoverageException.php',
+    'PHPUnit_Framework_Constraint' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint.php',
+    'PHPUnit_Framework_Constraint_And' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/And.php',
+    'PHPUnit_Framework_Constraint_ArrayHasKey' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/ArrayHasKey.php',
+    'PHPUnit_Framework_Constraint_ArraySubset' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/ArraySubset.php',
+    'PHPUnit_Framework_Constraint_Attribute' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Attribute.php',
+    'PHPUnit_Framework_Constraint_Callback' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Callback.php',
+    'PHPUnit_Framework_Constraint_ClassHasAttribute' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/ClassHasAttribute.php',
+    'PHPUnit_Framework_Constraint_ClassHasStaticAttribute' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/ClassHasStaticAttribute.php',
+    'PHPUnit_Framework_Constraint_Composite' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Composite.php',
+    'PHPUnit_Framework_Constraint_Count' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Count.php',
+    'PHPUnit_Framework_Constraint_Exception' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Exception.php',
+    'PHPUnit_Framework_Constraint_ExceptionCode' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/ExceptionCode.php',
+    'PHPUnit_Framework_Constraint_ExceptionMessage' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/ExceptionMessage.php',
+    'PHPUnit_Framework_Constraint_ExceptionMessageRegExp' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/ExceptionMessageRegExp.php',
+    'PHPUnit_Framework_Constraint_FileExists' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/FileExists.php',
+    'PHPUnit_Framework_Constraint_GreaterThan' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/GreaterThan.php',
+    'PHPUnit_Framework_Constraint_IsAnything' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsAnything.php',
+    'PHPUnit_Framework_Constraint_IsEmpty' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsEmpty.php',
+    'PHPUnit_Framework_Constraint_IsEqual' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsEqual.php',
+    'PHPUnit_Framework_Constraint_IsFalse' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsFalse.php',
+    'PHPUnit_Framework_Constraint_IsIdentical' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsIdentical.php',
+    'PHPUnit_Framework_Constraint_IsInstanceOf' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsInstanceOf.php',
+    'PHPUnit_Framework_Constraint_IsJson' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsJson.php',
+    'PHPUnit_Framework_Constraint_IsNull' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsNull.php',
+    'PHPUnit_Framework_Constraint_IsTrue' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsTrue.php',
+    'PHPUnit_Framework_Constraint_IsType' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsType.php',
+    'PHPUnit_Framework_Constraint_JsonMatches' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/JsonMatches.php',
+    'PHPUnit_Framework_Constraint_JsonMatches_ErrorMessageProvider' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/JsonMatches/ErrorMessageProvider.php',
+    'PHPUnit_Framework_Constraint_LessThan' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/LessThan.php',
+    'PHPUnit_Framework_Constraint_Not' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Not.php',
+    'PHPUnit_Framework_Constraint_ObjectHasAttribute' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/ObjectHasAttribute.php',
+    'PHPUnit_Framework_Constraint_Or' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Or.php',
+    'PHPUnit_Framework_Constraint_PCREMatch' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/PCREMatch.php',
+    'PHPUnit_Framework_Constraint_SameSize' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/SameSize.php',
+    'PHPUnit_Framework_Constraint_StringContains' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/StringContains.php',
+    'PHPUnit_Framework_Constraint_StringEndsWith' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/StringEndsWith.php',
+    'PHPUnit_Framework_Constraint_StringMatches' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/StringMatches.php',
+    'PHPUnit_Framework_Constraint_StringStartsWith' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/StringStartsWith.php',
+    'PHPUnit_Framework_Constraint_TraversableContains' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/TraversableContains.php',
+    'PHPUnit_Framework_Constraint_TraversableContainsOnly' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/TraversableContainsOnly.php',
+    'PHPUnit_Framework_Constraint_Xor' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Xor.php',
+    'PHPUnit_Framework_Error' => $vendorDir . '/phpunit/phpunit/src/Framework/Error.php',
+    'PHPUnit_Framework_Error_Deprecated' => $vendorDir . '/phpunit/phpunit/src/Framework/Error/Deprecated.php',
+    'PHPUnit_Framework_Error_Notice' => $vendorDir . '/phpunit/phpunit/src/Framework/Error/Notice.php',
+    'PHPUnit_Framework_Error_Warning' => $vendorDir . '/phpunit/phpunit/src/Framework/Error/Warning.php',
+    'PHPUnit_Framework_Exception' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception.php',
+    'PHPUnit_Framework_ExceptionWrapper' => $vendorDir . '/phpunit/phpunit/src/Framework/ExceptionWrapper.php',
+    'PHPUnit_Framework_ExpectationFailedException' => $vendorDir . '/phpunit/phpunit/src/Framework/ExpectationFailedException.php',
+    'PHPUnit_Framework_IncompleteTest' => $vendorDir . '/phpunit/phpunit/src/Framework/IncompleteTest.php',
+    'PHPUnit_Framework_IncompleteTestCase' => $vendorDir . '/phpunit/phpunit/src/Framework/IncompleteTestCase.php',
+    'PHPUnit_Framework_IncompleteTestError' => $vendorDir . '/phpunit/phpunit/src/Framework/IncompleteTestError.php',
+    'PHPUnit_Framework_InvalidCoversTargetError' => $vendorDir . '/phpunit/phpunit/src/Framework/InvalidCoversTargetError.php',
+    'PHPUnit_Framework_InvalidCoversTargetException' => $vendorDir . '/phpunit/phpunit/src/Framework/InvalidCoversTargetException.php',
+    'PHPUnit_Framework_MockObject_BadMethodCallException' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Exception/BadMethodCallException.php',
+    'PHPUnit_Framework_MockObject_Builder_Identity' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/Identity.php',
+    'PHPUnit_Framework_MockObject_Builder_InvocationMocker' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/InvocationMocker.php',
+    'PHPUnit_Framework_MockObject_Builder_Match' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/Match.php',
+    'PHPUnit_Framework_MockObject_Builder_MethodNameMatch' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/MethodNameMatch.php',
+    'PHPUnit_Framework_MockObject_Builder_Namespace' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/Namespace.php',
+    'PHPUnit_Framework_MockObject_Builder_ParametersMatch' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/ParametersMatch.php',
+    'PHPUnit_Framework_MockObject_Builder_Stub' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/Stub.php',
+    'PHPUnit_Framework_MockObject_Exception' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Exception/Exception.php',
+    'PHPUnit_Framework_MockObject_Generator' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Generator.php',
+    'PHPUnit_Framework_MockObject_Invocation' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Invocation.php',
+    'PHPUnit_Framework_MockObject_InvocationMocker' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/InvocationMocker.php',
+    'PHPUnit_Framework_MockObject_Invocation_Object' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Invocation/Object.php',
+    'PHPUnit_Framework_MockObject_Invocation_Static' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Invocation/Static.php',
+    'PHPUnit_Framework_MockObject_Invokable' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Invokable.php',
+    'PHPUnit_Framework_MockObject_Matcher' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher.php',
+    'PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/AnyInvokedCount.php',
+    'PHPUnit_Framework_MockObject_Matcher_AnyParameters' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/AnyParameters.php',
+    'PHPUnit_Framework_MockObject_Matcher_ConsecutiveParameters' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/ConsecutiveParameters.php',
+    'PHPUnit_Framework_MockObject_Matcher_Invocation' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/Invocation.php',
+    'PHPUnit_Framework_MockObject_Matcher_InvokedAtIndex' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedAtIndex.php',
+    'PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastCount' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedAtLeastCount.php',
+    'PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastOnce' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedAtLeastOnce.php',
+    'PHPUnit_Framework_MockObject_Matcher_InvokedAtMostCount' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedAtMostCount.php',
+    'PHPUnit_Framework_MockObject_Matcher_InvokedCount' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedCount.php',
+    'PHPUnit_Framework_MockObject_Matcher_InvokedRecorder' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedRecorder.php',
+    'PHPUnit_Framework_MockObject_Matcher_MethodName' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/MethodName.php',
+    'PHPUnit_Framework_MockObject_Matcher_Parameters' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/Parameters.php',
+    'PHPUnit_Framework_MockObject_Matcher_StatelessInvocation' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/StatelessInvocation.php',
+    'PHPUnit_Framework_MockObject_MockBuilder' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/MockBuilder.php',
+    'PHPUnit_Framework_MockObject_MockObject' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/MockObject.php',
+    'PHPUnit_Framework_MockObject_RuntimeException' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Exception/RuntimeException.php',
+    'PHPUnit_Framework_MockObject_Stub' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub.php',
+    'PHPUnit_Framework_MockObject_Stub_ConsecutiveCalls' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ConsecutiveCalls.php',
+    'PHPUnit_Framework_MockObject_Stub_Exception' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/Exception.php',
+    'PHPUnit_Framework_MockObject_Stub_MatcherCollection' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/MatcherCollection.php',
+    'PHPUnit_Framework_MockObject_Stub_Return' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/Return.php',
+    'PHPUnit_Framework_MockObject_Stub_ReturnArgument' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ReturnArgument.php',
+    'PHPUnit_Framework_MockObject_Stub_ReturnCallback' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ReturnCallback.php',
+    'PHPUnit_Framework_MockObject_Stub_ReturnSelf' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ReturnSelf.php',
+    'PHPUnit_Framework_MockObject_Stub_ReturnValueMap' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ReturnValueMap.php',
+    'PHPUnit_Framework_MockObject_Verifiable' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Verifiable.php',
+    'PHPUnit_Framework_OutputError' => $vendorDir . '/phpunit/phpunit/src/Framework/OutputError.php',
+    'PHPUnit_Framework_RiskyTest' => $vendorDir . '/phpunit/phpunit/src/Framework/RiskyTest.php',
+    'PHPUnit_Framework_RiskyTestError' => $vendorDir . '/phpunit/phpunit/src/Framework/RiskyTestError.php',
+    'PHPUnit_Framework_SelfDescribing' => $vendorDir . '/phpunit/phpunit/src/Framework/SelfDescribing.php',
+    'PHPUnit_Framework_SkippedTest' => $vendorDir . '/phpunit/phpunit/src/Framework/SkippedTest.php',
+    'PHPUnit_Framework_SkippedTestCase' => $vendorDir . '/phpunit/phpunit/src/Framework/SkippedTestCase.php',
+    'PHPUnit_Framework_SkippedTestError' => $vendorDir . '/phpunit/phpunit/src/Framework/SkippedTestError.php',
+    'PHPUnit_Framework_SkippedTestSuiteError' => $vendorDir . '/phpunit/phpunit/src/Framework/SkippedTestSuiteError.php',
+    'PHPUnit_Framework_SyntheticError' => $vendorDir . '/phpunit/phpunit/src/Framework/SyntheticError.php',
+    'PHPUnit_Framework_Test' => $vendorDir . '/phpunit/phpunit/src/Framework/Test.php',
+    'PHPUnit_Framework_TestCase' => $vendorDir . '/phpunit/phpunit/src/Framework/TestCase.php',
+    'PHPUnit_Framework_TestFailure' => $vendorDir . '/phpunit/phpunit/src/Framework/TestFailure.php',
+    'PHPUnit_Framework_TestListener' => $vendorDir . '/phpunit/phpunit/src/Framework/TestListener.php',
+    'PHPUnit_Framework_TestResult' => $vendorDir . '/phpunit/phpunit/src/Framework/TestResult.php',
+    'PHPUnit_Framework_TestSuite' => $vendorDir . '/phpunit/phpunit/src/Framework/TestSuite.php',
+    'PHPUnit_Framework_TestSuite_DataProvider' => $vendorDir . '/phpunit/phpunit/src/Framework/TestSuite/DataProvider.php',
+    'PHPUnit_Framework_UnintentionallyCoveredCodeError' => $vendorDir . '/phpunit/phpunit/src/Framework/UnintentionallyCoveredCodeError.php',
+    'PHPUnit_Framework_Warning' => $vendorDir . '/phpunit/phpunit/src/Framework/Warning.php',
+    'PHPUnit_Runner_BaseTestRunner' => $vendorDir . '/phpunit/phpunit/src/Runner/BaseTestRunner.php',
+    'PHPUnit_Runner_Exception' => $vendorDir . '/phpunit/phpunit/src/Runner/Exception.php',
+    'PHPUnit_Runner_Filter_Factory' => $vendorDir . '/phpunit/phpunit/src/Runner/Filter/Factory.php',
+    'PHPUnit_Runner_Filter_GroupFilterIterator' => $vendorDir . '/phpunit/phpunit/src/Runner/Filter/Group.php',
+    'PHPUnit_Runner_Filter_Group_Exclude' => $vendorDir . '/phpunit/phpunit/src/Runner/Filter/Group/Exclude.php',
+    'PHPUnit_Runner_Filter_Group_Include' => $vendorDir . '/phpunit/phpunit/src/Runner/Filter/Group/Include.php',
+    'PHPUnit_Runner_Filter_Test' => $vendorDir . '/phpunit/phpunit/src/Runner/Filter/Test.php',
+    'PHPUnit_Runner_StandardTestSuiteLoader' => $vendorDir . '/phpunit/phpunit/src/Runner/StandardTestSuiteLoader.php',
+    'PHPUnit_Runner_TestSuiteLoader' => $vendorDir . '/phpunit/phpunit/src/Runner/TestSuiteLoader.php',
+    'PHPUnit_Runner_Version' => $vendorDir . '/phpunit/phpunit/src/Runner/Version.php',
+    'PHPUnit_TextUI_Command' => $vendorDir . '/phpunit/phpunit/src/TextUI/Command.php',
+    'PHPUnit_TextUI_ResultPrinter' => $vendorDir . '/phpunit/phpunit/src/TextUI/ResultPrinter.php',
+    'PHPUnit_TextUI_TestRunner' => $vendorDir . '/phpunit/phpunit/src/TextUI/TestRunner.php',
+    'PHPUnit_Util_Blacklist' => $vendorDir . '/phpunit/phpunit/src/Util/Blacklist.php',
+    'PHPUnit_Util_Configuration' => $vendorDir . '/phpunit/phpunit/src/Util/Configuration.php',
+    'PHPUnit_Util_ErrorHandler' => $vendorDir . '/phpunit/phpunit/src/Util/ErrorHandler.php',
+    'PHPUnit_Util_Fileloader' => $vendorDir . '/phpunit/phpunit/src/Util/Fileloader.php',
+    'PHPUnit_Util_Filesystem' => $vendorDir . '/phpunit/phpunit/src/Util/Filesystem.php',
+    'PHPUnit_Util_Filter' => $vendorDir . '/phpunit/phpunit/src/Util/Filter.php',
+    'PHPUnit_Util_Getopt' => $vendorDir . '/phpunit/phpunit/src/Util/Getopt.php',
+    'PHPUnit_Util_GlobalState' => $vendorDir . '/phpunit/phpunit/src/Util/GlobalState.php',
+    'PHPUnit_Util_InvalidArgumentHelper' => $vendorDir . '/phpunit/phpunit/src/Util/InvalidArgumentHelper.php',
+    'PHPUnit_Util_Log_JSON' => $vendorDir . '/phpunit/phpunit/src/Util/Log/JSON.php',
+    'PHPUnit_Util_Log_JUnit' => $vendorDir . '/phpunit/phpunit/src/Util/Log/JUnit.php',
+    'PHPUnit_Util_Log_TAP' => $vendorDir . '/phpunit/phpunit/src/Util/Log/TAP.php',
+    'PHPUnit_Util_PHP' => $vendorDir . '/phpunit/phpunit/src/Util/PHP.php',
+    'PHPUnit_Util_PHP_Default' => $vendorDir . '/phpunit/phpunit/src/Util/PHP/Default.php',
+    'PHPUnit_Util_PHP_Windows' => $vendorDir . '/phpunit/phpunit/src/Util/PHP/Windows.php',
+    'PHPUnit_Util_Printer' => $vendorDir . '/phpunit/phpunit/src/Util/Printer.php',
+    'PHPUnit_Util_Regex' => $vendorDir . '/phpunit/phpunit/src/Util/Regex.php',
+    'PHPUnit_Util_String' => $vendorDir . '/phpunit/phpunit/src/Util/String.php',
+    'PHPUnit_Util_Test' => $vendorDir . '/phpunit/phpunit/src/Util/Test.php',
+    'PHPUnit_Util_TestDox_NamePrettifier' => $vendorDir . '/phpunit/phpunit/src/Util/TestDox/NamePrettifier.php',
+    'PHPUnit_Util_TestDox_ResultPrinter' => $vendorDir . '/phpunit/phpunit/src/Util/TestDox/ResultPrinter.php',
+    'PHPUnit_Util_TestDox_ResultPrinter_HTML' => $vendorDir . '/phpunit/phpunit/src/Util/TestDox/ResultPrinter/HTML.php',
+    'PHPUnit_Util_TestDox_ResultPrinter_Text' => $vendorDir . '/phpunit/phpunit/src/Util/TestDox/ResultPrinter/Text.php',
+    'PHPUnit_Util_TestSuiteIterator' => $vendorDir . '/phpunit/phpunit/src/Util/TestSuiteIterator.php',
+    'PHPUnit_Util_Type' => $vendorDir . '/phpunit/phpunit/src/Util/Type.php',
+    'PHPUnit_Util_XML' => $vendorDir . '/phpunit/phpunit/src/Util/XML.php',
+    'PHP_CodeCoverage' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage.php',
+    'PHP_CodeCoverage_Driver' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage/Driver.php',
+    'PHP_CodeCoverage_Driver_HHVM' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage/Driver/HHVM.php',
+    'PHP_CodeCoverage_Driver_PHPDBG' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage/Driver/PHPDBG.php',
+    'PHP_CodeCoverage_Driver_Xdebug' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage/Driver/Xdebug.php',
+    'PHP_CodeCoverage_Exception' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage/Exception.php',
+    'PHP_CodeCoverage_Exception_UnintentionallyCoveredCode' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage/Exception/UnintentionallyCoveredCode.php',
+    'PHP_CodeCoverage_Filter' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage/Filter.php',
+    'PHP_CodeCoverage_Report_Clover' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage/Report/Clover.php',
+    'PHP_CodeCoverage_Report_Crap4j' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage/Report/Crap4j.php',
+    'PHP_CodeCoverage_Report_Factory' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage/Report/Factory.php',
+    'PHP_CodeCoverage_Report_HTML' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage/Report/HTML.php',
+    'PHP_CodeCoverage_Report_HTML_Renderer' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage/Report/HTML/Renderer.php',
+    'PHP_CodeCoverage_Report_HTML_Renderer_Dashboard' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage/Report/HTML/Renderer/Dashboard.php',
+    'PHP_CodeCoverage_Report_HTML_Renderer_Directory' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage/Report/HTML/Renderer/Directory.php',
+    'PHP_CodeCoverage_Report_HTML_Renderer_File' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage/Report/HTML/Renderer/File.php',
+    'PHP_CodeCoverage_Report_Node' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage/Report/Node.php',
+    'PHP_CodeCoverage_Report_Node_Directory' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage/Report/Node/Directory.php',
+    'PHP_CodeCoverage_Report_Node_File' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage/Report/Node/File.php',
+    'PHP_CodeCoverage_Report_Node_Iterator' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage/Report/Node/Iterator.php',
+    'PHP_CodeCoverage_Report_PHP' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage/Report/PHP.php',
+    'PHP_CodeCoverage_Report_Text' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage/Report/Text.php',
+    'PHP_CodeCoverage_Report_XML' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage/Report/XML.php',
+    'PHP_CodeCoverage_Report_XML_Directory' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage/Report/XML/Directory.php',
+    'PHP_CodeCoverage_Report_XML_File' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage/Report/XML/File.php',
+    'PHP_CodeCoverage_Report_XML_File_Coverage' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage/Report/XML/File/Coverage.php',
+    'PHP_CodeCoverage_Report_XML_File_Method' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage/Report/XML/File/Method.php',
+    'PHP_CodeCoverage_Report_XML_File_Report' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage/Report/XML/File/Report.php',
+    'PHP_CodeCoverage_Report_XML_File_Unit' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage/Report/XML/File/Unit.php',
+    'PHP_CodeCoverage_Report_XML_Node' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage/Report/XML/Node.php',
+    'PHP_CodeCoverage_Report_XML_Project' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage/Report/XML/Project.php',
+    'PHP_CodeCoverage_Report_XML_Tests' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage/Report/XML/Tests.php',
+    'PHP_CodeCoverage_Report_XML_Totals' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage/Report/XML/Totals.php',
+    'PHP_CodeCoverage_Util' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage/Util.php',
+    'PHP_CodeCoverage_Util_InvalidArgumentHelper' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage/Util/InvalidArgumentHelper.php',
+    'PHP_Timer' => $vendorDir . '/phpunit/php-timer/src/Timer.php',
+    'PHP_Token' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_TokenWithScope' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_TokenWithScopeAndVisibility' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_ABSTRACT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_AMPERSAND' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_AND_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_ARRAY' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_ARRAY_CAST' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_AS' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_ASYNC' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_AT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_AWAIT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_BACKTICK' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_BAD_CHARACTER' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_BOOLEAN_AND' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_BOOLEAN_OR' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_BOOL_CAST' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_BREAK' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_CALLABLE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_CARET' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_CASE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_CATCH' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_CHARACTER' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_CLASS' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_CLASS_C' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_CLASS_NAME_CONSTANT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_CLONE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_CLOSE_BRACKET' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_CLOSE_CURLY' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_CLOSE_SQUARE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_CLOSE_TAG' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_COALESCE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_COLON' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_COMMA' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_COMMENT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_COMPILER_HALT_OFFSET' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_CONCAT_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_CONST' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_CONSTANT_ENCAPSED_STRING' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_CONTINUE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_CURLY_OPEN' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_DEC' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_DECLARE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_DEFAULT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_DIR' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_DIV' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_DIV_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_DNUMBER' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_DO' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_DOC_COMMENT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_DOLLAR' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_DOLLAR_OPEN_CURLY_BRACES' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_DOT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_DOUBLE_ARROW' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_DOUBLE_CAST' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_DOUBLE_COLON' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_DOUBLE_QUOTES' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_ECHO' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_ELLIPSIS' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_ELSE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_ELSEIF' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_EMPTY' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_ENCAPSED_AND_WHITESPACE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_ENDDECLARE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_ENDFOR' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_ENDFOREACH' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_ENDIF' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_ENDSWITCH' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_ENDWHILE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_END_HEREDOC' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_ENUM' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_EQUALS' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_EVAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_EXCLAMATION_MARK' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_EXIT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_EXTENDS' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_FILE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_FINAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_FINALLY' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_FOR' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_FOREACH' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_FUNCTION' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_FUNC_C' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_GLOBAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_GOTO' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_GT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_HALT_COMPILER' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_IF' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_IMPLEMENTS' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_IN' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_INC' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_INCLUDE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_INCLUDE_ONCE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_INLINE_HTML' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_INSTANCEOF' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_INSTEADOF' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_INTERFACE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_INT_CAST' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_ISSET' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_IS_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_IS_GREATER_OR_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_IS_IDENTICAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_IS_NOT_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_IS_NOT_IDENTICAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_IS_SMALLER_OR_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_Includes' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_JOIN' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_LAMBDA_ARROW' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_LAMBDA_CP' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_LAMBDA_OP' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_LINE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_LIST' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_LNUMBER' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_LOGICAL_AND' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_LOGICAL_OR' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_LOGICAL_XOR' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_LT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_METHOD_C' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_MINUS' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_MINUS_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_MOD_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_MULT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_MUL_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_NAMESPACE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_NEW' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_NS_C' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_NS_SEPARATOR' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_NULLSAFE_OBJECT_OPERATOR' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_NUM_STRING' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_OBJECT_CAST' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_OBJECT_OPERATOR' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_ONUMBER' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_OPEN_BRACKET' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_OPEN_CURLY' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_OPEN_SQUARE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_OPEN_TAG' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_OPEN_TAG_WITH_ECHO' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_OR_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_PAAMAYIM_NEKUDOTAYIM' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_PERCENT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_PIPE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_PLUS' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_PLUS_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_POW' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_POW_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_PRINT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_PRIVATE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_PROTECTED' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_PUBLIC' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_QUESTION_MARK' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_REQUIRE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_REQUIRE_ONCE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_RETURN' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_SEMICOLON' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_SHAPE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_SL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_SL_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_SPACESHIP' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_SR' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_SR_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_START_HEREDOC' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_STATIC' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_STRING' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_STRING_CAST' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_STRING_VARNAME' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_SUPER' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_SWITCH' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_Stream' => $vendorDir . '/phpunit/php-token-stream/src/Token/Stream.php',
+    'PHP_Token_Stream_CachingFactory' => $vendorDir . '/phpunit/php-token-stream/src/Token/Stream/CachingFactory.php',
+    'PHP_Token_THROW' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_TILDE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_TRAIT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_TRAIT_C' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_TRY' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_TYPE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_TYPELIST_GT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_TYPELIST_LT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_UNSET' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_UNSET_CAST' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_USE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_USE_FUNCTION' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_VAR' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_VARIABLE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_WHERE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_WHILE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_WHITESPACE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_XHP_ATTRIBUTE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_XHP_CATEGORY' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_XHP_CATEGORY_LABEL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_XHP_CHILDREN' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_XHP_LABEL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_XHP_REQUIRED' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_XHP_TAG_GT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_XHP_TAG_LT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_XHP_TEXT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_XOR_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_YIELD' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PHP_Token_YIELD_FROM' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
+    'PhpToken' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
+    'SebastianBergmann\\Comparator\\ArrayComparator' => $vendorDir . '/sebastian/comparator/src/ArrayComparator.php',
+    'SebastianBergmann\\Comparator\\Comparator' => $vendorDir . '/sebastian/comparator/src/Comparator.php',
+    'SebastianBergmann\\Comparator\\ComparisonFailure' => $vendorDir . '/sebastian/comparator/src/ComparisonFailure.php',
+    'SebastianBergmann\\Comparator\\DOMNodeComparator' => $vendorDir . '/sebastian/comparator/src/DOMNodeComparator.php',
+    'SebastianBergmann\\Comparator\\DateTimeComparator' => $vendorDir . '/sebastian/comparator/src/DateTimeComparator.php',
+    'SebastianBergmann\\Comparator\\DoubleComparator' => $vendorDir . '/sebastian/comparator/src/DoubleComparator.php',
+    'SebastianBergmann\\Comparator\\ExceptionComparator' => $vendorDir . '/sebastian/comparator/src/ExceptionComparator.php',
+    'SebastianBergmann\\Comparator\\Factory' => $vendorDir . '/sebastian/comparator/src/Factory.php',
+    'SebastianBergmann\\Comparator\\MockObjectComparator' => $vendorDir . '/sebastian/comparator/src/MockObjectComparator.php',
+    'SebastianBergmann\\Comparator\\NumericComparator' => $vendorDir . '/sebastian/comparator/src/NumericComparator.php',
+    'SebastianBergmann\\Comparator\\ObjectComparator' => $vendorDir . '/sebastian/comparator/src/ObjectComparator.php',
+    'SebastianBergmann\\Comparator\\ResourceComparator' => $vendorDir . '/sebastian/comparator/src/ResourceComparator.php',
+    'SebastianBergmann\\Comparator\\ScalarComparator' => $vendorDir . '/sebastian/comparator/src/ScalarComparator.php',
+    'SebastianBergmann\\Comparator\\SplObjectStorageComparator' => $vendorDir . '/sebastian/comparator/src/SplObjectStorageComparator.php',
+    'SebastianBergmann\\Comparator\\TypeComparator' => $vendorDir . '/sebastian/comparator/src/TypeComparator.php',
+    'SebastianBergmann\\Diff\\Chunk' => $vendorDir . '/sebastian/diff/src/Chunk.php',
+    'SebastianBergmann\\Diff\\Diff' => $vendorDir . '/sebastian/diff/src/Diff.php',
+    'SebastianBergmann\\Diff\\Differ' => $vendorDir . '/sebastian/diff/src/Differ.php',
+    'SebastianBergmann\\Diff\\LCS\\LongestCommonSubsequence' => $vendorDir . '/sebastian/diff/src/LCS/LongestCommonSubsequence.php',
+    'SebastianBergmann\\Diff\\LCS\\MemoryEfficientImplementation' => $vendorDir . '/sebastian/diff/src/LCS/MemoryEfficientLongestCommonSubsequenceImplementation.php',
+    'SebastianBergmann\\Diff\\LCS\\TimeEfficientImplementation' => $vendorDir . '/sebastian/diff/src/LCS/TimeEfficientLongestCommonSubsequenceImplementation.php',
+    'SebastianBergmann\\Diff\\Line' => $vendorDir . '/sebastian/diff/src/Line.php',
+    'SebastianBergmann\\Diff\\Parser' => $vendorDir . '/sebastian/diff/src/Parser.php',
+    'SebastianBergmann\\Environment\\Console' => $vendorDir . '/sebastian/environment/src/Console.php',
+    'SebastianBergmann\\Environment\\Runtime' => $vendorDir . '/sebastian/environment/src/Runtime.php',
+    'SebastianBergmann\\Exporter\\Exporter' => $vendorDir . '/sebastian/exporter/src/Exporter.php',
+    'SebastianBergmann\\GlobalState\\Blacklist' => $vendorDir . '/sebastian/global-state/src/Blacklist.php',
+    'SebastianBergmann\\GlobalState\\CodeExporter' => $vendorDir . '/sebastian/global-state/src/CodeExporter.php',
+    'SebastianBergmann\\GlobalState\\Exception' => $vendorDir . '/sebastian/global-state/src/Exception.php',
+    'SebastianBergmann\\GlobalState\\Restorer' => $vendorDir . '/sebastian/global-state/src/Restorer.php',
+    'SebastianBergmann\\GlobalState\\RuntimeException' => $vendorDir . '/sebastian/global-state/src/RuntimeException.php',
+    'SebastianBergmann\\GlobalState\\Snapshot' => $vendorDir . '/sebastian/global-state/src/Snapshot.php',
+    'SebastianBergmann\\RecursionContext\\Context' => $vendorDir . '/sebastian/recursion-context/src/Context.php',
+    'SebastianBergmann\\RecursionContext\\Exception' => $vendorDir . '/sebastian/recursion-context/src/Exception.php',
+    'SebastianBergmann\\RecursionContext\\InvalidArgumentException' => $vendorDir . '/sebastian/recursion-context/src/InvalidArgumentException.php',
+    'SebastianBergmann\\Version' => $vendorDir . '/sebastian/version/src/Version.php',
+    'Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
+    'Text_Template' => $vendorDir . '/phpunit/php-text-template/src/Template.php',
+    'UnhandledMatchError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
+    'ValueError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
+);

+ 15 - 0
htdocs/includes/webklex/php-imap/vendor/composer/autoload_files.php

@@ -0,0 +1,15 @@
+<?php
+
+// autoload_files.php @generated by Composer
+
+$vendorDir = dirname(__DIR__);
+$baseDir = dirname($vendorDir);
+
+return array(
+    '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
+    'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
+    '60799491728b879e74601d83e38b2cad' => $vendorDir . '/illuminate/collections/helpers.php',
+    '320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
+    '72579e7bd17821bb1321b87411366eae' => $vendorDir . '/illuminate/support/helpers.php',
+    '6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
+);

+ 9 - 0
htdocs/includes/webklex/php-imap/vendor/composer/autoload_namespaces.php

@@ -0,0 +1,9 @@
+<?php
+
+// autoload_namespaces.php @generated by Composer
+
+$vendorDir = dirname(__DIR__);
+$baseDir = dirname($vendorDir);
+
+return array(
+);

+ 30 - 0
htdocs/includes/webklex/php-imap/vendor/composer/autoload_psr4.php

@@ -0,0 +1,30 @@
+<?php
+
+// autoload_psr4.php @generated by Composer
+
+$vendorDir = dirname(__DIR__);
+$baseDir = dirname($vendorDir);
+
+return array(
+    'voku\\' => array($vendorDir . '/voku/portable-ascii/src/voku'),
+    'phpDocumentor\\Reflection\\' => array($vendorDir . '/phpdocumentor/reflection-common/src', $vendorDir . '/phpdocumentor/type-resolver/src', $vendorDir . '/phpdocumentor/reflection-docblock/src'),
+    'Webmozart\\Assert\\' => array($vendorDir . '/webmozart/assert/src'),
+    'Webklex\\PHPIMAP\\' => array($baseDir . '/src'),
+    'Tests\\' => array($baseDir . '/tests'),
+    'Symfony\\Polyfill\\Php80\\' => array($vendorDir . '/symfony/polyfill-php80'),
+    'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
+    'Symfony\\Polyfill\\Ctype\\' => array($vendorDir . '/symfony/polyfill-ctype'),
+    'Symfony\\Contracts\\Translation\\' => array($vendorDir . '/symfony/translation-contracts'),
+    'Symfony\\Component\\Yaml\\' => array($vendorDir . '/symfony/yaml'),
+    'Symfony\\Component\\Translation\\' => array($vendorDir . '/symfony/translation'),
+    'Symfony\\Component\\HttpFoundation\\' => array($vendorDir . '/symfony/http-foundation'),
+    'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'),
+    'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
+    'Prophecy\\' => array($vendorDir . '/phpspec/prophecy/src/Prophecy'),
+    'Illuminate\\Support\\' => array($vendorDir . '/illuminate/macroable', $vendorDir . '/illuminate/collections', $vendorDir . '/illuminate/support'),
+    'Illuminate\\Pagination\\' => array($vendorDir . '/illuminate/pagination'),
+    'Illuminate\\Contracts\\' => array($vendorDir . '/illuminate/contracts'),
+    'Doctrine\\Instantiator\\' => array($vendorDir . '/doctrine/instantiator/src/Doctrine/Instantiator'),
+    'Doctrine\\Inflector\\' => array($vendorDir . '/doctrine/inflector/lib/Doctrine/Inflector'),
+    'Carbon\\' => array($vendorDir . '/nesbot/carbon/src/Carbon'),
+);

+ 57 - 0
htdocs/includes/webklex/php-imap/vendor/composer/autoload_real.php

@@ -0,0 +1,57 @@
+<?php
+
+// autoload_real.php @generated by Composer
+
+class ComposerAutoloaderInit4da13270269c89a28e472e1f7324e6d1
+{
+    private static $loader;
+
+    public static function loadClassLoader($class)
+    {
+        if ('Composer\Autoload\ClassLoader' === $class) {
+            require __DIR__ . '/ClassLoader.php';
+        }
+    }
+
+    /**
+     * @return \Composer\Autoload\ClassLoader
+     */
+    public static function getLoader()
+    {
+        if (null !== self::$loader) {
+            return self::$loader;
+        }
+
+        require __DIR__ . '/platform_check.php';
+
+        spl_autoload_register(array('ComposerAutoloaderInit4da13270269c89a28e472e1f7324e6d1', 'loadClassLoader'), true, true);
+        self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
+        spl_autoload_unregister(array('ComposerAutoloaderInit4da13270269c89a28e472e1f7324e6d1', 'loadClassLoader'));
+
+        require __DIR__ . '/autoload_static.php';
+        call_user_func(\Composer\Autoload\ComposerStaticInit4da13270269c89a28e472e1f7324e6d1::getInitializer($loader));
+
+        $loader->register(true);
+
+        $includeFiles = \Composer\Autoload\ComposerStaticInit4da13270269c89a28e472e1f7324e6d1::$files;
+        foreach ($includeFiles as $fileIdentifier => $file) {
+            composerRequire4da13270269c89a28e472e1f7324e6d1($fileIdentifier, $file);
+        }
+
+        return $loader;
+    }
+}
+
+/**
+ * @param string $fileIdentifier
+ * @param string $file
+ * @return void
+ */
+function composerRequire4da13270269c89a28e472e1f7324e6d1($fileIdentifier, $file)
+{
+    if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
+        $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
+
+        require $file;
+    }
+}

+ 623 - 0
htdocs/includes/webklex/php-imap/vendor/composer/autoload_static.php

@@ -0,0 +1,623 @@
+<?php
+
+// autoload_static.php @generated by Composer
+
+namespace Composer\Autoload;
+
+class ComposerStaticInit4da13270269c89a28e472e1f7324e6d1
+{
+    public static $files = array (
+        '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
+        'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
+        '60799491728b879e74601d83e38b2cad' => __DIR__ . '/..' . '/illuminate/collections/helpers.php',
+        '320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
+        '72579e7bd17821bb1321b87411366eae' => __DIR__ . '/..' . '/illuminate/support/helpers.php',
+        '6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
+    );
+
+    public static $prefixLengthsPsr4 = array (
+        'v' => 
+        array (
+            'voku\\' => 5,
+        ),
+        'p' => 
+        array (
+            'phpDocumentor\\Reflection\\' => 25,
+        ),
+        'W' => 
+        array (
+            'Webmozart\\Assert\\' => 17,
+            'Webklex\\PHPIMAP\\' => 16,
+        ),
+        'T' => 
+        array (
+            'Tests\\' => 6,
+        ),
+        'S' => 
+        array (
+            'Symfony\\Polyfill\\Php80\\' => 23,
+            'Symfony\\Polyfill\\Mbstring\\' => 26,
+            'Symfony\\Polyfill\\Ctype\\' => 23,
+            'Symfony\\Contracts\\Translation\\' => 30,
+            'Symfony\\Component\\Yaml\\' => 23,
+            'Symfony\\Component\\Translation\\' => 30,
+            'Symfony\\Component\\HttpFoundation\\' => 33,
+        ),
+        'P' => 
+        array (
+            'Psr\\SimpleCache\\' => 16,
+            'Psr\\Container\\' => 14,
+            'Prophecy\\' => 9,
+        ),
+        'I' => 
+        array (
+            'Illuminate\\Support\\' => 19,
+            'Illuminate\\Pagination\\' => 22,
+            'Illuminate\\Contracts\\' => 21,
+        ),
+        'D' => 
+        array (
+            'Doctrine\\Instantiator\\' => 22,
+            'Doctrine\\Inflector\\' => 19,
+        ),
+        'C' => 
+        array (
+            'Carbon\\' => 7,
+        ),
+    );
+
+    public static $prefixDirsPsr4 = array (
+        'voku\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/voku/portable-ascii/src/voku',
+        ),
+        'phpDocumentor\\Reflection\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/phpdocumentor/reflection-common/src',
+            1 => __DIR__ . '/..' . '/phpdocumentor/type-resolver/src',
+            2 => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src',
+        ),
+        'Webmozart\\Assert\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/webmozart/assert/src',
+        ),
+        'Webklex\\PHPIMAP\\' => 
+        array (
+            0 => __DIR__ . '/../..' . '/src',
+        ),
+        'Tests\\' => 
+        array (
+            0 => __DIR__ . '/../..' . '/tests',
+        ),
+        'Symfony\\Polyfill\\Php80\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/symfony/polyfill-php80',
+        ),
+        'Symfony\\Polyfill\\Mbstring\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring',
+        ),
+        'Symfony\\Polyfill\\Ctype\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/symfony/polyfill-ctype',
+        ),
+        'Symfony\\Contracts\\Translation\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/symfony/translation-contracts',
+        ),
+        'Symfony\\Component\\Yaml\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/symfony/yaml',
+        ),
+        'Symfony\\Component\\Translation\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/symfony/translation',
+        ),
+        'Symfony\\Component\\HttpFoundation\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/symfony/http-foundation',
+        ),
+        'Psr\\SimpleCache\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/psr/simple-cache/src',
+        ),
+        'Psr\\Container\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/psr/container/src',
+        ),
+        'Prophecy\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy',
+        ),
+        'Illuminate\\Support\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/illuminate/macroable',
+            1 => __DIR__ . '/..' . '/illuminate/collections',
+            2 => __DIR__ . '/..' . '/illuminate/support',
+        ),
+        'Illuminate\\Pagination\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/illuminate/pagination',
+        ),
+        'Illuminate\\Contracts\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/illuminate/contracts',
+        ),
+        'Doctrine\\Instantiator\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/doctrine/instantiator/src/Doctrine/Instantiator',
+        ),
+        'Doctrine\\Inflector\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/doctrine/inflector/lib/Doctrine/Inflector',
+        ),
+        'Carbon\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/nesbot/carbon/src/Carbon',
+        ),
+    );
+
+    public static $classMap = array (
+        'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
+        'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
+        'File_Iterator' => __DIR__ . '/..' . '/phpunit/php-file-iterator/src/Iterator.php',
+        'File_Iterator_Facade' => __DIR__ . '/..' . '/phpunit/php-file-iterator/src/Facade.php',
+        'File_Iterator_Factory' => __DIR__ . '/..' . '/phpunit/php-file-iterator/src/Factory.php',
+        'PHPUnit\\Framework\\Assert' => __DIR__ . '/..' . '/phpunit/phpunit/src/ForwardCompatibility/Assert.php',
+        'PHPUnit\\Framework\\AssertionFailedError' => __DIR__ . '/..' . '/phpunit/phpunit/src/ForwardCompatibility/AssertionFailedError.php',
+        'PHPUnit\\Framework\\BaseTestListener' => __DIR__ . '/..' . '/phpunit/phpunit/src/ForwardCompatibility/BaseTestListener.php',
+        'PHPUnit\\Framework\\Test' => __DIR__ . '/..' . '/phpunit/phpunit/src/ForwardCompatibility/Test.php',
+        'PHPUnit\\Framework\\TestCase' => __DIR__ . '/..' . '/phpunit/phpunit/src/ForwardCompatibility/TestCase.php',
+        'PHPUnit\\Framework\\TestListener' => __DIR__ . '/..' . '/phpunit/phpunit/src/ForwardCompatibility/TestListener.php',
+        'PHPUnit\\Framework\\TestSuite' => __DIR__ . '/..' . '/phpunit/phpunit/src/ForwardCompatibility/TestSuite.php',
+        'PHPUnit_Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/Exception.php',
+        'PHPUnit_Extensions_GroupTestSuite' => __DIR__ . '/..' . '/phpunit/phpunit/src/Extensions/GroupTestSuite.php',
+        'PHPUnit_Extensions_PhptTestCase' => __DIR__ . '/..' . '/phpunit/phpunit/src/Extensions/PhptTestCase.php',
+        'PHPUnit_Extensions_PhptTestSuite' => __DIR__ . '/..' . '/phpunit/phpunit/src/Extensions/PhptTestSuite.php',
+        'PHPUnit_Extensions_RepeatedTest' => __DIR__ . '/..' . '/phpunit/phpunit/src/Extensions/RepeatedTest.php',
+        'PHPUnit_Extensions_TestDecorator' => __DIR__ . '/..' . '/phpunit/phpunit/src/Extensions/TestDecorator.php',
+        'PHPUnit_Extensions_TicketListener' => __DIR__ . '/..' . '/phpunit/phpunit/src/Extensions/TicketListener.php',
+        'PHPUnit_Framework_Assert' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Assert.php',
+        'PHPUnit_Framework_AssertionFailedError' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/AssertionFailedError.php',
+        'PHPUnit_Framework_BaseTestListener' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/BaseTestListener.php',
+        'PHPUnit_Framework_CodeCoverageException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/CodeCoverageException.php',
+        'PHPUnit_Framework_Constraint' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint.php',
+        'PHPUnit_Framework_Constraint_And' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/And.php',
+        'PHPUnit_Framework_Constraint_ArrayHasKey' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/ArrayHasKey.php',
+        'PHPUnit_Framework_Constraint_ArraySubset' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/ArraySubset.php',
+        'PHPUnit_Framework_Constraint_Attribute' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Attribute.php',
+        'PHPUnit_Framework_Constraint_Callback' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Callback.php',
+        'PHPUnit_Framework_Constraint_ClassHasAttribute' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/ClassHasAttribute.php',
+        'PHPUnit_Framework_Constraint_ClassHasStaticAttribute' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/ClassHasStaticAttribute.php',
+        'PHPUnit_Framework_Constraint_Composite' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Composite.php',
+        'PHPUnit_Framework_Constraint_Count' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Count.php',
+        'PHPUnit_Framework_Constraint_Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Exception.php',
+        'PHPUnit_Framework_Constraint_ExceptionCode' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/ExceptionCode.php',
+        'PHPUnit_Framework_Constraint_ExceptionMessage' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/ExceptionMessage.php',
+        'PHPUnit_Framework_Constraint_ExceptionMessageRegExp' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/ExceptionMessageRegExp.php',
+        'PHPUnit_Framework_Constraint_FileExists' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/FileExists.php',
+        'PHPUnit_Framework_Constraint_GreaterThan' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/GreaterThan.php',
+        'PHPUnit_Framework_Constraint_IsAnything' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsAnything.php',
+        'PHPUnit_Framework_Constraint_IsEmpty' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsEmpty.php',
+        'PHPUnit_Framework_Constraint_IsEqual' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsEqual.php',
+        'PHPUnit_Framework_Constraint_IsFalse' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsFalse.php',
+        'PHPUnit_Framework_Constraint_IsIdentical' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsIdentical.php',
+        'PHPUnit_Framework_Constraint_IsInstanceOf' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsInstanceOf.php',
+        'PHPUnit_Framework_Constraint_IsJson' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsJson.php',
+        'PHPUnit_Framework_Constraint_IsNull' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsNull.php',
+        'PHPUnit_Framework_Constraint_IsTrue' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsTrue.php',
+        'PHPUnit_Framework_Constraint_IsType' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsType.php',
+        'PHPUnit_Framework_Constraint_JsonMatches' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/JsonMatches.php',
+        'PHPUnit_Framework_Constraint_JsonMatches_ErrorMessageProvider' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/JsonMatches/ErrorMessageProvider.php',
+        'PHPUnit_Framework_Constraint_LessThan' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/LessThan.php',
+        'PHPUnit_Framework_Constraint_Not' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Not.php',
+        'PHPUnit_Framework_Constraint_ObjectHasAttribute' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/ObjectHasAttribute.php',
+        'PHPUnit_Framework_Constraint_Or' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Or.php',
+        'PHPUnit_Framework_Constraint_PCREMatch' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/PCREMatch.php',
+        'PHPUnit_Framework_Constraint_SameSize' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/SameSize.php',
+        'PHPUnit_Framework_Constraint_StringContains' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/StringContains.php',
+        'PHPUnit_Framework_Constraint_StringEndsWith' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/StringEndsWith.php',
+        'PHPUnit_Framework_Constraint_StringMatches' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/StringMatches.php',
+        'PHPUnit_Framework_Constraint_StringStartsWith' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/StringStartsWith.php',
+        'PHPUnit_Framework_Constraint_TraversableContains' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/TraversableContains.php',
+        'PHPUnit_Framework_Constraint_TraversableContainsOnly' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/TraversableContainsOnly.php',
+        'PHPUnit_Framework_Constraint_Xor' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Xor.php',
+        'PHPUnit_Framework_Error' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Error.php',
+        'PHPUnit_Framework_Error_Deprecated' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Error/Deprecated.php',
+        'PHPUnit_Framework_Error_Notice' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Error/Notice.php',
+        'PHPUnit_Framework_Error_Warning' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Error/Warning.php',
+        'PHPUnit_Framework_Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception.php',
+        'PHPUnit_Framework_ExceptionWrapper' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/ExceptionWrapper.php',
+        'PHPUnit_Framework_ExpectationFailedException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/ExpectationFailedException.php',
+        'PHPUnit_Framework_IncompleteTest' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/IncompleteTest.php',
+        'PHPUnit_Framework_IncompleteTestCase' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/IncompleteTestCase.php',
+        'PHPUnit_Framework_IncompleteTestError' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/IncompleteTestError.php',
+        'PHPUnit_Framework_InvalidCoversTargetError' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/InvalidCoversTargetError.php',
+        'PHPUnit_Framework_InvalidCoversTargetException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/InvalidCoversTargetException.php',
+        'PHPUnit_Framework_MockObject_BadMethodCallException' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Exception/BadMethodCallException.php',
+        'PHPUnit_Framework_MockObject_Builder_Identity' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/Identity.php',
+        'PHPUnit_Framework_MockObject_Builder_InvocationMocker' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/InvocationMocker.php',
+        'PHPUnit_Framework_MockObject_Builder_Match' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/Match.php',
+        'PHPUnit_Framework_MockObject_Builder_MethodNameMatch' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/MethodNameMatch.php',
+        'PHPUnit_Framework_MockObject_Builder_Namespace' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/Namespace.php',
+        'PHPUnit_Framework_MockObject_Builder_ParametersMatch' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/ParametersMatch.php',
+        'PHPUnit_Framework_MockObject_Builder_Stub' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/Stub.php',
+        'PHPUnit_Framework_MockObject_Exception' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Exception/Exception.php',
+        'PHPUnit_Framework_MockObject_Generator' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Generator.php',
+        'PHPUnit_Framework_MockObject_Invocation' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Invocation.php',
+        'PHPUnit_Framework_MockObject_InvocationMocker' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/InvocationMocker.php',
+        'PHPUnit_Framework_MockObject_Invocation_Object' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Invocation/Object.php',
+        'PHPUnit_Framework_MockObject_Invocation_Static' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Invocation/Static.php',
+        'PHPUnit_Framework_MockObject_Invokable' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Invokable.php',
+        'PHPUnit_Framework_MockObject_Matcher' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher.php',
+        'PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/AnyInvokedCount.php',
+        'PHPUnit_Framework_MockObject_Matcher_AnyParameters' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/AnyParameters.php',
+        'PHPUnit_Framework_MockObject_Matcher_ConsecutiveParameters' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/ConsecutiveParameters.php',
+        'PHPUnit_Framework_MockObject_Matcher_Invocation' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/Invocation.php',
+        'PHPUnit_Framework_MockObject_Matcher_InvokedAtIndex' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedAtIndex.php',
+        'PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastCount' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedAtLeastCount.php',
+        'PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastOnce' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedAtLeastOnce.php',
+        'PHPUnit_Framework_MockObject_Matcher_InvokedAtMostCount' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedAtMostCount.php',
+        'PHPUnit_Framework_MockObject_Matcher_InvokedCount' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedCount.php',
+        'PHPUnit_Framework_MockObject_Matcher_InvokedRecorder' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedRecorder.php',
+        'PHPUnit_Framework_MockObject_Matcher_MethodName' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/MethodName.php',
+        'PHPUnit_Framework_MockObject_Matcher_Parameters' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/Parameters.php',
+        'PHPUnit_Framework_MockObject_Matcher_StatelessInvocation' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/StatelessInvocation.php',
+        'PHPUnit_Framework_MockObject_MockBuilder' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/MockBuilder.php',
+        'PHPUnit_Framework_MockObject_MockObject' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/MockObject.php',
+        'PHPUnit_Framework_MockObject_RuntimeException' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Exception/RuntimeException.php',
+        'PHPUnit_Framework_MockObject_Stub' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub.php',
+        'PHPUnit_Framework_MockObject_Stub_ConsecutiveCalls' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ConsecutiveCalls.php',
+        'PHPUnit_Framework_MockObject_Stub_Exception' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/Exception.php',
+        'PHPUnit_Framework_MockObject_Stub_MatcherCollection' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/MatcherCollection.php',
+        'PHPUnit_Framework_MockObject_Stub_Return' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/Return.php',
+        'PHPUnit_Framework_MockObject_Stub_ReturnArgument' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ReturnArgument.php',
+        'PHPUnit_Framework_MockObject_Stub_ReturnCallback' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ReturnCallback.php',
+        'PHPUnit_Framework_MockObject_Stub_ReturnSelf' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ReturnSelf.php',
+        'PHPUnit_Framework_MockObject_Stub_ReturnValueMap' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ReturnValueMap.php',
+        'PHPUnit_Framework_MockObject_Verifiable' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Verifiable.php',
+        'PHPUnit_Framework_OutputError' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/OutputError.php',
+        'PHPUnit_Framework_RiskyTest' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/RiskyTest.php',
+        'PHPUnit_Framework_RiskyTestError' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/RiskyTestError.php',
+        'PHPUnit_Framework_SelfDescribing' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/SelfDescribing.php',
+        'PHPUnit_Framework_SkippedTest' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/SkippedTest.php',
+        'PHPUnit_Framework_SkippedTestCase' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/SkippedTestCase.php',
+        'PHPUnit_Framework_SkippedTestError' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/SkippedTestError.php',
+        'PHPUnit_Framework_SkippedTestSuiteError' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/SkippedTestSuiteError.php',
+        'PHPUnit_Framework_SyntheticError' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/SyntheticError.php',
+        'PHPUnit_Framework_Test' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Test.php',
+        'PHPUnit_Framework_TestCase' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/TestCase.php',
+        'PHPUnit_Framework_TestFailure' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/TestFailure.php',
+        'PHPUnit_Framework_TestListener' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/TestListener.php',
+        'PHPUnit_Framework_TestResult' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/TestResult.php',
+        'PHPUnit_Framework_TestSuite' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/TestSuite.php',
+        'PHPUnit_Framework_TestSuite_DataProvider' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/TestSuite/DataProvider.php',
+        'PHPUnit_Framework_UnintentionallyCoveredCodeError' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/UnintentionallyCoveredCodeError.php',
+        'PHPUnit_Framework_Warning' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Warning.php',
+        'PHPUnit_Runner_BaseTestRunner' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/BaseTestRunner.php',
+        'PHPUnit_Runner_Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Exception.php',
+        'PHPUnit_Runner_Filter_Factory' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Filter/Factory.php',
+        'PHPUnit_Runner_Filter_GroupFilterIterator' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Filter/Group.php',
+        'PHPUnit_Runner_Filter_Group_Exclude' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Filter/Group/Exclude.php',
+        'PHPUnit_Runner_Filter_Group_Include' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Filter/Group/Include.php',
+        'PHPUnit_Runner_Filter_Test' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Filter/Test.php',
+        'PHPUnit_Runner_StandardTestSuiteLoader' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/StandardTestSuiteLoader.php',
+        'PHPUnit_Runner_TestSuiteLoader' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/TestSuiteLoader.php',
+        'PHPUnit_Runner_Version' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Version.php',
+        'PHPUnit_TextUI_Command' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/Command.php',
+        'PHPUnit_TextUI_ResultPrinter' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/ResultPrinter.php',
+        'PHPUnit_TextUI_TestRunner' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/TestRunner.php',
+        'PHPUnit_Util_Blacklist' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Blacklist.php',
+        'PHPUnit_Util_Configuration' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Configuration.php',
+        'PHPUnit_Util_ErrorHandler' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/ErrorHandler.php',
+        'PHPUnit_Util_Fileloader' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Fileloader.php',
+        'PHPUnit_Util_Filesystem' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Filesystem.php',
+        'PHPUnit_Util_Filter' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Filter.php',
+        'PHPUnit_Util_Getopt' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Getopt.php',
+        'PHPUnit_Util_GlobalState' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/GlobalState.php',
+        'PHPUnit_Util_InvalidArgumentHelper' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/InvalidArgumentHelper.php',
+        'PHPUnit_Util_Log_JSON' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Log/JSON.php',
+        'PHPUnit_Util_Log_JUnit' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Log/JUnit.php',
+        'PHPUnit_Util_Log_TAP' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Log/TAP.php',
+        'PHPUnit_Util_PHP' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/PHP.php',
+        'PHPUnit_Util_PHP_Default' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/PHP/Default.php',
+        'PHPUnit_Util_PHP_Windows' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/PHP/Windows.php',
+        'PHPUnit_Util_Printer' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Printer.php',
+        'PHPUnit_Util_Regex' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Regex.php',
+        'PHPUnit_Util_String' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/String.php',
+        'PHPUnit_Util_Test' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Test.php',
+        'PHPUnit_Util_TestDox_NamePrettifier' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/TestDox/NamePrettifier.php',
+        'PHPUnit_Util_TestDox_ResultPrinter' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/TestDox/ResultPrinter.php',
+        'PHPUnit_Util_TestDox_ResultPrinter_HTML' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/TestDox/ResultPrinter/HTML.php',
+        'PHPUnit_Util_TestDox_ResultPrinter_Text' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/TestDox/ResultPrinter/Text.php',
+        'PHPUnit_Util_TestSuiteIterator' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/TestSuiteIterator.php',
+        'PHPUnit_Util_Type' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Type.php',
+        'PHPUnit_Util_XML' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/XML.php',
+        'PHP_CodeCoverage' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage.php',
+        'PHP_CodeCoverage_Driver' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage/Driver.php',
+        'PHP_CodeCoverage_Driver_HHVM' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage/Driver/HHVM.php',
+        'PHP_CodeCoverage_Driver_PHPDBG' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage/Driver/PHPDBG.php',
+        'PHP_CodeCoverage_Driver_Xdebug' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage/Driver/Xdebug.php',
+        'PHP_CodeCoverage_Exception' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage/Exception.php',
+        'PHP_CodeCoverage_Exception_UnintentionallyCoveredCode' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage/Exception/UnintentionallyCoveredCode.php',
+        'PHP_CodeCoverage_Filter' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage/Filter.php',
+        'PHP_CodeCoverage_Report_Clover' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage/Report/Clover.php',
+        'PHP_CodeCoverage_Report_Crap4j' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage/Report/Crap4j.php',
+        'PHP_CodeCoverage_Report_Factory' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage/Report/Factory.php',
+        'PHP_CodeCoverage_Report_HTML' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage/Report/HTML.php',
+        'PHP_CodeCoverage_Report_HTML_Renderer' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage/Report/HTML/Renderer.php',
+        'PHP_CodeCoverage_Report_HTML_Renderer_Dashboard' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage/Report/HTML/Renderer/Dashboard.php',
+        'PHP_CodeCoverage_Report_HTML_Renderer_Directory' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage/Report/HTML/Renderer/Directory.php',
+        'PHP_CodeCoverage_Report_HTML_Renderer_File' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage/Report/HTML/Renderer/File.php',
+        'PHP_CodeCoverage_Report_Node' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage/Report/Node.php',
+        'PHP_CodeCoverage_Report_Node_Directory' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage/Report/Node/Directory.php',
+        'PHP_CodeCoverage_Report_Node_File' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage/Report/Node/File.php',
+        'PHP_CodeCoverage_Report_Node_Iterator' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage/Report/Node/Iterator.php',
+        'PHP_CodeCoverage_Report_PHP' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage/Report/PHP.php',
+        'PHP_CodeCoverage_Report_Text' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage/Report/Text.php',
+        'PHP_CodeCoverage_Report_XML' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage/Report/XML.php',
+        'PHP_CodeCoverage_Report_XML_Directory' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage/Report/XML/Directory.php',
+        'PHP_CodeCoverage_Report_XML_File' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage/Report/XML/File.php',
+        'PHP_CodeCoverage_Report_XML_File_Coverage' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage/Report/XML/File/Coverage.php',
+        'PHP_CodeCoverage_Report_XML_File_Method' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage/Report/XML/File/Method.php',
+        'PHP_CodeCoverage_Report_XML_File_Report' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage/Report/XML/File/Report.php',
+        'PHP_CodeCoverage_Report_XML_File_Unit' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage/Report/XML/File/Unit.php',
+        'PHP_CodeCoverage_Report_XML_Node' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage/Report/XML/Node.php',
+        'PHP_CodeCoverage_Report_XML_Project' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage/Report/XML/Project.php',
+        'PHP_CodeCoverage_Report_XML_Tests' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage/Report/XML/Tests.php',
+        'PHP_CodeCoverage_Report_XML_Totals' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage/Report/XML/Totals.php',
+        'PHP_CodeCoverage_Util' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage/Util.php',
+        'PHP_CodeCoverage_Util_InvalidArgumentHelper' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage/Util/InvalidArgumentHelper.php',
+        'PHP_Timer' => __DIR__ . '/..' . '/phpunit/php-timer/src/Timer.php',
+        'PHP_Token' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_TokenWithScope' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_TokenWithScopeAndVisibility' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_ABSTRACT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_AMPERSAND' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_AND_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_ARRAY' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_ARRAY_CAST' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_AS' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_ASYNC' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_AT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_AWAIT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_BACKTICK' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_BAD_CHARACTER' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_BOOLEAN_AND' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_BOOLEAN_OR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_BOOL_CAST' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_BREAK' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_CALLABLE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_CARET' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_CASE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_CATCH' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_CHARACTER' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_CLASS' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_CLASS_C' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_CLASS_NAME_CONSTANT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_CLONE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_CLOSE_BRACKET' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_CLOSE_CURLY' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_CLOSE_SQUARE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_CLOSE_TAG' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_COALESCE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_COLON' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_COMMA' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_COMMENT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_COMPILER_HALT_OFFSET' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_CONCAT_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_CONST' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_CONSTANT_ENCAPSED_STRING' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_CONTINUE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_CURLY_OPEN' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_DEC' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_DECLARE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_DEFAULT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_DIR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_DIV' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_DIV_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_DNUMBER' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_DO' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_DOC_COMMENT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_DOLLAR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_DOLLAR_OPEN_CURLY_BRACES' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_DOT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_DOUBLE_ARROW' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_DOUBLE_CAST' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_DOUBLE_COLON' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_DOUBLE_QUOTES' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_ECHO' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_ELLIPSIS' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_ELSE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_ELSEIF' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_EMPTY' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_ENCAPSED_AND_WHITESPACE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_ENDDECLARE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_ENDFOR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_ENDFOREACH' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_ENDIF' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_ENDSWITCH' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_ENDWHILE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_END_HEREDOC' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_ENUM' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_EQUALS' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_EVAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_EXCLAMATION_MARK' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_EXIT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_EXTENDS' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_FILE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_FINAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_FINALLY' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_FOR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_FOREACH' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_FUNCTION' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_FUNC_C' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_GLOBAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_GOTO' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_GT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_HALT_COMPILER' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_IF' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_IMPLEMENTS' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_IN' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_INC' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_INCLUDE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_INCLUDE_ONCE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_INLINE_HTML' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_INSTANCEOF' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_INSTEADOF' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_INTERFACE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_INT_CAST' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_ISSET' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_IS_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_IS_GREATER_OR_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_IS_IDENTICAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_IS_NOT_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_IS_NOT_IDENTICAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_IS_SMALLER_OR_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_Includes' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_JOIN' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_LAMBDA_ARROW' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_LAMBDA_CP' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_LAMBDA_OP' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_LINE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_LIST' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_LNUMBER' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_LOGICAL_AND' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_LOGICAL_OR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_LOGICAL_XOR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_LT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_METHOD_C' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_MINUS' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_MINUS_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_MOD_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_MULT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_MUL_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_NAMESPACE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_NEW' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_NS_C' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_NS_SEPARATOR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_NULLSAFE_OBJECT_OPERATOR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_NUM_STRING' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_OBJECT_CAST' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_OBJECT_OPERATOR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_ONUMBER' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_OPEN_BRACKET' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_OPEN_CURLY' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_OPEN_SQUARE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_OPEN_TAG' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_OPEN_TAG_WITH_ECHO' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_OR_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_PAAMAYIM_NEKUDOTAYIM' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_PERCENT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_PIPE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_PLUS' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_PLUS_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_POW' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_POW_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_PRINT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_PRIVATE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_PROTECTED' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_PUBLIC' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_QUESTION_MARK' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_REQUIRE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_REQUIRE_ONCE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_RETURN' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_SEMICOLON' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_SHAPE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_SL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_SL_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_SPACESHIP' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_SR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_SR_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_START_HEREDOC' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_STATIC' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_STRING' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_STRING_CAST' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_STRING_VARNAME' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_SUPER' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_SWITCH' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_Stream' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token/Stream.php',
+        'PHP_Token_Stream_CachingFactory' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token/Stream/CachingFactory.php',
+        'PHP_Token_THROW' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_TILDE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_TRAIT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_TRAIT_C' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_TRY' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_TYPE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_TYPELIST_GT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_TYPELIST_LT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_UNSET' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_UNSET_CAST' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_USE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_USE_FUNCTION' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_VAR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_VARIABLE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_WHERE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_WHILE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_WHITESPACE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_XHP_ATTRIBUTE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_XHP_CATEGORY' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_XHP_CATEGORY_LABEL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_XHP_CHILDREN' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_XHP_LABEL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_XHP_REQUIRED' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_XHP_TAG_GT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_XHP_TAG_LT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_XHP_TEXT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_XOR_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_YIELD' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PHP_Token_YIELD_FROM' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
+        'PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
+        'SebastianBergmann\\Comparator\\ArrayComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/ArrayComparator.php',
+        'SebastianBergmann\\Comparator\\Comparator' => __DIR__ . '/..' . '/sebastian/comparator/src/Comparator.php',
+        'SebastianBergmann\\Comparator\\ComparisonFailure' => __DIR__ . '/..' . '/sebastian/comparator/src/ComparisonFailure.php',
+        'SebastianBergmann\\Comparator\\DOMNodeComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/DOMNodeComparator.php',
+        'SebastianBergmann\\Comparator\\DateTimeComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/DateTimeComparator.php',
+        'SebastianBergmann\\Comparator\\DoubleComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/DoubleComparator.php',
+        'SebastianBergmann\\Comparator\\ExceptionComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/ExceptionComparator.php',
+        'SebastianBergmann\\Comparator\\Factory' => __DIR__ . '/..' . '/sebastian/comparator/src/Factory.php',
+        'SebastianBergmann\\Comparator\\MockObjectComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/MockObjectComparator.php',
+        'SebastianBergmann\\Comparator\\NumericComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/NumericComparator.php',
+        'SebastianBergmann\\Comparator\\ObjectComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/ObjectComparator.php',
+        'SebastianBergmann\\Comparator\\ResourceComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/ResourceComparator.php',
+        'SebastianBergmann\\Comparator\\ScalarComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/ScalarComparator.php',
+        'SebastianBergmann\\Comparator\\SplObjectStorageComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/SplObjectStorageComparator.php',
+        'SebastianBergmann\\Comparator\\TypeComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/TypeComparator.php',
+        'SebastianBergmann\\Diff\\Chunk' => __DIR__ . '/..' . '/sebastian/diff/src/Chunk.php',
+        'SebastianBergmann\\Diff\\Diff' => __DIR__ . '/..' . '/sebastian/diff/src/Diff.php',
+        'SebastianBergmann\\Diff\\Differ' => __DIR__ . '/..' . '/sebastian/diff/src/Differ.php',
+        'SebastianBergmann\\Diff\\LCS\\LongestCommonSubsequence' => __DIR__ . '/..' . '/sebastian/diff/src/LCS/LongestCommonSubsequence.php',
+        'SebastianBergmann\\Diff\\LCS\\MemoryEfficientImplementation' => __DIR__ . '/..' . '/sebastian/diff/src/LCS/MemoryEfficientLongestCommonSubsequenceImplementation.php',
+        'SebastianBergmann\\Diff\\LCS\\TimeEfficientImplementation' => __DIR__ . '/..' . '/sebastian/diff/src/LCS/TimeEfficientLongestCommonSubsequenceImplementation.php',
+        'SebastianBergmann\\Diff\\Line' => __DIR__ . '/..' . '/sebastian/diff/src/Line.php',
+        'SebastianBergmann\\Diff\\Parser' => __DIR__ . '/..' . '/sebastian/diff/src/Parser.php',
+        'SebastianBergmann\\Environment\\Console' => __DIR__ . '/..' . '/sebastian/environment/src/Console.php',
+        'SebastianBergmann\\Environment\\Runtime' => __DIR__ . '/..' . '/sebastian/environment/src/Runtime.php',
+        'SebastianBergmann\\Exporter\\Exporter' => __DIR__ . '/..' . '/sebastian/exporter/src/Exporter.php',
+        'SebastianBergmann\\GlobalState\\Blacklist' => __DIR__ . '/..' . '/sebastian/global-state/src/Blacklist.php',
+        'SebastianBergmann\\GlobalState\\CodeExporter' => __DIR__ . '/..' . '/sebastian/global-state/src/CodeExporter.php',
+        'SebastianBergmann\\GlobalState\\Exception' => __DIR__ . '/..' . '/sebastian/global-state/src/Exception.php',
+        'SebastianBergmann\\GlobalState\\Restorer' => __DIR__ . '/..' . '/sebastian/global-state/src/Restorer.php',
+        'SebastianBergmann\\GlobalState\\RuntimeException' => __DIR__ . '/..' . '/sebastian/global-state/src/RuntimeException.php',
+        'SebastianBergmann\\GlobalState\\Snapshot' => __DIR__ . '/..' . '/sebastian/global-state/src/Snapshot.php',
+        'SebastianBergmann\\RecursionContext\\Context' => __DIR__ . '/..' . '/sebastian/recursion-context/src/Context.php',
+        'SebastianBergmann\\RecursionContext\\Exception' => __DIR__ . '/..' . '/sebastian/recursion-context/src/Exception.php',
+        'SebastianBergmann\\RecursionContext\\InvalidArgumentException' => __DIR__ . '/..' . '/sebastian/recursion-context/src/InvalidArgumentException.php',
+        'SebastianBergmann\\Version' => __DIR__ . '/..' . '/sebastian/version/src/Version.php',
+        'Stringable' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
+        'Text_Template' => __DIR__ . '/..' . '/phpunit/php-text-template/src/Template.php',
+        'UnhandledMatchError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
+        'ValueError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
+    );
+
+    public static function getInitializer(ClassLoader $loader)
+    {
+        return \Closure::bind(function () use ($loader) {
+            $loader->prefixLengthsPsr4 = ComposerStaticInit4da13270269c89a28e472e1f7324e6d1::$prefixLengthsPsr4;
+            $loader->prefixDirsPsr4 = ComposerStaticInit4da13270269c89a28e472e1f7324e6d1::$prefixDirsPsr4;
+            $loader->classMap = ComposerStaticInit4da13270269c89a28e472e1f7324e6d1::$classMap;
+
+        }, null, ClassLoader::class);
+    }
+}

+ 2565 - 0
htdocs/includes/webklex/php-imap/vendor/composer/installed.json

@@ -0,0 +1,2565 @@
+{
+    "packages": [
+        {
+            "name": "doctrine/inflector",
+            "version": "2.0.4",
+            "version_normalized": "2.0.4.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/doctrine/inflector.git",
+                "reference": "8b7ff3e4b7de6b2c84da85637b59fd2880ecaa89"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/doctrine/inflector/zipball/8b7ff3e4b7de6b2c84da85637b59fd2880ecaa89",
+                "reference": "8b7ff3e4b7de6b2c84da85637b59fd2880ecaa89",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2 || ^8.0"
+            },
+            "require-dev": {
+                "doctrine/coding-standard": "^8.2",
+                "phpstan/phpstan": "^0.12",
+                "phpstan/phpstan-phpunit": "^0.12",
+                "phpstan/phpstan-strict-rules": "^0.12",
+                "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
+                "vimeo/psalm": "^4.10"
+            },
+            "time": "2021-10-22T20:16:43+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Doctrine\\Inflector\\": "lib/Doctrine/Inflector"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Guilherme Blanco",
+                    "email": "guilhermeblanco@gmail.com"
+                },
+                {
+                    "name": "Roman Borschel",
+                    "email": "roman@code-factory.org"
+                },
+                {
+                    "name": "Benjamin Eberlei",
+                    "email": "kontakt@beberlei.de"
+                },
+                {
+                    "name": "Jonathan Wage",
+                    "email": "jonwage@gmail.com"
+                },
+                {
+                    "name": "Johannes Schmitt",
+                    "email": "schmittjoh@gmail.com"
+                }
+            ],
+            "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.",
+            "homepage": "https://www.doctrine-project.org/projects/inflector.html",
+            "keywords": [
+                "inflection",
+                "inflector",
+                "lowercase",
+                "manipulation",
+                "php",
+                "plural",
+                "singular",
+                "strings",
+                "uppercase",
+                "words"
+            ],
+            "support": {
+                "issues": "https://github.com/doctrine/inflector/issues",
+                "source": "https://github.com/doctrine/inflector/tree/2.0.4"
+            },
+            "funding": [
+                {
+                    "url": "https://www.doctrine-project.org/sponsorship.html",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://www.patreon.com/phpdoctrine",
+                    "type": "patreon"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../doctrine/inflector"
+        },
+        {
+            "name": "doctrine/instantiator",
+            "version": "1.4.1",
+            "version_normalized": "1.4.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/doctrine/instantiator.git",
+                "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc",
+                "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1 || ^8.0"
+            },
+            "require-dev": {
+                "doctrine/coding-standard": "^9",
+                "ext-pdo": "*",
+                "ext-phar": "*",
+                "phpbench/phpbench": "^0.16 || ^1",
+                "phpstan/phpstan": "^1.4",
+                "phpstan/phpstan-phpunit": "^1",
+                "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
+                "vimeo/psalm": "^4.22"
+            },
+            "time": "2022-03-03T08:28:38+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Marco Pivetta",
+                    "email": "ocramius@gmail.com",
+                    "homepage": "https://ocramius.github.io/"
+                }
+            ],
+            "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
+            "homepage": "https://www.doctrine-project.org/projects/instantiator.html",
+            "keywords": [
+                "constructor",
+                "instantiate"
+            ],
+            "support": {
+                "issues": "https://github.com/doctrine/instantiator/issues",
+                "source": "https://github.com/doctrine/instantiator/tree/1.4.1"
+            },
+            "funding": [
+                {
+                    "url": "https://www.doctrine-project.org/sponsorship.html",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://www.patreon.com/phpdoctrine",
+                    "type": "patreon"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../doctrine/instantiator"
+        },
+        {
+            "name": "illuminate/collections",
+            "version": "v8.83.23",
+            "version_normalized": "8.83.23.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/illuminate/collections.git",
+                "reference": "705a4e1ef93cd492c45b9b3e7911cccc990a07f4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/illuminate/collections/zipball/705a4e1ef93cd492c45b9b3e7911cccc990a07f4",
+                "reference": "705a4e1ef93cd492c45b9b3e7911cccc990a07f4",
+                "shasum": ""
+            },
+            "require": {
+                "illuminate/contracts": "^8.0",
+                "illuminate/macroable": "^8.0",
+                "php": "^7.3|^8.0"
+            },
+            "suggest": {
+                "symfony/var-dumper": "Required to use the dump method (^5.4)."
+            },
+            "time": "2022-06-23T15:29:49+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "8.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "files": [
+                    "helpers.php"
+                ],
+                "psr-4": {
+                    "Illuminate\\Support\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Taylor Otwell",
+                    "email": "taylor@laravel.com"
+                }
+            ],
+            "description": "The Illuminate Collections package.",
+            "homepage": "https://laravel.com",
+            "support": {
+                "issues": "https://github.com/laravel/framework/issues",
+                "source": "https://github.com/laravel/framework"
+            },
+            "install-path": "../illuminate/collections"
+        },
+        {
+            "name": "illuminate/contracts",
+            "version": "v8.83.23",
+            "version_normalized": "8.83.23.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/illuminate/contracts.git",
+                "reference": "5e0fd287a1b22a6b346a9f7cd484d8cf0234585d"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/illuminate/contracts/zipball/5e0fd287a1b22a6b346a9f7cd484d8cf0234585d",
+                "reference": "5e0fd287a1b22a6b346a9f7cd484d8cf0234585d",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.3|^8.0",
+                "psr/container": "^1.0",
+                "psr/simple-cache": "^1.0"
+            },
+            "time": "2022-01-13T14:47:47+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "8.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Illuminate\\Contracts\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Taylor Otwell",
+                    "email": "taylor@laravel.com"
+                }
+            ],
+            "description": "The Illuminate Contracts package.",
+            "homepage": "https://laravel.com",
+            "support": {
+                "issues": "https://github.com/laravel/framework/issues",
+                "source": "https://github.com/laravel/framework"
+            },
+            "install-path": "../illuminate/contracts"
+        },
+        {
+            "name": "illuminate/macroable",
+            "version": "v8.83.23",
+            "version_normalized": "8.83.23.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/illuminate/macroable.git",
+                "reference": "aed81891a6e046fdee72edd497f822190f61c162"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/illuminate/macroable/zipball/aed81891a6e046fdee72edd497f822190f61c162",
+                "reference": "aed81891a6e046fdee72edd497f822190f61c162",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.3|^8.0"
+            },
+            "time": "2021-11-16T13:57:03+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "8.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Illuminate\\Support\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Taylor Otwell",
+                    "email": "taylor@laravel.com"
+                }
+            ],
+            "description": "The Illuminate Macroable package.",
+            "homepage": "https://laravel.com",
+            "support": {
+                "issues": "https://github.com/laravel/framework/issues",
+                "source": "https://github.com/laravel/framework"
+            },
+            "install-path": "../illuminate/macroable"
+        },
+        {
+            "name": "illuminate/pagination",
+            "version": "v8.83.23",
+            "version_normalized": "8.83.23.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/illuminate/pagination.git",
+                "reference": "16fe8dc35f9d18c58a3471469af656a02e9ab692"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/illuminate/pagination/zipball/16fe8dc35f9d18c58a3471469af656a02e9ab692",
+                "reference": "16fe8dc35f9d18c58a3471469af656a02e9ab692",
+                "shasum": ""
+            },
+            "require": {
+                "ext-json": "*",
+                "illuminate/collections": "^8.0",
+                "illuminate/contracts": "^8.0",
+                "illuminate/support": "^8.0",
+                "php": "^7.3|^8.0"
+            },
+            "time": "2022-06-27T13:26:06+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "8.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Illuminate\\Pagination\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Taylor Otwell",
+                    "email": "taylor@laravel.com"
+                }
+            ],
+            "description": "The Illuminate Pagination package.",
+            "homepage": "https://laravel.com",
+            "support": {
+                "issues": "https://github.com/laravel/framework/issues",
+                "source": "https://github.com/laravel/framework"
+            },
+            "install-path": "../illuminate/pagination"
+        },
+        {
+            "name": "illuminate/support",
+            "version": "v8.83.23",
+            "version_normalized": "8.83.23.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/illuminate/support.git",
+                "reference": "c3d643e77082786ae8a51502c757e9b1a3ee254e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/illuminate/support/zipball/c3d643e77082786ae8a51502c757e9b1a3ee254e",
+                "reference": "c3d643e77082786ae8a51502c757e9b1a3ee254e",
+                "shasum": ""
+            },
+            "require": {
+                "doctrine/inflector": "^1.4|^2.0",
+                "ext-json": "*",
+                "ext-mbstring": "*",
+                "illuminate/collections": "^8.0",
+                "illuminate/contracts": "^8.0",
+                "illuminate/macroable": "^8.0",
+                "nesbot/carbon": "^2.53.1",
+                "php": "^7.3|^8.0",
+                "voku/portable-ascii": "^1.6.1"
+            },
+            "conflict": {
+                "tightenco/collect": "<5.5.33"
+            },
+            "suggest": {
+                "illuminate/filesystem": "Required to use the composer class (^8.0).",
+                "league/commonmark": "Required to use Str::markdown() and Stringable::markdown() (^1.3|^2.0.2).",
+                "ramsey/uuid": "Required to use Str::uuid() (^4.2.2).",
+                "symfony/process": "Required to use the composer class (^5.4).",
+                "symfony/var-dumper": "Required to use the dd function (^5.4).",
+                "vlucas/phpdotenv": "Required to use the Env class and env helper (^5.4.1)."
+            },
+            "time": "2022-06-27T13:26:30+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "8.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "files": [
+                    "helpers.php"
+                ],
+                "psr-4": {
+                    "Illuminate\\Support\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Taylor Otwell",
+                    "email": "taylor@laravel.com"
+                }
+            ],
+            "description": "The Illuminate Support package.",
+            "homepage": "https://laravel.com",
+            "support": {
+                "issues": "https://github.com/laravel/framework/issues",
+                "source": "https://github.com/laravel/framework"
+            },
+            "install-path": "../illuminate/support"
+        },
+        {
+            "name": "nesbot/carbon",
+            "version": "2.61.0",
+            "version_normalized": "2.61.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/briannesbitt/Carbon.git",
+                "reference": "bdf4f4fe3a3eac4de84dbec0738082a862c68ba6"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/bdf4f4fe3a3eac4de84dbec0738082a862c68ba6",
+                "reference": "bdf4f4fe3a3eac4de84dbec0738082a862c68ba6",
+                "shasum": ""
+            },
+            "require": {
+                "ext-json": "*",
+                "php": "^7.1.8 || ^8.0",
+                "symfony/polyfill-mbstring": "^1.0",
+                "symfony/polyfill-php80": "^1.16",
+                "symfony/translation": "^3.4 || ^4.0 || ^5.0 || ^6.0"
+            },
+            "require-dev": {
+                "doctrine/dbal": "^2.0 || ^3.0",
+                "doctrine/orm": "^2.7",
+                "friendsofphp/php-cs-fixer": "^3.0",
+                "kylekatarnls/multi-tester": "^2.0",
+                "ondrejmirtes/better-reflection": "*",
+                "phpmd/phpmd": "^2.9",
+                "phpstan/extension-installer": "^1.0",
+                "phpstan/phpstan": "^0.12.99 || ^1.7.14",
+                "phpunit/php-file-iterator": "^2.0.5 || ^3.0.6",
+                "phpunit/phpunit": "^7.5.20 || ^8.5.26 || ^9.5.20",
+                "squizlabs/php_codesniffer": "^3.4"
+            },
+            "time": "2022-08-06T12:41:24+00:00",
+            "bin": [
+                "bin/carbon"
+            ],
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-3.x": "3.x-dev",
+                    "dev-master": "2.x-dev"
+                },
+                "laravel": {
+                    "providers": [
+                        "Carbon\\Laravel\\ServiceProvider"
+                    ]
+                },
+                "phpstan": {
+                    "includes": [
+                        "extension.neon"
+                    ]
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Carbon\\": "src/Carbon/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Brian Nesbitt",
+                    "email": "brian@nesbot.com",
+                    "homepage": "https://markido.com"
+                },
+                {
+                    "name": "kylekatarnls",
+                    "homepage": "https://github.com/kylekatarnls"
+                }
+            ],
+            "description": "An API extension for DateTime that supports 281 different languages.",
+            "homepage": "https://carbon.nesbot.com",
+            "keywords": [
+                "date",
+                "datetime",
+                "time"
+            ],
+            "support": {
+                "docs": "https://carbon.nesbot.com/docs",
+                "issues": "https://github.com/briannesbitt/Carbon/issues",
+                "source": "https://github.com/briannesbitt/Carbon"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sponsors/kylekatarnls",
+                    "type": "github"
+                },
+                {
+                    "url": "https://opencollective.com/Carbon#sponsor",
+                    "type": "opencollective"
+                },
+                {
+                    "url": "https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../nesbot/carbon"
+        },
+        {
+            "name": "phpdocumentor/reflection-common",
+            "version": "2.2.0",
+            "version_normalized": "2.2.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpDocumentor/ReflectionCommon.git",
+                "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b",
+                "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2 || ^8.0"
+            },
+            "time": "2020-06-27T09:03:43+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-2.x": "2.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "phpDocumentor\\Reflection\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Jaap van Otterdijk",
+                    "email": "opensource@ijaap.nl"
+                }
+            ],
+            "description": "Common reflection classes used by phpdocumentor to reflect the code structure",
+            "homepage": "http://www.phpdoc.org",
+            "keywords": [
+                "FQSEN",
+                "phpDocumentor",
+                "phpdoc",
+                "reflection",
+                "static analysis"
+            ],
+            "support": {
+                "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues",
+                "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x"
+            },
+            "install-path": "../phpdocumentor/reflection-common"
+        },
+        {
+            "name": "phpdocumentor/reflection-docblock",
+            "version": "5.3.0",
+            "version_normalized": "5.3.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
+                "reference": "622548b623e81ca6d78b721c5e029f4ce664f170"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170",
+                "reference": "622548b623e81ca6d78b721c5e029f4ce664f170",
+                "shasum": ""
+            },
+            "require": {
+                "ext-filter": "*",
+                "php": "^7.2 || ^8.0",
+                "phpdocumentor/reflection-common": "^2.2",
+                "phpdocumentor/type-resolver": "^1.3",
+                "webmozart/assert": "^1.9.1"
+            },
+            "require-dev": {
+                "mockery/mockery": "~1.3.2",
+                "psalm/phar": "^4.8"
+            },
+            "time": "2021-10-19T17:43:47+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "5.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "phpDocumentor\\Reflection\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Mike van Riel",
+                    "email": "me@mikevanriel.com"
+                },
+                {
+                    "name": "Jaap van Otterdijk",
+                    "email": "account@ijaap.nl"
+                }
+            ],
+            "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
+            "support": {
+                "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues",
+                "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0"
+            },
+            "install-path": "../phpdocumentor/reflection-docblock"
+        },
+        {
+            "name": "phpdocumentor/type-resolver",
+            "version": "1.6.1",
+            "version_normalized": "1.6.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpDocumentor/TypeResolver.git",
+                "reference": "77a32518733312af16a44300404e945338981de3"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/77a32518733312af16a44300404e945338981de3",
+                "reference": "77a32518733312af16a44300404e945338981de3",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2 || ^8.0",
+                "phpdocumentor/reflection-common": "^2.0"
+            },
+            "require-dev": {
+                "ext-tokenizer": "*",
+                "psalm/phar": "^4.8"
+            },
+            "time": "2022-03-15T21:29:03+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-1.x": "1.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "phpDocumentor\\Reflection\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Mike van Riel",
+                    "email": "me@mikevanriel.com"
+                }
+            ],
+            "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
+            "support": {
+                "issues": "https://github.com/phpDocumentor/TypeResolver/issues",
+                "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.1"
+            },
+            "install-path": "../phpdocumentor/type-resolver"
+        },
+        {
+            "name": "phpspec/prophecy",
+            "version": "v1.10.3",
+            "version_normalized": "1.10.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpspec/prophecy.git",
+                "reference": "451c3cd1418cf640de218914901e51b064abb093"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093",
+                "reference": "451c3cd1418cf640de218914901e51b064abb093",
+                "shasum": ""
+            },
+            "require": {
+                "doctrine/instantiator": "^1.0.2",
+                "php": "^5.3|^7.0",
+                "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0",
+                "sebastian/comparator": "^1.2.3|^2.0|^3.0|^4.0",
+                "sebastian/recursion-context": "^1.0|^2.0|^3.0|^4.0"
+            },
+            "require-dev": {
+                "phpspec/phpspec": "^2.5 || ^3.2",
+                "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1"
+            },
+            "time": "2020-03-05T15:02:03+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.10.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Prophecy\\": "src/Prophecy"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Konstantin Kudryashov",
+                    "email": "ever.zet@gmail.com",
+                    "homepage": "http://everzet.com"
+                },
+                {
+                    "name": "Marcello Duarte",
+                    "email": "marcello.duarte@gmail.com"
+                }
+            ],
+            "description": "Highly opinionated mocking framework for PHP 5.3+",
+            "homepage": "https://github.com/phpspec/prophecy",
+            "keywords": [
+                "Double",
+                "Dummy",
+                "fake",
+                "mock",
+                "spy",
+                "stub"
+            ],
+            "support": {
+                "issues": "https://github.com/phpspec/prophecy/issues",
+                "source": "https://github.com/phpspec/prophecy/tree/v1.10.3"
+            },
+            "install-path": "../phpspec/prophecy"
+        },
+        {
+            "name": "phpunit/php-code-coverage",
+            "version": "2.2.4",
+            "version_normalized": "2.2.4.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
+                "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/eabf68b476ac7d0f73793aada060f1c1a9bf8979",
+                "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3",
+                "phpunit/php-file-iterator": "~1.3",
+                "phpunit/php-text-template": "~1.2",
+                "phpunit/php-token-stream": "~1.3",
+                "sebastian/environment": "^1.3.2",
+                "sebastian/version": "~1.0"
+            },
+            "require-dev": {
+                "ext-xdebug": ">=2.1.4",
+                "phpunit/phpunit": "~4"
+            },
+            "suggest": {
+                "ext-dom": "*",
+                "ext-xdebug": ">=2.2.1",
+                "ext-xmlwriter": "*"
+            },
+            "time": "2015-10-06T15:47:00+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.2.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sb@sebastian-bergmann.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
+            "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
+            "keywords": [
+                "coverage",
+                "testing",
+                "xunit"
+            ],
+            "support": {
+                "irc": "irc://irc.freenode.net/phpunit",
+                "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
+                "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/2.2"
+            },
+            "install-path": "../phpunit/php-code-coverage"
+        },
+        {
+            "name": "phpunit/php-file-iterator",
+            "version": "1.4.5",
+            "version_normalized": "1.4.5.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
+                "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4",
+                "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "time": "2017-11-27T13:52:08+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.4.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sb@sebastian-bergmann.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "FilterIterator implementation that filters files based on a list of suffixes.",
+            "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
+            "keywords": [
+                "filesystem",
+                "iterator"
+            ],
+            "support": {
+                "irc": "irc://irc.freenode.net/phpunit",
+                "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
+                "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/1.4.5"
+            },
+            "install-path": "../phpunit/php-file-iterator"
+        },
+        {
+            "name": "phpunit/php-text-template",
+            "version": "1.2.1",
+            "version_normalized": "1.2.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-text-template.git",
+                "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
+                "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "time": "2015-06-21T13:50:34+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Simple template engine.",
+            "homepage": "https://github.com/sebastianbergmann/php-text-template/",
+            "keywords": [
+                "template"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/php-text-template/issues",
+                "source": "https://github.com/sebastianbergmann/php-text-template/tree/1.2.1"
+            },
+            "install-path": "../phpunit/php-text-template"
+        },
+        {
+            "name": "phpunit/php-timer",
+            "version": "1.0.9",
+            "version_normalized": "1.0.9.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-timer.git",
+                "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f",
+                "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.3.3 || ^7.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0"
+            },
+            "time": "2017-02-26T11:10:40+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sb@sebastian-bergmann.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Utility class for timing",
+            "homepage": "https://github.com/sebastianbergmann/php-timer/",
+            "keywords": [
+                "timer"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/php-timer/issues",
+                "source": "https://github.com/sebastianbergmann/php-timer/tree/master"
+            },
+            "install-path": "../phpunit/php-timer"
+        },
+        {
+            "name": "phpunit/php-token-stream",
+            "version": "1.4.12",
+            "version_normalized": "1.4.12.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-token-stream.git",
+                "reference": "1ce90ba27c42e4e44e6d8458241466380b51fa16"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/1ce90ba27c42e4e44e6d8458241466380b51fa16",
+                "reference": "1ce90ba27c42e4e44e6d8458241466380b51fa16",
+                "shasum": ""
+            },
+            "require": {
+                "ext-tokenizer": "*",
+                "php": ">=5.3.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.2"
+            },
+            "time": "2017-12-04T08:55:13+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.4-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Wrapper around PHP's tokenizer extension.",
+            "homepage": "https://github.com/sebastianbergmann/php-token-stream/",
+            "keywords": [
+                "tokenizer"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/php-token-stream/issues",
+                "source": "https://github.com/sebastianbergmann/php-token-stream/tree/1.4"
+            },
+            "abandoned": true,
+            "install-path": "../phpunit/php-token-stream"
+        },
+        {
+            "name": "phpunit/phpunit",
+            "version": "4.8.36",
+            "version_normalized": "4.8.36.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/phpunit.git",
+                "reference": "46023de9a91eec7dfb06cc56cb4e260017298517"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/46023de9a91eec7dfb06cc56cb4e260017298517",
+                "reference": "46023de9a91eec7dfb06cc56cb4e260017298517",
+                "shasum": ""
+            },
+            "require": {
+                "ext-dom": "*",
+                "ext-json": "*",
+                "ext-pcre": "*",
+                "ext-reflection": "*",
+                "ext-spl": "*",
+                "php": ">=5.3.3",
+                "phpspec/prophecy": "^1.3.1",
+                "phpunit/php-code-coverage": "~2.1",
+                "phpunit/php-file-iterator": "~1.4",
+                "phpunit/php-text-template": "~1.2",
+                "phpunit/php-timer": "^1.0.6",
+                "phpunit/phpunit-mock-objects": "~2.3",
+                "sebastian/comparator": "~1.2.2",
+                "sebastian/diff": "~1.2",
+                "sebastian/environment": "~1.3",
+                "sebastian/exporter": "~1.2",
+                "sebastian/global-state": "~1.0",
+                "sebastian/version": "~1.0",
+                "symfony/yaml": "~2.1|~3.0"
+            },
+            "suggest": {
+                "phpunit/php-invoker": "~1.1"
+            },
+            "time": "2017-06-21T08:07:12+00:00",
+            "bin": [
+                "phpunit"
+            ],
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "4.8.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "The PHP Unit Testing framework.",
+            "homepage": "https://phpunit.de/",
+            "keywords": [
+                "phpunit",
+                "testing",
+                "xunit"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/phpunit/issues",
+                "source": "https://github.com/sebastianbergmann/phpunit/tree/4.8.36"
+            },
+            "install-path": "../phpunit/phpunit"
+        },
+        {
+            "name": "phpunit/phpunit-mock-objects",
+            "version": "2.3.8",
+            "version_normalized": "2.3.8.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git",
+                "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/ac8e7a3db35738d56ee9a76e78a4e03d97628983",
+                "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983",
+                "shasum": ""
+            },
+            "require": {
+                "doctrine/instantiator": "^1.0.2",
+                "php": ">=5.3.3",
+                "phpunit/php-text-template": "~1.2",
+                "sebastian/exporter": "~1.2"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.4"
+            },
+            "suggest": {
+                "ext-soap": "*"
+            },
+            "time": "2015-10-02T06:51:40+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.3.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sb@sebastian-bergmann.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Mock Object library for PHPUnit",
+            "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/",
+            "keywords": [
+                "mock",
+                "xunit"
+            ],
+            "support": {
+                "irc": "irc://irc.freenode.net/phpunit",
+                "issues": "https://github.com/sebastianbergmann/phpunit-mock-objects/issues",
+                "source": "https://github.com/sebastianbergmann/phpunit-mock-objects/tree/2.3"
+            },
+            "abandoned": true,
+            "install-path": "../phpunit/phpunit-mock-objects"
+        },
+        {
+            "name": "psr/container",
+            "version": "1.1.2",
+            "version_normalized": "1.1.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/container.git",
+                "reference": "513e0666f7216c7459170d56df27dfcefe1689ea"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea",
+                "reference": "513e0666f7216c7459170d56df27dfcefe1689ea",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.4.0"
+            },
+            "time": "2021-11-05T16:50:12+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Container\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "https://www.php-fig.org/"
+                }
+            ],
+            "description": "Common Container Interface (PHP FIG PSR-11)",
+            "homepage": "https://github.com/php-fig/container",
+            "keywords": [
+                "PSR-11",
+                "container",
+                "container-interface",
+                "container-interop",
+                "psr"
+            ],
+            "support": {
+                "issues": "https://github.com/php-fig/container/issues",
+                "source": "https://github.com/php-fig/container/tree/1.1.2"
+            },
+            "install-path": "../psr/container"
+        },
+        {
+            "name": "psr/simple-cache",
+            "version": "1.0.1",
+            "version_normalized": "1.0.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/simple-cache.git",
+                "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
+                "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "time": "2017-10-23T01:57:42+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Psr\\SimpleCache\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interfaces for simple caching",
+            "keywords": [
+                "cache",
+                "caching",
+                "psr",
+                "psr-16",
+                "simple-cache"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/simple-cache/tree/master"
+            },
+            "install-path": "../psr/simple-cache"
+        },
+        {
+            "name": "sebastian/comparator",
+            "version": "1.2.4",
+            "version_normalized": "1.2.4.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/comparator.git",
+                "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2b7424b55f5047b47ac6e5ccb20b2aea4011d9be",
+                "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3",
+                "sebastian/diff": "~1.2",
+                "sebastian/exporter": "~1.2 || ~2.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.4"
+            },
+            "time": "2017-01-29T09:50:25+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.2.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Jeff Welch",
+                    "email": "whatthejeff@gmail.com"
+                },
+                {
+                    "name": "Volker Dusch",
+                    "email": "github@wallbash.com"
+                },
+                {
+                    "name": "Bernhard Schussek",
+                    "email": "bschussek@2bepublished.at"
+                },
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Provides the functionality to compare PHP values for equality",
+            "homepage": "http://www.github.com/sebastianbergmann/comparator",
+            "keywords": [
+                "comparator",
+                "compare",
+                "equality"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/comparator/issues",
+                "source": "https://github.com/sebastianbergmann/comparator/tree/1.2"
+            },
+            "install-path": "../sebastian/comparator"
+        },
+        {
+            "name": "sebastian/diff",
+            "version": "1.4.3",
+            "version_normalized": "1.4.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/diff.git",
+                "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7f066a26a962dbe58ddea9f72a4e82874a3975a4",
+                "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.3.3 || ^7.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0"
+            },
+            "time": "2017-05-22T07:24:03+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.4-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Kore Nordmann",
+                    "email": "mail@kore-nordmann.de"
+                },
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Diff implementation",
+            "homepage": "https://github.com/sebastianbergmann/diff",
+            "keywords": [
+                "diff"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/diff/issues",
+                "source": "https://github.com/sebastianbergmann/diff/tree/1.4"
+            },
+            "install-path": "../sebastian/diff"
+        },
+        {
+            "name": "sebastian/environment",
+            "version": "1.3.8",
+            "version_normalized": "1.3.8.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/environment.git",
+                "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/be2c607e43ce4c89ecd60e75c6a85c126e754aea",
+                "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.3.3 || ^7.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4.8 || ^5.0"
+            },
+            "time": "2016-08-18T05:49:44+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.3.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Provides functionality to handle HHVM/PHP environments",
+            "homepage": "http://www.github.com/sebastianbergmann/environment",
+            "keywords": [
+                "Xdebug",
+                "environment",
+                "hhvm"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/environment/issues",
+                "source": "https://github.com/sebastianbergmann/environment/tree/1.3"
+            },
+            "install-path": "../sebastian/environment"
+        },
+        {
+            "name": "sebastian/exporter",
+            "version": "1.2.2",
+            "version_normalized": "1.2.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/exporter.git",
+                "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/42c4c2eec485ee3e159ec9884f95b431287edde4",
+                "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3",
+                "sebastian/recursion-context": "~1.0"
+            },
+            "require-dev": {
+                "ext-mbstring": "*",
+                "phpunit/phpunit": "~4.4"
+            },
+            "time": "2016-06-17T09:04:28+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.3.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Jeff Welch",
+                    "email": "whatthejeff@gmail.com"
+                },
+                {
+                    "name": "Volker Dusch",
+                    "email": "github@wallbash.com"
+                },
+                {
+                    "name": "Bernhard Schussek",
+                    "email": "bschussek@2bepublished.at"
+                },
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                },
+                {
+                    "name": "Adam Harvey",
+                    "email": "aharvey@php.net"
+                }
+            ],
+            "description": "Provides the functionality to export PHP variables for visualization",
+            "homepage": "http://www.github.com/sebastianbergmann/exporter",
+            "keywords": [
+                "export",
+                "exporter"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/exporter/issues",
+                "source": "https://github.com/sebastianbergmann/exporter/tree/master"
+            },
+            "install-path": "../sebastian/exporter"
+        },
+        {
+            "name": "sebastian/global-state",
+            "version": "1.1.1",
+            "version_normalized": "1.1.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/global-state.git",
+                "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4",
+                "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.2"
+            },
+            "suggest": {
+                "ext-uopz": "*"
+            },
+            "time": "2015-10-12T03:26:01+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Snapshotting of global state",
+            "homepage": "http://www.github.com/sebastianbergmann/global-state",
+            "keywords": [
+                "global state"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/global-state/issues",
+                "source": "https://github.com/sebastianbergmann/global-state/tree/1.1.1"
+            },
+            "install-path": "../sebastian/global-state"
+        },
+        {
+            "name": "sebastian/recursion-context",
+            "version": "1.0.5",
+            "version_normalized": "1.0.5.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/recursion-context.git",
+                "reference": "b19cc3298482a335a95f3016d2f8a6950f0fbcd7"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/b19cc3298482a335a95f3016d2f8a6950f0fbcd7",
+                "reference": "b19cc3298482a335a95f3016d2f8a6950f0fbcd7",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.4"
+            },
+            "time": "2016-10-03T07:41:43+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Jeff Welch",
+                    "email": "whatthejeff@gmail.com"
+                },
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                },
+                {
+                    "name": "Adam Harvey",
+                    "email": "aharvey@php.net"
+                }
+            ],
+            "description": "Provides functionality to recursively process PHP variables",
+            "homepage": "http://www.github.com/sebastianbergmann/recursion-context",
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/recursion-context/issues",
+                "source": "https://github.com/sebastianbergmann/recursion-context/tree/master"
+            },
+            "install-path": "../sebastian/recursion-context"
+        },
+        {
+            "name": "sebastian/version",
+            "version": "1.0.6",
+            "version_normalized": "1.0.6.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/version.git",
+                "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/58b3a85e7999757d6ad81c787a1fbf5ff6c628c6",
+                "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6",
+                "shasum": ""
+            },
+            "time": "2015-06-21T13:59:46+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Library that helps with managing the version number of Git-hosted PHP projects",
+            "homepage": "https://github.com/sebastianbergmann/version",
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/version/issues",
+                "source": "https://github.com/sebastianbergmann/version/tree/1.0.6"
+            },
+            "install-path": "../sebastian/version"
+        },
+        {
+            "name": "symfony/deprecation-contracts",
+            "version": "v2.5.2",
+            "version_normalized": "2.5.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/deprecation-contracts.git",
+                "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e8b495ea28c1d97b5e0c121748d6f9b53d075c66",
+                "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "time": "2022-01-02T09:53:40+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "2.5-dev"
+                },
+                "thanks": {
+                    "name": "symfony/contracts",
+                    "url": "https://github.com/symfony/contracts"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "files": [
+                    "function.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "A generic function and convention to trigger deprecation notices",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.2"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../symfony/deprecation-contracts"
+        },
+        {
+            "name": "symfony/http-foundation",
+            "version": "v5.4.11",
+            "version_normalized": "5.4.11.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/http-foundation.git",
+                "reference": "0a5868e0999e9d47859ba3d918548ff6943e6389"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/http-foundation/zipball/0a5868e0999e9d47859ba3d918548ff6943e6389",
+                "reference": "0a5868e0999e9d47859ba3d918548ff6943e6389",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2.5",
+                "symfony/deprecation-contracts": "^2.1|^3",
+                "symfony/polyfill-mbstring": "~1.1",
+                "symfony/polyfill-php80": "^1.16"
+            },
+            "require-dev": {
+                "predis/predis": "~1.0",
+                "symfony/cache": "^4.4|^5.0|^6.0",
+                "symfony/expression-language": "^4.4|^5.0|^6.0",
+                "symfony/mime": "^4.4|^5.0|^6.0"
+            },
+            "suggest": {
+                "symfony/mime": "To use the file extension guesser"
+            },
+            "time": "2022-07-20T13:00:38+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\HttpFoundation\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Defines an object-oriented layer for the HTTP specification",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/http-foundation/tree/v5.4.11"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../symfony/http-foundation"
+        },
+        {
+            "name": "symfony/polyfill-ctype",
+            "version": "v1.26.0",
+            "version_normalized": "1.26.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-ctype.git",
+                "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4",
+                "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "provide": {
+                "ext-ctype": "*"
+            },
+            "suggest": {
+                "ext-ctype": "For best performance"
+            },
+            "time": "2022-05-24T11:49:31+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "1.26-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Ctype\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Gert de Pagter",
+                    "email": "BackEndTea@gmail.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for ctype functions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "ctype",
+                "polyfill",
+                "portable"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-ctype/tree/v1.26.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../symfony/polyfill-ctype"
+        },
+        {
+            "name": "symfony/polyfill-mbstring",
+            "version": "v1.26.0",
+            "version_normalized": "1.26.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-mbstring.git",
+                "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e",
+                "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "provide": {
+                "ext-mbstring": "*"
+            },
+            "suggest": {
+                "ext-mbstring": "For best performance"
+            },
+            "time": "2022-05-24T11:49:31+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "1.26-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Mbstring\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for the Mbstring extension",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "mbstring",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../symfony/polyfill-mbstring"
+        },
+        {
+            "name": "symfony/polyfill-php80",
+            "version": "v1.26.0",
+            "version_normalized": "1.26.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-php80.git",
+                "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace",
+                "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "time": "2022-05-10T07:21:04+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "1.26-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Php80\\": ""
+                },
+                "classmap": [
+                    "Resources/stubs"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Ion Bazan",
+                    "email": "ion.bazan@gmail.com"
+                },
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../symfony/polyfill-php80"
+        },
+        {
+            "name": "symfony/translation",
+            "version": "v4.4.44",
+            "version_normalized": "4.4.44.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/translation.git",
+                "reference": "af947fefc306cec6ea5a1f6160c7e305a71f2493"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/translation/zipball/af947fefc306cec6ea5a1f6160c7e305a71f2493",
+                "reference": "af947fefc306cec6ea5a1f6160c7e305a71f2493",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1.3",
+                "symfony/polyfill-mbstring": "~1.0",
+                "symfony/polyfill-php80": "^1.16",
+                "symfony/translation-contracts": "^1.1.6|^2"
+            },
+            "conflict": {
+                "symfony/config": "<3.4",
+                "symfony/dependency-injection": "<3.4",
+                "symfony/http-kernel": "<4.4",
+                "symfony/yaml": "<3.4"
+            },
+            "provide": {
+                "symfony/translation-implementation": "1.0|2.0"
+            },
+            "require-dev": {
+                "psr/log": "^1|^2|^3",
+                "symfony/config": "^3.4|^4.0|^5.0",
+                "symfony/console": "^3.4|^4.0|^5.0",
+                "symfony/dependency-injection": "^3.4|^4.0|^5.0",
+                "symfony/finder": "~2.8|~3.0|~4.0|^5.0",
+                "symfony/http-kernel": "^4.4",
+                "symfony/intl": "^3.4|^4.0|^5.0",
+                "symfony/service-contracts": "^1.1.2|^2",
+                "symfony/yaml": "^3.4|^4.0|^5.0"
+            },
+            "suggest": {
+                "psr/log-implementation": "To use logging capability in translator",
+                "symfony/config": "",
+                "symfony/yaml": ""
+            },
+            "time": "2022-07-20T09:59:04+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Translation\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Provides tools to internationalize your application",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/translation/tree/v4.4.44"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../symfony/translation"
+        },
+        {
+            "name": "symfony/translation-contracts",
+            "version": "v2.5.2",
+            "version_normalized": "2.5.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/translation-contracts.git",
+                "reference": "136b19dd05cdf0709db6537d058bcab6dd6e2dbe"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/136b19dd05cdf0709db6537d058bcab6dd6e2dbe",
+                "reference": "136b19dd05cdf0709db6537d058bcab6dd6e2dbe",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2.5"
+            },
+            "suggest": {
+                "symfony/translation-implementation": ""
+            },
+            "time": "2022-06-27T16:58:25+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "2.5-dev"
+                },
+                "thanks": {
+                    "name": "symfony/contracts",
+                    "url": "https://github.com/symfony/contracts"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Contracts\\Translation\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Generic abstractions related to translation",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "abstractions",
+                "contracts",
+                "decoupling",
+                "interfaces",
+                "interoperability",
+                "standards"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/translation-contracts/tree/v2.5.2"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../symfony/translation-contracts"
+        },
+        {
+            "name": "symfony/yaml",
+            "version": "v3.4.47",
+            "version_normalized": "3.4.47.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/yaml.git",
+                "reference": "88289caa3c166321883f67fe5130188ebbb47094"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/yaml/zipball/88289caa3c166321883f67fe5130188ebbb47094",
+                "reference": "88289caa3c166321883f67fe5130188ebbb47094",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.5.9|>=7.0.8",
+                "symfony/polyfill-ctype": "~1.8"
+            },
+            "conflict": {
+                "symfony/console": "<3.4"
+            },
+            "require-dev": {
+                "symfony/console": "~3.4|~4.0"
+            },
+            "suggest": {
+                "symfony/console": "For validating YAML files using the lint command"
+            },
+            "time": "2020-10-24T10:57:07+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Yaml\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony Yaml Component",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/yaml/tree/v3.4.47"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../symfony/yaml"
+        },
+        {
+            "name": "voku/portable-ascii",
+            "version": "1.6.1",
+            "version_normalized": "1.6.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/voku/portable-ascii.git",
+                "reference": "87337c91b9dfacee02452244ee14ab3c43bc485a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/voku/portable-ascii/zipball/87337c91b9dfacee02452244ee14ab3c43bc485a",
+                "reference": "87337c91b9dfacee02452244ee14ab3c43bc485a",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.0.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0"
+            },
+            "suggest": {
+                "ext-intl": "Use Intl for transliterator_transliterate() support"
+            },
+            "time": "2022-01-24T18:55:24+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "voku\\": "src/voku/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Lars Moelleken",
+                    "homepage": "http://www.moelleken.org/"
+                }
+            ],
+            "description": "Portable ASCII library - performance optimized (ascii) string functions for php.",
+            "homepage": "https://github.com/voku/portable-ascii",
+            "keywords": [
+                "ascii",
+                "clean",
+                "php"
+            ],
+            "support": {
+                "issues": "https://github.com/voku/portable-ascii/issues",
+                "source": "https://github.com/voku/portable-ascii/tree/1.6.1"
+            },
+            "funding": [
+                {
+                    "url": "https://www.paypal.me/moelleken",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/voku",
+                    "type": "github"
+                },
+                {
+                    "url": "https://opencollective.com/portable-ascii",
+                    "type": "open_collective"
+                },
+                {
+                    "url": "https://www.patreon.com/voku",
+                    "type": "patreon"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../voku/portable-ascii"
+        },
+        {
+            "name": "webmozart/assert",
+            "version": "1.11.0",
+            "version_normalized": "1.11.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/webmozarts/assert.git",
+                "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991",
+                "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991",
+                "shasum": ""
+            },
+            "require": {
+                "ext-ctype": "*",
+                "php": "^7.2 || ^8.0"
+            },
+            "conflict": {
+                "phpstan/phpstan": "<0.12.20",
+                "vimeo/psalm": "<4.6.1 || 4.6.2"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^8.5.13"
+            },
+            "time": "2022-06-03T18:03:27+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.10-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Webmozart\\Assert\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Bernhard Schussek",
+                    "email": "bschussek@gmail.com"
+                }
+            ],
+            "description": "Assertions to validate method input/output with nice error messages.",
+            "keywords": [
+                "assert",
+                "check",
+                "validate"
+            ],
+            "support": {
+                "issues": "https://github.com/webmozarts/assert/issues",
+                "source": "https://github.com/webmozarts/assert/tree/1.11.0"
+            },
+            "install-path": "../webmozart/assert"
+        }
+    ],
+    "dev": true,
+    "dev-package-names": [
+        "doctrine/instantiator",
+        "phpdocumentor/reflection-common",
+        "phpdocumentor/reflection-docblock",
+        "phpdocumentor/type-resolver",
+        "phpspec/prophecy",
+        "phpunit/php-code-coverage",
+        "phpunit/php-file-iterator",
+        "phpunit/php-text-template",
+        "phpunit/php-timer",
+        "phpunit/php-token-stream",
+        "phpunit/phpunit",
+        "phpunit/phpunit-mock-objects",
+        "sebastian/comparator",
+        "sebastian/diff",
+        "sebastian/environment",
+        "sebastian/exporter",
+        "sebastian/global-state",
+        "sebastian/recursion-context",
+        "sebastian/version",
+        "symfony/polyfill-ctype",
+        "symfony/yaml",
+        "webmozart/assert"
+    ]
+}

+ 371 - 0
htdocs/includes/webklex/php-imap/vendor/composer/installed.php

@@ -0,0 +1,371 @@
+<?php return array(
+    'root' => array(
+        'name' => 'webklex/php-imap',
+        'pretty_version' => 'dev-develop',
+        'version' => 'dev-develop',
+        'reference' => '0f467d1c4283cc035ef474db3c733653feeb570f',
+        'type' => 'library',
+        'install_path' => __DIR__ . '/../../',
+        'aliases' => array(),
+        'dev' => true,
+    ),
+    'versions' => array(
+        'doctrine/inflector' => array(
+            'pretty_version' => '2.0.4',
+            'version' => '2.0.4.0',
+            'reference' => '8b7ff3e4b7de6b2c84da85637b59fd2880ecaa89',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../doctrine/inflector',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'doctrine/instantiator' => array(
+            'pretty_version' => '1.4.1',
+            'version' => '1.4.1.0',
+            'reference' => '10dcfce151b967d20fde1b34ae6640712c3891bc',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../doctrine/instantiator',
+            'aliases' => array(),
+            'dev_requirement' => true,
+        ),
+        'illuminate/collections' => array(
+            'pretty_version' => 'v8.83.23',
+            'version' => '8.83.23.0',
+            'reference' => '705a4e1ef93cd492c45b9b3e7911cccc990a07f4',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../illuminate/collections',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'illuminate/contracts' => array(
+            'pretty_version' => 'v8.83.23',
+            'version' => '8.83.23.0',
+            'reference' => '5e0fd287a1b22a6b346a9f7cd484d8cf0234585d',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../illuminate/contracts',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'illuminate/macroable' => array(
+            'pretty_version' => 'v8.83.23',
+            'version' => '8.83.23.0',
+            'reference' => 'aed81891a6e046fdee72edd497f822190f61c162',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../illuminate/macroable',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'illuminate/pagination' => array(
+            'pretty_version' => 'v8.83.23',
+            'version' => '8.83.23.0',
+            'reference' => '16fe8dc35f9d18c58a3471469af656a02e9ab692',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../illuminate/pagination',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'illuminate/support' => array(
+            'pretty_version' => 'v8.83.23',
+            'version' => '8.83.23.0',
+            'reference' => 'c3d643e77082786ae8a51502c757e9b1a3ee254e',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../illuminate/support',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'nesbot/carbon' => array(
+            'pretty_version' => '2.61.0',
+            'version' => '2.61.0.0',
+            'reference' => 'bdf4f4fe3a3eac4de84dbec0738082a862c68ba6',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../nesbot/carbon',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'phpdocumentor/reflection-common' => array(
+            'pretty_version' => '2.2.0',
+            'version' => '2.2.0.0',
+            'reference' => '1d01c49d4ed62f25aa84a747ad35d5a16924662b',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../phpdocumentor/reflection-common',
+            'aliases' => array(),
+            'dev_requirement' => true,
+        ),
+        'phpdocumentor/reflection-docblock' => array(
+            'pretty_version' => '5.3.0',
+            'version' => '5.3.0.0',
+            'reference' => '622548b623e81ca6d78b721c5e029f4ce664f170',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../phpdocumentor/reflection-docblock',
+            'aliases' => array(),
+            'dev_requirement' => true,
+        ),
+        'phpdocumentor/type-resolver' => array(
+            'pretty_version' => '1.6.1',
+            'version' => '1.6.1.0',
+            'reference' => '77a32518733312af16a44300404e945338981de3',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../phpdocumentor/type-resolver',
+            'aliases' => array(),
+            'dev_requirement' => true,
+        ),
+        'phpspec/prophecy' => array(
+            'pretty_version' => 'v1.10.3',
+            'version' => '1.10.3.0',
+            'reference' => '451c3cd1418cf640de218914901e51b064abb093',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../phpspec/prophecy',
+            'aliases' => array(),
+            'dev_requirement' => true,
+        ),
+        'phpunit/php-code-coverage' => array(
+            'pretty_version' => '2.2.4',
+            'version' => '2.2.4.0',
+            'reference' => 'eabf68b476ac7d0f73793aada060f1c1a9bf8979',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../phpunit/php-code-coverage',
+            'aliases' => array(),
+            'dev_requirement' => true,
+        ),
+        'phpunit/php-file-iterator' => array(
+            'pretty_version' => '1.4.5',
+            'version' => '1.4.5.0',
+            'reference' => '730b01bc3e867237eaac355e06a36b85dd93a8b4',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../phpunit/php-file-iterator',
+            'aliases' => array(),
+            'dev_requirement' => true,
+        ),
+        'phpunit/php-text-template' => array(
+            'pretty_version' => '1.2.1',
+            'version' => '1.2.1.0',
+            'reference' => '31f8b717e51d9a2afca6c9f046f5d69fc27c8686',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../phpunit/php-text-template',
+            'aliases' => array(),
+            'dev_requirement' => true,
+        ),
+        'phpunit/php-timer' => array(
+            'pretty_version' => '1.0.9',
+            'version' => '1.0.9.0',
+            'reference' => '3dcf38ca72b158baf0bc245e9184d3fdffa9c46f',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../phpunit/php-timer',
+            'aliases' => array(),
+            'dev_requirement' => true,
+        ),
+        'phpunit/php-token-stream' => array(
+            'pretty_version' => '1.4.12',
+            'version' => '1.4.12.0',
+            'reference' => '1ce90ba27c42e4e44e6d8458241466380b51fa16',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../phpunit/php-token-stream',
+            'aliases' => array(),
+            'dev_requirement' => true,
+        ),
+        'phpunit/phpunit' => array(
+            'pretty_version' => '4.8.36',
+            'version' => '4.8.36.0',
+            'reference' => '46023de9a91eec7dfb06cc56cb4e260017298517',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../phpunit/phpunit',
+            'aliases' => array(),
+            'dev_requirement' => true,
+        ),
+        'phpunit/phpunit-mock-objects' => array(
+            'pretty_version' => '2.3.8',
+            'version' => '2.3.8.0',
+            'reference' => 'ac8e7a3db35738d56ee9a76e78a4e03d97628983',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../phpunit/phpunit-mock-objects',
+            'aliases' => array(),
+            'dev_requirement' => true,
+        ),
+        'psr/container' => array(
+            'pretty_version' => '1.1.2',
+            'version' => '1.1.2.0',
+            'reference' => '513e0666f7216c7459170d56df27dfcefe1689ea',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../psr/container',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'psr/simple-cache' => array(
+            'pretty_version' => '1.0.1',
+            'version' => '1.0.1.0',
+            'reference' => '408d5eafb83c57f6365a3ca330ff23aa4a5fa39b',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../psr/simple-cache',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'sebastian/comparator' => array(
+            'pretty_version' => '1.2.4',
+            'version' => '1.2.4.0',
+            'reference' => '2b7424b55f5047b47ac6e5ccb20b2aea4011d9be',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../sebastian/comparator',
+            'aliases' => array(),
+            'dev_requirement' => true,
+        ),
+        'sebastian/diff' => array(
+            'pretty_version' => '1.4.3',
+            'version' => '1.4.3.0',
+            'reference' => '7f066a26a962dbe58ddea9f72a4e82874a3975a4',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../sebastian/diff',
+            'aliases' => array(),
+            'dev_requirement' => true,
+        ),
+        'sebastian/environment' => array(
+            'pretty_version' => '1.3.8',
+            'version' => '1.3.8.0',
+            'reference' => 'be2c607e43ce4c89ecd60e75c6a85c126e754aea',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../sebastian/environment',
+            'aliases' => array(),
+            'dev_requirement' => true,
+        ),
+        'sebastian/exporter' => array(
+            'pretty_version' => '1.2.2',
+            'version' => '1.2.2.0',
+            'reference' => '42c4c2eec485ee3e159ec9884f95b431287edde4',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../sebastian/exporter',
+            'aliases' => array(),
+            'dev_requirement' => true,
+        ),
+        'sebastian/global-state' => array(
+            'pretty_version' => '1.1.1',
+            'version' => '1.1.1.0',
+            'reference' => 'bc37d50fea7d017d3d340f230811c9f1d7280af4',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../sebastian/global-state',
+            'aliases' => array(),
+            'dev_requirement' => true,
+        ),
+        'sebastian/recursion-context' => array(
+            'pretty_version' => '1.0.5',
+            'version' => '1.0.5.0',
+            'reference' => 'b19cc3298482a335a95f3016d2f8a6950f0fbcd7',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../sebastian/recursion-context',
+            'aliases' => array(),
+            'dev_requirement' => true,
+        ),
+        'sebastian/version' => array(
+            'pretty_version' => '1.0.6',
+            'version' => '1.0.6.0',
+            'reference' => '58b3a85e7999757d6ad81c787a1fbf5ff6c628c6',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../sebastian/version',
+            'aliases' => array(),
+            'dev_requirement' => true,
+        ),
+        'symfony/deprecation-contracts' => array(
+            'pretty_version' => 'v2.5.2',
+            'version' => '2.5.2.0',
+            'reference' => 'e8b495ea28c1d97b5e0c121748d6f9b53d075c66',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/deprecation-contracts',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'symfony/http-foundation' => array(
+            'pretty_version' => 'v5.4.11',
+            'version' => '5.4.11.0',
+            'reference' => '0a5868e0999e9d47859ba3d918548ff6943e6389',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/http-foundation',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'symfony/polyfill-ctype' => array(
+            'pretty_version' => 'v1.26.0',
+            'version' => '1.26.0.0',
+            'reference' => '6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/polyfill-ctype',
+            'aliases' => array(),
+            'dev_requirement' => true,
+        ),
+        'symfony/polyfill-mbstring' => array(
+            'pretty_version' => 'v1.26.0',
+            'version' => '1.26.0.0',
+            'reference' => '9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'symfony/polyfill-php80' => array(
+            'pretty_version' => 'v1.26.0',
+            'version' => '1.26.0.0',
+            'reference' => 'cfa0ae98841b9e461207c13ab093d76b0fa7bace',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/polyfill-php80',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'symfony/translation' => array(
+            'pretty_version' => 'v4.4.44',
+            'version' => '4.4.44.0',
+            'reference' => 'af947fefc306cec6ea5a1f6160c7e305a71f2493',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/translation',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'symfony/translation-contracts' => array(
+            'pretty_version' => 'v2.5.2',
+            'version' => '2.5.2.0',
+            'reference' => '136b19dd05cdf0709db6537d058bcab6dd6e2dbe',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/translation-contracts',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'symfony/translation-implementation' => array(
+            'dev_requirement' => false,
+            'provided' => array(
+                0 => '1.0|2.0',
+            ),
+        ),
+        'symfony/yaml' => array(
+            'pretty_version' => 'v3.4.47',
+            'version' => '3.4.47.0',
+            'reference' => '88289caa3c166321883f67fe5130188ebbb47094',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/yaml',
+            'aliases' => array(),
+            'dev_requirement' => true,
+        ),
+        'voku/portable-ascii' => array(
+            'pretty_version' => '1.6.1',
+            'version' => '1.6.1.0',
+            'reference' => '87337c91b9dfacee02452244ee14ab3c43bc485a',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../voku/portable-ascii',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'webklex/php-imap' => array(
+            'pretty_version' => 'dev-develop',
+            'version' => 'dev-develop',
+            'reference' => '0f467d1c4283cc035ef474db3c733653feeb570f',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../../',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'webmozart/assert' => array(
+            'pretty_version' => '1.11.0',
+            'version' => '1.11.0.0',
+            'reference' => '11cb2199493b2f8a3b53e7f19068fc6aac760991',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../webmozart/assert',
+            'aliases' => array(),
+            'dev_requirement' => true,
+        ),
+    ),
+);

+ 26 - 0
htdocs/includes/webklex/php-imap/vendor/composer/platform_check.php

@@ -0,0 +1,26 @@
+<?php
+
+// platform_check.php @generated by Composer
+
+$issues = array();
+
+if (!(PHP_VERSION_ID >= 70400)) {
+    $issues[] = 'Your Composer dependencies require a PHP version ">= 7.4.0". You are running ' . PHP_VERSION . '.';
+}
+
+if ($issues) {
+    if (!headers_sent()) {
+        header('HTTP/1.1 500 Internal Server Error');
+    }
+    if (!ini_get('display_errors')) {
+        if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
+            fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
+        } elseif (!headers_sent()) {
+            echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
+        }
+    }
+    trigger_error(
+        'Composer detected issues in your platform: ' . implode(' ', $issues),
+        E_USER_ERROR
+    );
+}

+ 747 - 0
htdocs/includes/webklex/php-imap/vendor/illuminate/collections/Arr.php

@@ -0,0 +1,747 @@
+<?php
+
+namespace Illuminate\Support;
+
+use ArrayAccess;
+use Illuminate\Support\Traits\Macroable;
+use InvalidArgumentException;
+
+class Arr
+{
+    use Macroable;
+
+    /**
+     * Determine whether the given value is array accessible.
+     *
+     * @param  mixed  $value
+     * @return bool
+     */
+    public static function accessible($value)
+    {
+        return is_array($value) || $value instanceof ArrayAccess;
+    }
+
+    /**
+     * Add an element to an array using "dot" notation if it doesn't exist.
+     *
+     * @param  array  $array
+     * @param  string  $key
+     * @param  mixed  $value
+     * @return array
+     */
+    public static function add($array, $key, $value)
+    {
+        if (is_null(static::get($array, $key))) {
+            static::set($array, $key, $value);
+        }
+
+        return $array;
+    }
+
+    /**
+     * Collapse an array of arrays into a single array.
+     *
+     * @param  iterable  $array
+     * @return array
+     */
+    public static function collapse($array)
+    {
+        $results = [];
+
+        foreach ($array as $values) {
+            if ($values instanceof Collection) {
+                $values = $values->all();
+            } elseif (! is_array($values)) {
+                continue;
+            }
+
+            $results[] = $values;
+        }
+
+        return array_merge([], ...$results);
+    }
+
+    /**
+     * Cross join the given arrays, returning all possible permutations.
+     *
+     * @param  iterable  ...$arrays
+     * @return array
+     */
+    public static function crossJoin(...$arrays)
+    {
+        $results = [[]];
+
+        foreach ($arrays as $index => $array) {
+            $append = [];
+
+            foreach ($results as $product) {
+                foreach ($array as $item) {
+                    $product[$index] = $item;
+
+                    $append[] = $product;
+                }
+            }
+
+            $results = $append;
+        }
+
+        return $results;
+    }
+
+    /**
+     * Divide an array into two arrays. One with keys and the other with values.
+     *
+     * @param  array  $array
+     * @return array
+     */
+    public static function divide($array)
+    {
+        return [array_keys($array), array_values($array)];
+    }
+
+    /**
+     * Flatten a multi-dimensional associative array with dots.
+     *
+     * @param  iterable  $array
+     * @param  string  $prepend
+     * @return array
+     */
+    public static function dot($array, $prepend = '')
+    {
+        $results = [];
+
+        foreach ($array as $key => $value) {
+            if (is_array($value) && ! empty($value)) {
+                $results = array_merge($results, static::dot($value, $prepend.$key.'.'));
+            } else {
+                $results[$prepend.$key] = $value;
+            }
+        }
+
+        return $results;
+    }
+
+    /**
+     * Convert a flatten "dot" notation array into an expanded array.
+     *
+     * @param  iterable  $array
+     * @return array
+     */
+    public static function undot($array)
+    {
+        $results = [];
+
+        foreach ($array as $key => $value) {
+            static::set($results, $key, $value);
+        }
+
+        return $results;
+    }
+
+    /**
+     * Get all of the given array except for a specified array of keys.
+     *
+     * @param  array  $array
+     * @param  array|string  $keys
+     * @return array
+     */
+    public static function except($array, $keys)
+    {
+        static::forget($array, $keys);
+
+        return $array;
+    }
+
+    /**
+     * Determine if the given key exists in the provided array.
+     *
+     * @param  \ArrayAccess|array  $array
+     * @param  string|int  $key
+     * @return bool
+     */
+    public static function exists($array, $key)
+    {
+        if ($array instanceof Enumerable) {
+            return $array->has($key);
+        }
+
+        if ($array instanceof ArrayAccess) {
+            return $array->offsetExists($key);
+        }
+
+        return array_key_exists($key, $array);
+    }
+
+    /**
+     * Return the first element in an array passing a given truth test.
+     *
+     * @param  iterable  $array
+     * @param  callable|null  $callback
+     * @param  mixed  $default
+     * @return mixed
+     */
+    public static function first($array, callable $callback = null, $default = null)
+    {
+        if (is_null($callback)) {
+            if (empty($array)) {
+                return value($default);
+            }
+
+            foreach ($array as $item) {
+                return $item;
+            }
+        }
+
+        foreach ($array as $key => $value) {
+            if ($callback($value, $key)) {
+                return $value;
+            }
+        }
+
+        return value($default);
+    }
+
+    /**
+     * Return the last element in an array passing a given truth test.
+     *
+     * @param  array  $array
+     * @param  callable|null  $callback
+     * @param  mixed  $default
+     * @return mixed
+     */
+    public static function last($array, callable $callback = null, $default = null)
+    {
+        if (is_null($callback)) {
+            return empty($array) ? value($default) : end($array);
+        }
+
+        return static::first(array_reverse($array, true), $callback, $default);
+    }
+
+    /**
+     * Flatten a multi-dimensional array into a single level.
+     *
+     * @param  iterable  $array
+     * @param  int  $depth
+     * @return array
+     */
+    public static function flatten($array, $depth = INF)
+    {
+        $result = [];
+
+        foreach ($array as $item) {
+            $item = $item instanceof Collection ? $item->all() : $item;
+
+            if (! is_array($item)) {
+                $result[] = $item;
+            } else {
+                $values = $depth === 1
+                    ? array_values($item)
+                    : static::flatten($item, $depth - 1);
+
+                foreach ($values as $value) {
+                    $result[] = $value;
+                }
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * Remove one or many array items from a given array using "dot" notation.
+     *
+     * @param  array  $array
+     * @param  array|string  $keys
+     * @return void
+     */
+    public static function forget(&$array, $keys)
+    {
+        $original = &$array;
+
+        $keys = (array) $keys;
+
+        if (count($keys) === 0) {
+            return;
+        }
+
+        foreach ($keys as $key) {
+            // if the exact key exists in the top-level, remove it
+            if (static::exists($array, $key)) {
+                unset($array[$key]);
+
+                continue;
+            }
+
+            $parts = explode('.', $key);
+
+            // clean up before each pass
+            $array = &$original;
+
+            while (count($parts) > 1) {
+                $part = array_shift($parts);
+
+                if (isset($array[$part]) && is_array($array[$part])) {
+                    $array = &$array[$part];
+                } else {
+                    continue 2;
+                }
+            }
+
+            unset($array[array_shift($parts)]);
+        }
+    }
+
+    /**
+     * Get an item from an array using "dot" notation.
+     *
+     * @param  \ArrayAccess|array  $array
+     * @param  string|int|null  $key
+     * @param  mixed  $default
+     * @return mixed
+     */
+    public static function get($array, $key, $default = null)
+    {
+        if (! static::accessible($array)) {
+            return value($default);
+        }
+
+        if (is_null($key)) {
+            return $array;
+        }
+
+        if (static::exists($array, $key)) {
+            return $array[$key];
+        }
+
+        if (strpos($key, '.') === false) {
+            return $array[$key] ?? value($default);
+        }
+
+        foreach (explode('.', $key) as $segment) {
+            if (static::accessible($array) && static::exists($array, $segment)) {
+                $array = $array[$segment];
+            } else {
+                return value($default);
+            }
+        }
+
+        return $array;
+    }
+
+    /**
+     * Check if an item or items exist in an array using "dot" notation.
+     *
+     * @param  \ArrayAccess|array  $array
+     * @param  string|array  $keys
+     * @return bool
+     */
+    public static function has($array, $keys)
+    {
+        $keys = (array) $keys;
+
+        if (! $array || $keys === []) {
+            return false;
+        }
+
+        foreach ($keys as $key) {
+            $subKeyArray = $array;
+
+            if (static::exists($array, $key)) {
+                continue;
+            }
+
+            foreach (explode('.', $key) as $segment) {
+                if (static::accessible($subKeyArray) && static::exists($subKeyArray, $segment)) {
+                    $subKeyArray = $subKeyArray[$segment];
+                } else {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Determine if any of the keys exist in an array using "dot" notation.
+     *
+     * @param  \ArrayAccess|array  $array
+     * @param  string|array  $keys
+     * @return bool
+     */
+    public static function hasAny($array, $keys)
+    {
+        if (is_null($keys)) {
+            return false;
+        }
+
+        $keys = (array) $keys;
+
+        if (! $array) {
+            return false;
+        }
+
+        if ($keys === []) {
+            return false;
+        }
+
+        foreach ($keys as $key) {
+            if (static::has($array, $key)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Determines if an array is associative.
+     *
+     * An array is "associative" if it doesn't have sequential numerical keys beginning with zero.
+     *
+     * @param  array  $array
+     * @return bool
+     */
+    public static function isAssoc(array $array)
+    {
+        $keys = array_keys($array);
+
+        return array_keys($keys) !== $keys;
+    }
+
+    /**
+     * Determines if an array is a list.
+     *
+     * An array is a "list" if all array keys are sequential integers starting from 0 with no gaps in between.
+     *
+     * @param  array  $array
+     * @return bool
+     */
+    public static function isList($array)
+    {
+        return ! self::isAssoc($array);
+    }
+
+    /**
+     * Get a subset of the items from the given array.
+     *
+     * @param  array  $array
+     * @param  array|string  $keys
+     * @return array
+     */
+    public static function only($array, $keys)
+    {
+        return array_intersect_key($array, array_flip((array) $keys));
+    }
+
+    /**
+     * Pluck an array of values from an array.
+     *
+     * @param  iterable  $array
+     * @param  string|array|int|null  $value
+     * @param  string|array|null  $key
+     * @return array
+     */
+    public static function pluck($array, $value, $key = null)
+    {
+        $results = [];
+
+        [$value, $key] = static::explodePluckParameters($value, $key);
+
+        foreach ($array as $item) {
+            $itemValue = data_get($item, $value);
+
+            // If the key is "null", we will just append the value to the array and keep
+            // looping. Otherwise we will key the array using the value of the key we
+            // received from the developer. Then we'll return the final array form.
+            if (is_null($key)) {
+                $results[] = $itemValue;
+            } else {
+                $itemKey = data_get($item, $key);
+
+                if (is_object($itemKey) && method_exists($itemKey, '__toString')) {
+                    $itemKey = (string) $itemKey;
+                }
+
+                $results[$itemKey] = $itemValue;
+            }
+        }
+
+        return $results;
+    }
+
+    /**
+     * Explode the "value" and "key" arguments passed to "pluck".
+     *
+     * @param  string|array  $value
+     * @param  string|array|null  $key
+     * @return array
+     */
+    protected static function explodePluckParameters($value, $key)
+    {
+        $value = is_string($value) ? explode('.', $value) : $value;
+
+        $key = is_null($key) || is_array($key) ? $key : explode('.', $key);
+
+        return [$value, $key];
+    }
+
+    /**
+     * Push an item onto the beginning of an array.
+     *
+     * @param  array  $array
+     * @param  mixed  $value
+     * @param  mixed  $key
+     * @return array
+     */
+    public static function prepend($array, $value, $key = null)
+    {
+        if (func_num_args() == 2) {
+            array_unshift($array, $value);
+        } else {
+            $array = [$key => $value] + $array;
+        }
+
+        return $array;
+    }
+
+    /**
+     * Get a value from the array, and remove it.
+     *
+     * @param  array  $array
+     * @param  string|int  $key
+     * @param  mixed  $default
+     * @return mixed
+     */
+    public static function pull(&$array, $key, $default = null)
+    {
+        $value = static::get($array, $key, $default);
+
+        static::forget($array, $key);
+
+        return $value;
+    }
+
+    /**
+     * Convert the array into a query string.
+     *
+     * @param  array  $array
+     * @return string
+     */
+    public static function query($array)
+    {
+        return http_build_query($array, '', '&', PHP_QUERY_RFC3986);
+    }
+
+    /**
+     * Get one or a specified number of random values from an array.
+     *
+     * @param  array  $array
+     * @param  int|null  $number
+     * @param  bool|false  $preserveKeys
+     * @return mixed
+     *
+     * @throws \InvalidArgumentException
+     */
+    public static function random($array, $number = null, $preserveKeys = false)
+    {
+        $requested = is_null($number) ? 1 : $number;
+
+        $count = count($array);
+
+        if ($requested > $count) {
+            throw new InvalidArgumentException(
+                "You requested {$requested} items, but there are only {$count} items available."
+            );
+        }
+
+        if (is_null($number)) {
+            return $array[array_rand($array)];
+        }
+
+        if ((int) $number === 0) {
+            return [];
+        }
+
+        $keys = array_rand($array, $number);
+
+        $results = [];
+
+        if ($preserveKeys) {
+            foreach ((array) $keys as $key) {
+                $results[$key] = $array[$key];
+            }
+        } else {
+            foreach ((array) $keys as $key) {
+                $results[] = $array[$key];
+            }
+        }
+
+        return $results;
+    }
+
+    /**
+     * Set an array item to a given value using "dot" notation.
+     *
+     * If no key is given to the method, the entire array will be replaced.
+     *
+     * @param  array  $array
+     * @param  string|null  $key
+     * @param  mixed  $value
+     * @return array
+     */
+    public static function set(&$array, $key, $value)
+    {
+        if (is_null($key)) {
+            return $array = $value;
+        }
+
+        $keys = explode('.', $key);
+
+        foreach ($keys as $i => $key) {
+            if (count($keys) === 1) {
+                break;
+            }
+
+            unset($keys[$i]);
+
+            // If the key doesn't exist at this depth, we will just create an empty array
+            // to hold the next value, allowing us to create the arrays to hold final
+            // values at the correct depth. Then we'll keep digging into the array.
+            if (! isset($array[$key]) || ! is_array($array[$key])) {
+                $array[$key] = [];
+            }
+
+            $array = &$array[$key];
+        }
+
+        $array[array_shift($keys)] = $value;
+
+        return $array;
+    }
+
+    /**
+     * Shuffle the given array and return the result.
+     *
+     * @param  array  $array
+     * @param  int|null  $seed
+     * @return array
+     */
+    public static function shuffle($array, $seed = null)
+    {
+        if (is_null($seed)) {
+            shuffle($array);
+        } else {
+            mt_srand($seed);
+            shuffle($array);
+            mt_srand();
+        }
+
+        return $array;
+    }
+
+    /**
+     * Sort the array using the given callback or "dot" notation.
+     *
+     * @param  array  $array
+     * @param  callable|array|string|null  $callback
+     * @return array
+     */
+    public static function sort($array, $callback = null)
+    {
+        return Collection::make($array)->sortBy($callback)->all();
+    }
+
+    /**
+     * Recursively sort an array by keys and values.
+     *
+     * @param  array  $array
+     * @param  int  $options
+     * @param  bool  $descending
+     * @return array
+     */
+    public static function sortRecursive($array, $options = SORT_REGULAR, $descending = false)
+    {
+        foreach ($array as &$value) {
+            if (is_array($value)) {
+                $value = static::sortRecursive($value, $options, $descending);
+            }
+        }
+
+        if (static::isAssoc($array)) {
+            $descending
+                    ? krsort($array, $options)
+                    : ksort($array, $options);
+        } else {
+            $descending
+                    ? rsort($array, $options)
+                    : sort($array, $options);
+        }
+
+        return $array;
+    }
+
+    /**
+     * Conditionally compile classes from an array into a CSS class list.
+     *
+     * @param  array  $array
+     * @return string
+     */
+    public static function toCssClasses($array)
+    {
+        $classList = static::wrap($array);
+
+        $classes = [];
+
+        foreach ($classList as $class => $constraint) {
+            if (is_numeric($class)) {
+                $classes[] = $constraint;
+            } elseif ($constraint) {
+                $classes[] = $class;
+            }
+        }
+
+        return implode(' ', $classes);
+    }
+
+    /**
+     * Filter the array using the given callback.
+     *
+     * @param  array  $array
+     * @param  callable  $callback
+     * @return array
+     */
+    public static function where($array, callable $callback)
+    {
+        return array_filter($array, $callback, ARRAY_FILTER_USE_BOTH);
+    }
+
+    /**
+     * Filter items where the value is not null.
+     *
+     * @param  array  $array
+     * @return array
+     */
+    public static function whereNotNull($array)
+    {
+        return static::where($array, function ($value) {
+            return ! is_null($value);
+        });
+    }
+
+    /**
+     * If the given value is not an array and not null, wrap it in one.
+     *
+     * @param  mixed  $value
+     * @return array
+     */
+    public static function wrap($value)
+    {
+        if (is_null($value)) {
+            return [];
+        }
+
+        return is_array($value) ? $value : [$value];
+    }
+}

+ 1672 - 0
htdocs/includes/webklex/php-imap/vendor/illuminate/collections/Collection.php

@@ -0,0 +1,1672 @@
+<?php
+
+namespace Illuminate\Support;
+
+use ArrayAccess;
+use ArrayIterator;
+use Illuminate\Contracts\Support\CanBeEscapedWhenCastToString;
+use Illuminate\Support\Traits\EnumeratesValues;
+use Illuminate\Support\Traits\Macroable;
+use stdClass;
+
+class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerable
+{
+    use EnumeratesValues, Macroable;
+
+    /**
+     * The items contained in the collection.
+     *
+     * @var array
+     */
+    protected $items = [];
+
+    /**
+     * Create a new collection.
+     *
+     * @param  mixed  $items
+     * @return void
+     */
+    public function __construct($items = [])
+    {
+        $this->items = $this->getArrayableItems($items);
+    }
+
+    /**
+     * Create a collection with the given range.
+     *
+     * @param  int  $from
+     * @param  int  $to
+     * @return static
+     */
+    public static function range($from, $to)
+    {
+        return new static(range($from, $to));
+    }
+
+    /**
+     * Get all of the items in the collection.
+     *
+     * @return array
+     */
+    public function all()
+    {
+        return $this->items;
+    }
+
+    /**
+     * Get a lazy collection for the items in this collection.
+     *
+     * @return \Illuminate\Support\LazyCollection
+     */
+    public function lazy()
+    {
+        return new LazyCollection($this->items);
+    }
+
+    /**
+     * Get the average value of a given key.
+     *
+     * @param  callable|string|null  $callback
+     * @return mixed
+     */
+    public function avg($callback = null)
+    {
+        $callback = $this->valueRetriever($callback);
+
+        $items = $this->map(function ($value) use ($callback) {
+            return $callback($value);
+        })->filter(function ($value) {
+            return ! is_null($value);
+        });
+
+        if ($count = $items->count()) {
+            return $items->sum() / $count;
+        }
+    }
+
+    /**
+     * Get the median of a given key.
+     *
+     * @param  string|array|null  $key
+     * @return mixed
+     */
+    public function median($key = null)
+    {
+        $values = (isset($key) ? $this->pluck($key) : $this)
+            ->filter(function ($item) {
+                return ! is_null($item);
+            })->sort()->values();
+
+        $count = $values->count();
+
+        if ($count === 0) {
+            return;
+        }
+
+        $middle = (int) ($count / 2);
+
+        if ($count % 2) {
+            return $values->get($middle);
+        }
+
+        return (new static([
+            $values->get($middle - 1), $values->get($middle),
+        ]))->average();
+    }
+
+    /**
+     * Get the mode of a given key.
+     *
+     * @param  string|array|null  $key
+     * @return array|null
+     */
+    public function mode($key = null)
+    {
+        if ($this->count() === 0) {
+            return;
+        }
+
+        $collection = isset($key) ? $this->pluck($key) : $this;
+
+        $counts = new static;
+
+        $collection->each(function ($value) use ($counts) {
+            $counts[$value] = isset($counts[$value]) ? $counts[$value] + 1 : 1;
+        });
+
+        $sorted = $counts->sort();
+
+        $highestValue = $sorted->last();
+
+        return $sorted->filter(function ($value) use ($highestValue) {
+            return $value == $highestValue;
+        })->sort()->keys()->all();
+    }
+
+    /**
+     * Collapse the collection of items into a single array.
+     *
+     * @return static
+     */
+    public function collapse()
+    {
+        return new static(Arr::collapse($this->items));
+    }
+
+    /**
+     * Determine if an item exists in the collection.
+     *
+     * @param  mixed  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return bool
+     */
+    public function contains($key, $operator = null, $value = null)
+    {
+        if (func_num_args() === 1) {
+            if ($this->useAsCallable($key)) {
+                $placeholder = new stdClass;
+
+                return $this->first($key, $placeholder) !== $placeholder;
+            }
+
+            return in_array($key, $this->items);
+        }
+
+        return $this->contains($this->operatorForWhere(...func_get_args()));
+    }
+
+    /**
+     * Determine if an item is not contained in the collection.
+     *
+     * @param  mixed  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return bool
+     */
+    public function doesntContain($key, $operator = null, $value = null)
+    {
+        return ! $this->contains(...func_get_args());
+    }
+
+    /**
+     * Cross join with the given lists, returning all possible permutations.
+     *
+     * @param  mixed  ...$lists
+     * @return static
+     */
+    public function crossJoin(...$lists)
+    {
+        return new static(Arr::crossJoin(
+            $this->items, ...array_map([$this, 'getArrayableItems'], $lists)
+        ));
+    }
+
+    /**
+     * Get the items in the collection that are not present in the given items.
+     *
+     * @param  mixed  $items
+     * @return static
+     */
+    public function diff($items)
+    {
+        return new static(array_diff($this->items, $this->getArrayableItems($items)));
+    }
+
+    /**
+     * Get the items in the collection that are not present in the given items, using the callback.
+     *
+     * @param  mixed  $items
+     * @param  callable  $callback
+     * @return static
+     */
+    public function diffUsing($items, callable $callback)
+    {
+        return new static(array_udiff($this->items, $this->getArrayableItems($items), $callback));
+    }
+
+    /**
+     * Get the items in the collection whose keys and values are not present in the given items.
+     *
+     * @param  mixed  $items
+     * @return static
+     */
+    public function diffAssoc($items)
+    {
+        return new static(array_diff_assoc($this->items, $this->getArrayableItems($items)));
+    }
+
+    /**
+     * Get the items in the collection whose keys and values are not present in the given items, using the callback.
+     *
+     * @param  mixed  $items
+     * @param  callable  $callback
+     * @return static
+     */
+    public function diffAssocUsing($items, callable $callback)
+    {
+        return new static(array_diff_uassoc($this->items, $this->getArrayableItems($items), $callback));
+    }
+
+    /**
+     * Get the items in the collection whose keys are not present in the given items.
+     *
+     * @param  mixed  $items
+     * @return static
+     */
+    public function diffKeys($items)
+    {
+        return new static(array_diff_key($this->items, $this->getArrayableItems($items)));
+    }
+
+    /**
+     * Get the items in the collection whose keys are not present in the given items, using the callback.
+     *
+     * @param  mixed  $items
+     * @param  callable  $callback
+     * @return static
+     */
+    public function diffKeysUsing($items, callable $callback)
+    {
+        return new static(array_diff_ukey($this->items, $this->getArrayableItems($items), $callback));
+    }
+
+    /**
+     * Retrieve duplicate items from the collection.
+     *
+     * @param  callable|string|null  $callback
+     * @param  bool  $strict
+     * @return static
+     */
+    public function duplicates($callback = null, $strict = false)
+    {
+        $items = $this->map($this->valueRetriever($callback));
+
+        $uniqueItems = $items->unique(null, $strict);
+
+        $compare = $this->duplicateComparator($strict);
+
+        $duplicates = new static;
+
+        foreach ($items as $key => $value) {
+            if ($uniqueItems->isNotEmpty() && $compare($value, $uniqueItems->first())) {
+                $uniqueItems->shift();
+            } else {
+                $duplicates[$key] = $value;
+            }
+        }
+
+        return $duplicates;
+    }
+
+    /**
+     * Retrieve duplicate items from the collection using strict comparison.
+     *
+     * @param  callable|string|null  $callback
+     * @return static
+     */
+    public function duplicatesStrict($callback = null)
+    {
+        return $this->duplicates($callback, true);
+    }
+
+    /**
+     * Get the comparison function to detect duplicates.
+     *
+     * @param  bool  $strict
+     * @return \Closure
+     */
+    protected function duplicateComparator($strict)
+    {
+        if ($strict) {
+            return function ($a, $b) {
+                return $a === $b;
+            };
+        }
+
+        return function ($a, $b) {
+            return $a == $b;
+        };
+    }
+
+    /**
+     * Get all items except for those with the specified keys.
+     *
+     * @param  \Illuminate\Support\Collection|mixed  $keys
+     * @return static
+     */
+    public function except($keys)
+    {
+        if ($keys instanceof Enumerable) {
+            $keys = $keys->all();
+        } elseif (! is_array($keys)) {
+            $keys = func_get_args();
+        }
+
+        return new static(Arr::except($this->items, $keys));
+    }
+
+    /**
+     * Run a filter over each of the items.
+     *
+     * @param  callable|null  $callback
+     * @return static
+     */
+    public function filter(callable $callback = null)
+    {
+        if ($callback) {
+            return new static(Arr::where($this->items, $callback));
+        }
+
+        return new static(array_filter($this->items));
+    }
+
+    /**
+     * Get the first item from the collection passing the given truth test.
+     *
+     * @param  callable|null  $callback
+     * @param  mixed  $default
+     * @return mixed
+     */
+    public function first(callable $callback = null, $default = null)
+    {
+        return Arr::first($this->items, $callback, $default);
+    }
+
+    /**
+     * Get a flattened array of the items in the collection.
+     *
+     * @param  int  $depth
+     * @return static
+     */
+    public function flatten($depth = INF)
+    {
+        return new static(Arr::flatten($this->items, $depth));
+    }
+
+    /**
+     * Flip the items in the collection.
+     *
+     * @return static
+     */
+    public function flip()
+    {
+        return new static(array_flip($this->items));
+    }
+
+    /**
+     * Remove an item from the collection by key.
+     *
+     * @param  string|int|array  $keys
+     * @return $this
+     */
+    public function forget($keys)
+    {
+        foreach ((array) $keys as $key) {
+            $this->offsetUnset($key);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get an item from the collection by key.
+     *
+     * @param  mixed  $key
+     * @param  mixed  $default
+     * @return mixed
+     */
+    public function get($key, $default = null)
+    {
+        if (array_key_exists($key, $this->items)) {
+            return $this->items[$key];
+        }
+
+        return value($default);
+    }
+
+    /**
+     * Get an item from the collection by key or add it to collection if it does not exist.
+     *
+     * @param  mixed  $key
+     * @param  mixed  $value
+     * @return mixed
+     */
+    public function getOrPut($key, $value)
+    {
+        if (array_key_exists($key, $this->items)) {
+            return $this->items[$key];
+        }
+
+        $this->offsetSet($key, $value = value($value));
+
+        return $value;
+    }
+
+    /**
+     * Group an associative array by a field or using a callback.
+     *
+     * @param  array|callable|string  $groupBy
+     * @param  bool  $preserveKeys
+     * @return static
+     */
+    public function groupBy($groupBy, $preserveKeys = false)
+    {
+        if (! $this->useAsCallable($groupBy) && is_array($groupBy)) {
+            $nextGroups = $groupBy;
+
+            $groupBy = array_shift($nextGroups);
+        }
+
+        $groupBy = $this->valueRetriever($groupBy);
+
+        $results = [];
+
+        foreach ($this->items as $key => $value) {
+            $groupKeys = $groupBy($value, $key);
+
+            if (! is_array($groupKeys)) {
+                $groupKeys = [$groupKeys];
+            }
+
+            foreach ($groupKeys as $groupKey) {
+                $groupKey = is_bool($groupKey) ? (int) $groupKey : $groupKey;
+
+                if (! array_key_exists($groupKey, $results)) {
+                    $results[$groupKey] = new static;
+                }
+
+                $results[$groupKey]->offsetSet($preserveKeys ? $key : null, $value);
+            }
+        }
+
+        $result = new static($results);
+
+        if (! empty($nextGroups)) {
+            return $result->map->groupBy($nextGroups, $preserveKeys);
+        }
+
+        return $result;
+    }
+
+    /**
+     * Key an associative array by a field or using a callback.
+     *
+     * @param  callable|string  $keyBy
+     * @return static
+     */
+    public function keyBy($keyBy)
+    {
+        $keyBy = $this->valueRetriever($keyBy);
+
+        $results = [];
+
+        foreach ($this->items as $key => $item) {
+            $resolvedKey = $keyBy($item, $key);
+
+            if (is_object($resolvedKey)) {
+                $resolvedKey = (string) $resolvedKey;
+            }
+
+            $results[$resolvedKey] = $item;
+        }
+
+        return new static($results);
+    }
+
+    /**
+     * Determine if an item exists in the collection by key.
+     *
+     * @param  mixed  $key
+     * @return bool
+     */
+    public function has($key)
+    {
+        $keys = is_array($key) ? $key : func_get_args();
+
+        foreach ($keys as $value) {
+            if (! array_key_exists($value, $this->items)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Determine if any of the keys exist in the collection.
+     *
+     * @param  mixed  $key
+     * @return bool
+     */
+    public function hasAny($key)
+    {
+        if ($this->isEmpty()) {
+            return false;
+        }
+
+        $keys = is_array($key) ? $key : func_get_args();
+
+        foreach ($keys as $value) {
+            if ($this->has($value)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Concatenate values of a given key as a string.
+     *
+     * @param  string  $value
+     * @param  string|null  $glue
+     * @return string
+     */
+    public function implode($value, $glue = null)
+    {
+        $first = $this->first();
+
+        if (is_array($first) || (is_object($first) && ! $first instanceof Stringable)) {
+            return implode($glue ?? '', $this->pluck($value)->all());
+        }
+
+        return implode($value ?? '', $this->items);
+    }
+
+    /**
+     * Intersect the collection with the given items.
+     *
+     * @param  mixed  $items
+     * @return static
+     */
+    public function intersect($items)
+    {
+        return new static(array_intersect($this->items, $this->getArrayableItems($items)));
+    }
+
+    /**
+     * Intersect the collection with the given items by key.
+     *
+     * @param  mixed  $items
+     * @return static
+     */
+    public function intersectByKeys($items)
+    {
+        return new static(array_intersect_key(
+            $this->items, $this->getArrayableItems($items)
+        ));
+    }
+
+    /**
+     * Determine if the collection is empty or not.
+     *
+     * @return bool
+     */
+    public function isEmpty()
+    {
+        return empty($this->items);
+    }
+
+    /**
+     * Determine if the collection contains a single item.
+     *
+     * @return bool
+     */
+    public function containsOneItem()
+    {
+        return $this->count() === 1;
+    }
+
+    /**
+     * Join all items from the collection using a string. The final items can use a separate glue string.
+     *
+     * @param  string  $glue
+     * @param  string  $finalGlue
+     * @return string
+     */
+    public function join($glue, $finalGlue = '')
+    {
+        if ($finalGlue === '') {
+            return $this->implode($glue);
+        }
+
+        $count = $this->count();
+
+        if ($count === 0) {
+            return '';
+        }
+
+        if ($count === 1) {
+            return $this->last();
+        }
+
+        $collection = new static($this->items);
+
+        $finalItem = $collection->pop();
+
+        return $collection->implode($glue).$finalGlue.$finalItem;
+    }
+
+    /**
+     * Get the keys of the collection items.
+     *
+     * @return static
+     */
+    public function keys()
+    {
+        return new static(array_keys($this->items));
+    }
+
+    /**
+     * Get the last item from the collection.
+     *
+     * @param  callable|null  $callback
+     * @param  mixed  $default
+     * @return mixed
+     */
+    public function last(callable $callback = null, $default = null)
+    {
+        return Arr::last($this->items, $callback, $default);
+    }
+
+    /**
+     * Get the values of a given key.
+     *
+     * @param  string|array|int|null  $value
+     * @param  string|null  $key
+     * @return static
+     */
+    public function pluck($value, $key = null)
+    {
+        return new static(Arr::pluck($this->items, $value, $key));
+    }
+
+    /**
+     * Run a map over each of the items.
+     *
+     * @param  callable  $callback
+     * @return static
+     */
+    public function map(callable $callback)
+    {
+        $keys = array_keys($this->items);
+
+        $items = array_map($callback, $this->items, $keys);
+
+        return new static(array_combine($keys, $items));
+    }
+
+    /**
+     * Run a dictionary map over the items.
+     *
+     * The callback should return an associative array with a single key/value pair.
+     *
+     * @param  callable  $callback
+     * @return static
+     */
+    public function mapToDictionary(callable $callback)
+    {
+        $dictionary = [];
+
+        foreach ($this->items as $key => $item) {
+            $pair = $callback($item, $key);
+
+            $key = key($pair);
+
+            $value = reset($pair);
+
+            if (! isset($dictionary[$key])) {
+                $dictionary[$key] = [];
+            }
+
+            $dictionary[$key][] = $value;
+        }
+
+        return new static($dictionary);
+    }
+
+    /**
+     * Run an associative map over each of the items.
+     *
+     * The callback should return an associative array with a single key/value pair.
+     *
+     * @param  callable  $callback
+     * @return static
+     */
+    public function mapWithKeys(callable $callback)
+    {
+        $result = [];
+
+        foreach ($this->items as $key => $value) {
+            $assoc = $callback($value, $key);
+
+            foreach ($assoc as $mapKey => $mapValue) {
+                $result[$mapKey] = $mapValue;
+            }
+        }
+
+        return new static($result);
+    }
+
+    /**
+     * Merge the collection with the given items.
+     *
+     * @param  mixed  $items
+     * @return static
+     */
+    public function merge($items)
+    {
+        return new static(array_merge($this->items, $this->getArrayableItems($items)));
+    }
+
+    /**
+     * Recursively merge the collection with the given items.
+     *
+     * @param  mixed  $items
+     * @return static
+     */
+    public function mergeRecursive($items)
+    {
+        return new static(array_merge_recursive($this->items, $this->getArrayableItems($items)));
+    }
+
+    /**
+     * Create a collection by using this collection for keys and another for its values.
+     *
+     * @param  mixed  $values
+     * @return static
+     */
+    public function combine($values)
+    {
+        return new static(array_combine($this->all(), $this->getArrayableItems($values)));
+    }
+
+    /**
+     * Union the collection with the given items.
+     *
+     * @param  mixed  $items
+     * @return static
+     */
+    public function union($items)
+    {
+        return new static($this->items + $this->getArrayableItems($items));
+    }
+
+    /**
+     * Create a new collection consisting of every n-th element.
+     *
+     * @param  int  $step
+     * @param  int  $offset
+     * @return static
+     */
+    public function nth($step, $offset = 0)
+    {
+        $new = [];
+
+        $position = 0;
+
+        foreach ($this->slice($offset)->items as $item) {
+            if ($position % $step === 0) {
+                $new[] = $item;
+            }
+
+            $position++;
+        }
+
+        return new static($new);
+    }
+
+    /**
+     * Get the items with the specified keys.
+     *
+     * @param  mixed  $keys
+     * @return static
+     */
+    public function only($keys)
+    {
+        if (is_null($keys)) {
+            return new static($this->items);
+        }
+
+        if ($keys instanceof Enumerable) {
+            $keys = $keys->all();
+        }
+
+        $keys = is_array($keys) ? $keys : func_get_args();
+
+        return new static(Arr::only($this->items, $keys));
+    }
+
+    /**
+     * Get and remove the last N items from the collection.
+     *
+     * @param  int  $count
+     * @return mixed
+     */
+    public function pop($count = 1)
+    {
+        if ($count === 1) {
+            return array_pop($this->items);
+        }
+
+        if ($this->isEmpty()) {
+            return new static;
+        }
+
+        $results = [];
+
+        $collectionCount = $this->count();
+
+        foreach (range(1, min($count, $collectionCount)) as $item) {
+            array_push($results, array_pop($this->items));
+        }
+
+        return new static($results);
+    }
+
+    /**
+     * Push an item onto the beginning of the collection.
+     *
+     * @param  mixed  $value
+     * @param  mixed  $key
+     * @return $this
+     */
+    public function prepend($value, $key = null)
+    {
+        $this->items = Arr::prepend($this->items, ...func_get_args());
+
+        return $this;
+    }
+
+    /**
+     * Push one or more items onto the end of the collection.
+     *
+     * @param  mixed  $values
+     * @return $this
+     */
+    public function push(...$values)
+    {
+        foreach ($values as $value) {
+            $this->items[] = $value;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Push all of the given items onto the collection.
+     *
+     * @param  iterable  $source
+     * @return static
+     */
+    public function concat($source)
+    {
+        $result = new static($this);
+
+        foreach ($source as $item) {
+            $result->push($item);
+        }
+
+        return $result;
+    }
+
+    /**
+     * Get and remove an item from the collection.
+     *
+     * @param  mixed  $key
+     * @param  mixed  $default
+     * @return mixed
+     */
+    public function pull($key, $default = null)
+    {
+        return Arr::pull($this->items, $key, $default);
+    }
+
+    /**
+     * Put an item in the collection by key.
+     *
+     * @param  mixed  $key
+     * @param  mixed  $value
+     * @return $this
+     */
+    public function put($key, $value)
+    {
+        $this->offsetSet($key, $value);
+
+        return $this;
+    }
+
+    /**
+     * Get one or a specified number of items randomly from the collection.
+     *
+     * @param  int|null  $number
+     * @return static|mixed
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function random($number = null)
+    {
+        if (is_null($number)) {
+            return Arr::random($this->items);
+        }
+
+        return new static(Arr::random($this->items, $number));
+    }
+
+    /**
+     * Replace the collection items with the given items.
+     *
+     * @param  mixed  $items
+     * @return static
+     */
+    public function replace($items)
+    {
+        return new static(array_replace($this->items, $this->getArrayableItems($items)));
+    }
+
+    /**
+     * Recursively replace the collection items with the given items.
+     *
+     * @param  mixed  $items
+     * @return static
+     */
+    public function replaceRecursive($items)
+    {
+        return new static(array_replace_recursive($this->items, $this->getArrayableItems($items)));
+    }
+
+    /**
+     * Reverse items order.
+     *
+     * @return static
+     */
+    public function reverse()
+    {
+        return new static(array_reverse($this->items, true));
+    }
+
+    /**
+     * Search the collection for a given value and return the corresponding key if successful.
+     *
+     * @param  mixed  $value
+     * @param  bool  $strict
+     * @return mixed
+     */
+    public function search($value, $strict = false)
+    {
+        if (! $this->useAsCallable($value)) {
+            return array_search($value, $this->items, $strict);
+        }
+
+        foreach ($this->items as $key => $item) {
+            if ($value($item, $key)) {
+                return $key;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Get and remove the first N items from the collection.
+     *
+     * @param  int  $count
+     * @return mixed
+     */
+    public function shift($count = 1)
+    {
+        if ($count === 1) {
+            return array_shift($this->items);
+        }
+
+        if ($this->isEmpty()) {
+            return new static;
+        }
+
+        $results = [];
+
+        $collectionCount = $this->count();
+
+        foreach (range(1, min($count, $collectionCount)) as $item) {
+            array_push($results, array_shift($this->items));
+        }
+
+        return new static($results);
+    }
+
+    /**
+     * Shuffle the items in the collection.
+     *
+     * @param  int|null  $seed
+     * @return static
+     */
+    public function shuffle($seed = null)
+    {
+        return new static(Arr::shuffle($this->items, $seed));
+    }
+
+    /**
+     * Create chunks representing a "sliding window" view of the items in the collection.
+     *
+     * @param  int  $size
+     * @param  int  $step
+     * @return static
+     */
+    public function sliding($size = 2, $step = 1)
+    {
+        $chunks = floor(($this->count() - $size) / $step) + 1;
+
+        return static::times($chunks, function ($number) use ($size, $step) {
+            return $this->slice(($number - 1) * $step, $size);
+        });
+    }
+
+    /**
+     * Skip the first {$count} items.
+     *
+     * @param  int  $count
+     * @return static
+     */
+    public function skip($count)
+    {
+        return $this->slice($count);
+    }
+
+    /**
+     * Skip items in the collection until the given condition is met.
+     *
+     * @param  mixed  $value
+     * @return static
+     */
+    public function skipUntil($value)
+    {
+        return new static($this->lazy()->skipUntil($value)->all());
+    }
+
+    /**
+     * Skip items in the collection while the given condition is met.
+     *
+     * @param  mixed  $value
+     * @return static
+     */
+    public function skipWhile($value)
+    {
+        return new static($this->lazy()->skipWhile($value)->all());
+    }
+
+    /**
+     * Slice the underlying collection array.
+     *
+     * @param  int  $offset
+     * @param  int|null  $length
+     * @return static
+     */
+    public function slice($offset, $length = null)
+    {
+        return new static(array_slice($this->items, $offset, $length, true));
+    }
+
+    /**
+     * Split a collection into a certain number of groups.
+     *
+     * @param  int  $numberOfGroups
+     * @return static
+     */
+    public function split($numberOfGroups)
+    {
+        if ($this->isEmpty()) {
+            return new static;
+        }
+
+        $groups = new static;
+
+        $groupSize = floor($this->count() / $numberOfGroups);
+
+        $remain = $this->count() % $numberOfGroups;
+
+        $start = 0;
+
+        for ($i = 0; $i < $numberOfGroups; $i++) {
+            $size = $groupSize;
+
+            if ($i < $remain) {
+                $size++;
+            }
+
+            if ($size) {
+                $groups->push(new static(array_slice($this->items, $start, $size)));
+
+                $start += $size;
+            }
+        }
+
+        return $groups;
+    }
+
+    /**
+     * Split a collection into a certain number of groups, and fill the first groups completely.
+     *
+     * @param  int  $numberOfGroups
+     * @return static
+     */
+    public function splitIn($numberOfGroups)
+    {
+        return $this->chunk(ceil($this->count() / $numberOfGroups));
+    }
+
+    /**
+     * Get the first item in the collection, but only if exactly one item exists. Otherwise, throw an exception.
+     *
+     * @param  mixed  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return mixed
+     *
+     * @throws \Illuminate\Support\ItemNotFoundException
+     * @throws \Illuminate\Support\MultipleItemsFoundException
+     */
+    public function sole($key = null, $operator = null, $value = null)
+    {
+        $filter = func_num_args() > 1
+            ? $this->operatorForWhere(...func_get_args())
+            : $key;
+
+        $items = $this->when($filter)->filter($filter);
+
+        if ($items->isEmpty()) {
+            throw new ItemNotFoundException;
+        }
+
+        if ($items->count() > 1) {
+            throw new MultipleItemsFoundException;
+        }
+
+        return $items->first();
+    }
+
+    /**
+     * Get the first item in the collection but throw an exception if no matching items exist.
+     *
+     * @param  mixed  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return mixed
+     *
+     * @throws \Illuminate\Support\ItemNotFoundException
+     */
+    public function firstOrFail($key = null, $operator = null, $value = null)
+    {
+        $filter = func_num_args() > 1
+            ? $this->operatorForWhere(...func_get_args())
+            : $key;
+
+        $placeholder = new stdClass();
+
+        $item = $this->first($filter, $placeholder);
+
+        if ($item === $placeholder) {
+            throw new ItemNotFoundException;
+        }
+
+        return $item;
+    }
+
+    /**
+     * Chunk the collection into chunks of the given size.
+     *
+     * @param  int  $size
+     * @return static
+     */
+    public function chunk($size)
+    {
+        if ($size <= 0) {
+            return new static;
+        }
+
+        $chunks = [];
+
+        foreach (array_chunk($this->items, $size, true) as $chunk) {
+            $chunks[] = new static($chunk);
+        }
+
+        return new static($chunks);
+    }
+
+    /**
+     * Chunk the collection into chunks with a callback.
+     *
+     * @param  callable  $callback
+     * @return static
+     */
+    public function chunkWhile(callable $callback)
+    {
+        return new static(
+            $this->lazy()->chunkWhile($callback)->mapInto(static::class)
+        );
+    }
+
+    /**
+     * Sort through each item with a callback.
+     *
+     * @param  callable|int|null  $callback
+     * @return static
+     */
+    public function sort($callback = null)
+    {
+        $items = $this->items;
+
+        $callback && is_callable($callback)
+            ? uasort($items, $callback)
+            : asort($items, $callback ?? SORT_REGULAR);
+
+        return new static($items);
+    }
+
+    /**
+     * Sort items in descending order.
+     *
+     * @param  int  $options
+     * @return static
+     */
+    public function sortDesc($options = SORT_REGULAR)
+    {
+        $items = $this->items;
+
+        arsort($items, $options);
+
+        return new static($items);
+    }
+
+    /**
+     * Sort the collection using the given callback.
+     *
+     * @param  callable|array|string  $callback
+     * @param  int  $options
+     * @param  bool  $descending
+     * @return static
+     */
+    public function sortBy($callback, $options = SORT_REGULAR, $descending = false)
+    {
+        if (is_array($callback) && ! is_callable($callback)) {
+            return $this->sortByMany($callback);
+        }
+
+        $results = [];
+
+        $callback = $this->valueRetriever($callback);
+
+        // First we will loop through the items and get the comparator from a callback
+        // function which we were given. Then, we will sort the returned values and
+        // grab all the corresponding values for the sorted keys from this array.
+        foreach ($this->items as $key => $value) {
+            $results[$key] = $callback($value, $key);
+        }
+
+        $descending ? arsort($results, $options)
+            : asort($results, $options);
+
+        // Once we have sorted all of the keys in the array, we will loop through them
+        // and grab the corresponding model so we can set the underlying items list
+        // to the sorted version. Then we'll just return the collection instance.
+        foreach (array_keys($results) as $key) {
+            $results[$key] = $this->items[$key];
+        }
+
+        return new static($results);
+    }
+
+    /**
+     * Sort the collection using multiple comparisons.
+     *
+     * @param  array  $comparisons
+     * @return static
+     */
+    protected function sortByMany(array $comparisons = [])
+    {
+        $items = $this->items;
+
+        usort($items, function ($a, $b) use ($comparisons) {
+            foreach ($comparisons as $comparison) {
+                $comparison = Arr::wrap($comparison);
+
+                $prop = $comparison[0];
+
+                $ascending = Arr::get($comparison, 1, true) === true ||
+                             Arr::get($comparison, 1, true) === 'asc';
+
+                $result = 0;
+
+                if (! is_string($prop) && is_callable($prop)) {
+                    $result = $prop($a, $b);
+                } else {
+                    $values = [data_get($a, $prop), data_get($b, $prop)];
+
+                    if (! $ascending) {
+                        $values = array_reverse($values);
+                    }
+
+                    $result = $values[0] <=> $values[1];
+                }
+
+                if ($result === 0) {
+                    continue;
+                }
+
+                return $result;
+            }
+        });
+
+        return new static($items);
+    }
+
+    /**
+     * Sort the collection in descending order using the given callback.
+     *
+     * @param  callable|string  $callback
+     * @param  int  $options
+     * @return static
+     */
+    public function sortByDesc($callback, $options = SORT_REGULAR)
+    {
+        return $this->sortBy($callback, $options, true);
+    }
+
+    /**
+     * Sort the collection keys.
+     *
+     * @param  int  $options
+     * @param  bool  $descending
+     * @return static
+     */
+    public function sortKeys($options = SORT_REGULAR, $descending = false)
+    {
+        $items = $this->items;
+
+        $descending ? krsort($items, $options) : ksort($items, $options);
+
+        return new static($items);
+    }
+
+    /**
+     * Sort the collection keys in descending order.
+     *
+     * @param  int  $options
+     * @return static
+     */
+    public function sortKeysDesc($options = SORT_REGULAR)
+    {
+        return $this->sortKeys($options, true);
+    }
+
+    /**
+     * Sort the collection keys using a callback.
+     *
+     * @param  callable  $callback
+     * @return static
+     */
+    public function sortKeysUsing(callable $callback)
+    {
+        $items = $this->items;
+
+        uksort($items, $callback);
+
+        return new static($items);
+    }
+
+    /**
+     * Splice a portion of the underlying collection array.
+     *
+     * @param  int  $offset
+     * @param  int|null  $length
+     * @param  mixed  $replacement
+     * @return static
+     */
+    public function splice($offset, $length = null, $replacement = [])
+    {
+        if (func_num_args() === 1) {
+            return new static(array_splice($this->items, $offset));
+        }
+
+        return new static(array_splice($this->items, $offset, $length, $this->getArrayableItems($replacement)));
+    }
+
+    /**
+     * Take the first or last {$limit} items.
+     *
+     * @param  int  $limit
+     * @return static
+     */
+    public function take($limit)
+    {
+        if ($limit < 0) {
+            return $this->slice($limit, abs($limit));
+        }
+
+        return $this->slice(0, $limit);
+    }
+
+    /**
+     * Take items in the collection until the given condition is met.
+     *
+     * @param  mixed  $value
+     * @return static
+     */
+    public function takeUntil($value)
+    {
+        return new static($this->lazy()->takeUntil($value)->all());
+    }
+
+    /**
+     * Take items in the collection while the given condition is met.
+     *
+     * @param  mixed  $value
+     * @return static
+     */
+    public function takeWhile($value)
+    {
+        return new static($this->lazy()->takeWhile($value)->all());
+    }
+
+    /**
+     * Transform each item in the collection using a callback.
+     *
+     * @param  callable  $callback
+     * @return $this
+     */
+    public function transform(callable $callback)
+    {
+        $this->items = $this->map($callback)->all();
+
+        return $this;
+    }
+
+    /**
+     * Convert a flatten "dot" notation array into an expanded array.
+     *
+     * @return static
+     */
+    public function undot()
+    {
+        return new static(Arr::undot($this->all()));
+    }
+
+    /**
+     * Return only unique items from the collection array.
+     *
+     * @param  string|callable|null  $key
+     * @param  bool  $strict
+     * @return static
+     */
+    public function unique($key = null, $strict = false)
+    {
+        if (is_null($key) && $strict === false) {
+            return new static(array_unique($this->items, SORT_REGULAR));
+        }
+
+        $callback = $this->valueRetriever($key);
+
+        $exists = [];
+
+        return $this->reject(function ($item, $key) use ($callback, $strict, &$exists) {
+            if (in_array($id = $callback($item, $key), $exists, $strict)) {
+                return true;
+            }
+
+            $exists[] = $id;
+        });
+    }
+
+    /**
+     * Reset the keys on the underlying array.
+     *
+     * @return static
+     */
+    public function values()
+    {
+        return new static(array_values($this->items));
+    }
+
+    /**
+     * Zip the collection together with one or more arrays.
+     *
+     * e.g. new Collection([1, 2, 3])->zip([4, 5, 6]);
+     *      => [[1, 4], [2, 5], [3, 6]]
+     *
+     * @param  mixed  ...$items
+     * @return static
+     */
+    public function zip($items)
+    {
+        $arrayableItems = array_map(function ($items) {
+            return $this->getArrayableItems($items);
+        }, func_get_args());
+
+        $params = array_merge([function () {
+            return new static(func_get_args());
+        }, $this->items], $arrayableItems);
+
+        return new static(array_map(...$params));
+    }
+
+    /**
+     * Pad collection to the specified length with a value.
+     *
+     * @param  int  $size
+     * @param  mixed  $value
+     * @return static
+     */
+    public function pad($size, $value)
+    {
+        return new static(array_pad($this->items, $size, $value));
+    }
+
+    /**
+     * Get an iterator for the items.
+     *
+     * @return \ArrayIterator
+     */
+    #[\ReturnTypeWillChange]
+    public function getIterator()
+    {
+        return new ArrayIterator($this->items);
+    }
+
+    /**
+     * Count the number of items in the collection.
+     *
+     * @return int
+     */
+    #[\ReturnTypeWillChange]
+    public function count()
+    {
+        return count($this->items);
+    }
+
+    /**
+     * Count the number of items in the collection by a field or using a callback.
+     *
+     * @param  callable|string  $countBy
+     * @return static
+     */
+    public function countBy($countBy = null)
+    {
+        return new static($this->lazy()->countBy($countBy)->all());
+    }
+
+    /**
+     * Add an item to the collection.
+     *
+     * @param  mixed  $item
+     * @return $this
+     */
+    public function add($item)
+    {
+        $this->items[] = $item;
+
+        return $this;
+    }
+
+    /**
+     * Get a base Support collection instance from this collection.
+     *
+     * @return \Illuminate\Support\Collection
+     */
+    public function toBase()
+    {
+        return new self($this);
+    }
+
+    /**
+     * Determine if an item exists at an offset.
+     *
+     * @param  mixed  $key
+     * @return bool
+     */
+    #[\ReturnTypeWillChange]
+    public function offsetExists($key)
+    {
+        return isset($this->items[$key]);
+    }
+
+    /**
+     * Get an item at a given offset.
+     *
+     * @param  mixed  $key
+     * @return mixed
+     */
+    #[\ReturnTypeWillChange]
+    public function offsetGet($key)
+    {
+        return $this->items[$key];
+    }
+
+    /**
+     * Set the item at a given offset.
+     *
+     * @param  mixed  $key
+     * @param  mixed  $value
+     * @return void
+     */
+    #[\ReturnTypeWillChange]
+    public function offsetSet($key, $value)
+    {
+        if (is_null($key)) {
+            $this->items[] = $value;
+        } else {
+            $this->items[$key] = $value;
+        }
+    }
+
+    /**
+     * Unset the item at a given offset.
+     *
+     * @param  mixed  $key
+     * @return void
+     */
+    #[\ReturnTypeWillChange]
+    public function offsetUnset($key)
+    {
+        unset($this->items[$key]);
+    }
+}

+ 1027 - 0
htdocs/includes/webklex/php-imap/vendor/illuminate/collections/Enumerable.php

@@ -0,0 +1,1027 @@
+<?php
+
+namespace Illuminate\Support;
+
+use Countable;
+use Illuminate\Contracts\Support\Arrayable;
+use Illuminate\Contracts\Support\Jsonable;
+use IteratorAggregate;
+use JsonSerializable;
+
+interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, JsonSerializable
+{
+    /**
+     * Create a new collection instance if the value isn't one already.
+     *
+     * @param  mixed  $items
+     * @return static
+     */
+    public static function make($items = []);
+
+    /**
+     * Create a new instance by invoking the callback a given amount of times.
+     *
+     * @param  int  $number
+     * @param  callable|null  $callback
+     * @return static
+     */
+    public static function times($number, callable $callback = null);
+
+    /**
+     * Create a collection with the given range.
+     *
+     * @param  int  $from
+     * @param  int  $to
+     * @return static
+     */
+    public static function range($from, $to);
+
+    /**
+     * Wrap the given value in a collection if applicable.
+     *
+     * @param  mixed  $value
+     * @return static
+     */
+    public static function wrap($value);
+
+    /**
+     * Get the underlying items from the given collection if applicable.
+     *
+     * @param  array|static  $value
+     * @return array
+     */
+    public static function unwrap($value);
+
+    /**
+     * Create a new instance with no items.
+     *
+     * @return static
+     */
+    public static function empty();
+
+    /**
+     * Get all items in the enumerable.
+     *
+     * @return array
+     */
+    public function all();
+
+    /**
+     * Alias for the "avg" method.
+     *
+     * @param  callable|string|null  $callback
+     * @return mixed
+     */
+    public function average($callback = null);
+
+    /**
+     * Get the median of a given key.
+     *
+     * @param  string|array|null  $key
+     * @return mixed
+     */
+    public function median($key = null);
+
+    /**
+     * Get the mode of a given key.
+     *
+     * @param  string|array|null  $key
+     * @return array|null
+     */
+    public function mode($key = null);
+
+    /**
+     * Collapse the items into a single enumerable.
+     *
+     * @return static
+     */
+    public function collapse();
+
+    /**
+     * Alias for the "contains" method.
+     *
+     * @param  mixed  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return bool
+     */
+    public function some($key, $operator = null, $value = null);
+
+    /**
+     * Determine if an item exists, using strict comparison.
+     *
+     * @param  mixed  $key
+     * @param  mixed  $value
+     * @return bool
+     */
+    public function containsStrict($key, $value = null);
+
+    /**
+     * Get the average value of a given key.
+     *
+     * @param  callable|string|null  $callback
+     * @return mixed
+     */
+    public function avg($callback = null);
+
+    /**
+     * Determine if an item exists in the enumerable.
+     *
+     * @param  mixed  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return bool
+     */
+    public function contains($key, $operator = null, $value = null);
+
+    /**
+     * Cross join with the given lists, returning all possible permutations.
+     *
+     * @param  mixed  ...$lists
+     * @return static
+     */
+    public function crossJoin(...$lists);
+
+    /**
+     * Dump the collection and end the script.
+     *
+     * @param  mixed  ...$args
+     * @return void
+     */
+    public function dd(...$args);
+
+    /**
+     * Dump the collection.
+     *
+     * @return $this
+     */
+    public function dump();
+
+    /**
+     * Get the items that are not present in the given items.
+     *
+     * @param  mixed  $items
+     * @return static
+     */
+    public function diff($items);
+
+    /**
+     * Get the items that are not present in the given items, using the callback.
+     *
+     * @param  mixed  $items
+     * @param  callable  $callback
+     * @return static
+     */
+    public function diffUsing($items, callable $callback);
+
+    /**
+     * Get the items whose keys and values are not present in the given items.
+     *
+     * @param  mixed  $items
+     * @return static
+     */
+    public function diffAssoc($items);
+
+    /**
+     * Get the items whose keys and values are not present in the given items, using the callback.
+     *
+     * @param  mixed  $items
+     * @param  callable  $callback
+     * @return static
+     */
+    public function diffAssocUsing($items, callable $callback);
+
+    /**
+     * Get the items whose keys are not present in the given items.
+     *
+     * @param  mixed  $items
+     * @return static
+     */
+    public function diffKeys($items);
+
+    /**
+     * Get the items whose keys are not present in the given items, using the callback.
+     *
+     * @param  mixed  $items
+     * @param  callable  $callback
+     * @return static
+     */
+    public function diffKeysUsing($items, callable $callback);
+
+    /**
+     * Retrieve duplicate items.
+     *
+     * @param  callable|string|null  $callback
+     * @param  bool  $strict
+     * @return static
+     */
+    public function duplicates($callback = null, $strict = false);
+
+    /**
+     * Retrieve duplicate items using strict comparison.
+     *
+     * @param  callable|string|null  $callback
+     * @return static
+     */
+    public function duplicatesStrict($callback = null);
+
+    /**
+     * Execute a callback over each item.
+     *
+     * @param  callable  $callback
+     * @return $this
+     */
+    public function each(callable $callback);
+
+    /**
+     * Execute a callback over each nested chunk of items.
+     *
+     * @param  callable  $callback
+     * @return static
+     */
+    public function eachSpread(callable $callback);
+
+    /**
+     * Determine if all items pass the given truth test.
+     *
+     * @param  string|callable  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return bool
+     */
+    public function every($key, $operator = null, $value = null);
+
+    /**
+     * Get all items except for those with the specified keys.
+     *
+     * @param  mixed  $keys
+     * @return static
+     */
+    public function except($keys);
+
+    /**
+     * Run a filter over each of the items.
+     *
+     * @param  callable|null  $callback
+     * @return static
+     */
+    public function filter(callable $callback = null);
+
+    /**
+     * Apply the callback if the value is truthy.
+     *
+     * @param  bool  $value
+     * @param  callable  $callback
+     * @param  callable|null  $default
+     * @return static|mixed
+     */
+    public function when($value, callable $callback, callable $default = null);
+
+    /**
+     * Apply the callback if the collection is empty.
+     *
+     * @param  callable  $callback
+     * @param  callable|null  $default
+     * @return static|mixed
+     */
+    public function whenEmpty(callable $callback, callable $default = null);
+
+    /**
+     * Apply the callback if the collection is not empty.
+     *
+     * @param  callable  $callback
+     * @param  callable|null  $default
+     * @return static|mixed
+     */
+    public function whenNotEmpty(callable $callback, callable $default = null);
+
+    /**
+     * Apply the callback if the value is falsy.
+     *
+     * @param  bool  $value
+     * @param  callable  $callback
+     * @param  callable|null  $default
+     * @return static|mixed
+     */
+    public function unless($value, callable $callback, callable $default = null);
+
+    /**
+     * Apply the callback unless the collection is empty.
+     *
+     * @param  callable  $callback
+     * @param  callable|null  $default
+     * @return static|mixed
+     */
+    public function unlessEmpty(callable $callback, callable $default = null);
+
+    /**
+     * Apply the callback unless the collection is not empty.
+     *
+     * @param  callable  $callback
+     * @param  callable|null  $default
+     * @return static|mixed
+     */
+    public function unlessNotEmpty(callable $callback, callable $default = null);
+
+    /**
+     * Filter items by the given key value pair.
+     *
+     * @param  string  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return static
+     */
+    public function where($key, $operator = null, $value = null);
+
+    /**
+     * Filter items where the value for the given key is null.
+     *
+     * @param  string|null  $key
+     * @return static
+     */
+    public function whereNull($key = null);
+
+    /**
+     * Filter items where the value for the given key is not null.
+     *
+     * @param  string|null  $key
+     * @return static
+     */
+    public function whereNotNull($key = null);
+
+    /**
+     * Filter items by the given key value pair using strict comparison.
+     *
+     * @param  string  $key
+     * @param  mixed  $value
+     * @return static
+     */
+    public function whereStrict($key, $value);
+
+    /**
+     * Filter items by the given key value pair.
+     *
+     * @param  string  $key
+     * @param  mixed  $values
+     * @param  bool  $strict
+     * @return static
+     */
+    public function whereIn($key, $values, $strict = false);
+
+    /**
+     * Filter items by the given key value pair using strict comparison.
+     *
+     * @param  string  $key
+     * @param  mixed  $values
+     * @return static
+     */
+    public function whereInStrict($key, $values);
+
+    /**
+     * Filter items such that the value of the given key is between the given values.
+     *
+     * @param  string  $key
+     * @param  array  $values
+     * @return static
+     */
+    public function whereBetween($key, $values);
+
+    /**
+     * Filter items such that the value of the given key is not between the given values.
+     *
+     * @param  string  $key
+     * @param  array  $values
+     * @return static
+     */
+    public function whereNotBetween($key, $values);
+
+    /**
+     * Filter items by the given key value pair.
+     *
+     * @param  string  $key
+     * @param  mixed  $values
+     * @param  bool  $strict
+     * @return static
+     */
+    public function whereNotIn($key, $values, $strict = false);
+
+    /**
+     * Filter items by the given key value pair using strict comparison.
+     *
+     * @param  string  $key
+     * @param  mixed  $values
+     * @return static
+     */
+    public function whereNotInStrict($key, $values);
+
+    /**
+     * Filter the items, removing any items that don't match the given type(s).
+     *
+     * @param  string|string[]  $type
+     * @return static
+     */
+    public function whereInstanceOf($type);
+
+    /**
+     * Get the first item from the enumerable passing the given truth test.
+     *
+     * @param  callable|null  $callback
+     * @param  mixed  $default
+     * @return mixed
+     */
+    public function first(callable $callback = null, $default = null);
+
+    /**
+     * Get the first item by the given key value pair.
+     *
+     * @param  string  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return mixed
+     */
+    public function firstWhere($key, $operator = null, $value = null);
+
+    /**
+     * Get a flattened array of the items in the collection.
+     *
+     * @param  int  $depth
+     * @return static
+     */
+    public function flatten($depth = INF);
+
+    /**
+     * Flip the values with their keys.
+     *
+     * @return static
+     */
+    public function flip();
+
+    /**
+     * Get an item from the collection by key.
+     *
+     * @param  mixed  $key
+     * @param  mixed  $default
+     * @return mixed
+     */
+    public function get($key, $default = null);
+
+    /**
+     * Group an associative array by a field or using a callback.
+     *
+     * @param  array|callable|string  $groupBy
+     * @param  bool  $preserveKeys
+     * @return static
+     */
+    public function groupBy($groupBy, $preserveKeys = false);
+
+    /**
+     * Key an associative array by a field or using a callback.
+     *
+     * @param  callable|string  $keyBy
+     * @return static
+     */
+    public function keyBy($keyBy);
+
+    /**
+     * Determine if an item exists in the collection by key.
+     *
+     * @param  mixed  $key
+     * @return bool
+     */
+    public function has($key);
+
+    /**
+     * Concatenate values of a given key as a string.
+     *
+     * @param  string  $value
+     * @param  string|null  $glue
+     * @return string
+     */
+    public function implode($value, $glue = null);
+
+    /**
+     * Intersect the collection with the given items.
+     *
+     * @param  mixed  $items
+     * @return static
+     */
+    public function intersect($items);
+
+    /**
+     * Intersect the collection with the given items by key.
+     *
+     * @param  mixed  $items
+     * @return static
+     */
+    public function intersectByKeys($items);
+
+    /**
+     * Determine if the collection is empty or not.
+     *
+     * @return bool
+     */
+    public function isEmpty();
+
+    /**
+     * Determine if the collection is not empty.
+     *
+     * @return bool
+     */
+    public function isNotEmpty();
+
+    /**
+     * Join all items from the collection using a string. The final items can use a separate glue string.
+     *
+     * @param  string  $glue
+     * @param  string  $finalGlue
+     * @return string
+     */
+    public function join($glue, $finalGlue = '');
+
+    /**
+     * Get the keys of the collection items.
+     *
+     * @return static
+     */
+    public function keys();
+
+    /**
+     * Get the last item from the collection.
+     *
+     * @param  callable|null  $callback
+     * @param  mixed  $default
+     * @return mixed
+     */
+    public function last(callable $callback = null, $default = null);
+
+    /**
+     * Run a map over each of the items.
+     *
+     * @param  callable  $callback
+     * @return static
+     */
+    public function map(callable $callback);
+
+    /**
+     * Run a map over each nested chunk of items.
+     *
+     * @param  callable  $callback
+     * @return static
+     */
+    public function mapSpread(callable $callback);
+
+    /**
+     * Run a dictionary map over the items.
+     *
+     * The callback should return an associative array with a single key/value pair.
+     *
+     * @param  callable  $callback
+     * @return static
+     */
+    public function mapToDictionary(callable $callback);
+
+    /**
+     * Run a grouping map over the items.
+     *
+     * The callback should return an associative array with a single key/value pair.
+     *
+     * @param  callable  $callback
+     * @return static
+     */
+    public function mapToGroups(callable $callback);
+
+    /**
+     * Run an associative map over each of the items.
+     *
+     * The callback should return an associative array with a single key/value pair.
+     *
+     * @param  callable  $callback
+     * @return static
+     */
+    public function mapWithKeys(callable $callback);
+
+    /**
+     * Map a collection and flatten the result by a single level.
+     *
+     * @param  callable  $callback
+     * @return static
+     */
+    public function flatMap(callable $callback);
+
+    /**
+     * Map the values into a new class.
+     *
+     * @param  string  $class
+     * @return static
+     */
+    public function mapInto($class);
+
+    /**
+     * Merge the collection with the given items.
+     *
+     * @param  mixed  $items
+     * @return static
+     */
+    public function merge($items);
+
+    /**
+     * Recursively merge the collection with the given items.
+     *
+     * @param  mixed  $items
+     * @return static
+     */
+    public function mergeRecursive($items);
+
+    /**
+     * Create a collection by using this collection for keys and another for its values.
+     *
+     * @param  mixed  $values
+     * @return static
+     */
+    public function combine($values);
+
+    /**
+     * Union the collection with the given items.
+     *
+     * @param  mixed  $items
+     * @return static
+     */
+    public function union($items);
+
+    /**
+     * Get the min value of a given key.
+     *
+     * @param  callable|string|null  $callback
+     * @return mixed
+     */
+    public function min($callback = null);
+
+    /**
+     * Get the max value of a given key.
+     *
+     * @param  callable|string|null  $callback
+     * @return mixed
+     */
+    public function max($callback = null);
+
+    /**
+     * Create a new collection consisting of every n-th element.
+     *
+     * @param  int  $step
+     * @param  int  $offset
+     * @return static
+     */
+    public function nth($step, $offset = 0);
+
+    /**
+     * Get the items with the specified keys.
+     *
+     * @param  mixed  $keys
+     * @return static
+     */
+    public function only($keys);
+
+    /**
+     * "Paginate" the collection by slicing it into a smaller collection.
+     *
+     * @param  int  $page
+     * @param  int  $perPage
+     * @return static
+     */
+    public function forPage($page, $perPage);
+
+    /**
+     * Partition the collection into two arrays using the given callback or key.
+     *
+     * @param  callable|string  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return static
+     */
+    public function partition($key, $operator = null, $value = null);
+
+    /**
+     * Push all of the given items onto the collection.
+     *
+     * @param  iterable  $source
+     * @return static
+     */
+    public function concat($source);
+
+    /**
+     * Get one or a specified number of items randomly from the collection.
+     *
+     * @param  int|null  $number
+     * @return static|mixed
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function random($number = null);
+
+    /**
+     * Reduce the collection to a single value.
+     *
+     * @param  callable  $callback
+     * @param  mixed  $initial
+     * @return mixed
+     */
+    public function reduce(callable $callback, $initial = null);
+
+    /**
+     * Replace the collection items with the given items.
+     *
+     * @param  mixed  $items
+     * @return static
+     */
+    public function replace($items);
+
+    /**
+     * Recursively replace the collection items with the given items.
+     *
+     * @param  mixed  $items
+     * @return static
+     */
+    public function replaceRecursive($items);
+
+    /**
+     * Reverse items order.
+     *
+     * @return static
+     */
+    public function reverse();
+
+    /**
+     * Search the collection for a given value and return the corresponding key if successful.
+     *
+     * @param  mixed  $value
+     * @param  bool  $strict
+     * @return mixed
+     */
+    public function search($value, $strict = false);
+
+    /**
+     * Shuffle the items in the collection.
+     *
+     * @param  int|null  $seed
+     * @return static
+     */
+    public function shuffle($seed = null);
+
+    /**
+     * Skip the first {$count} items.
+     *
+     * @param  int  $count
+     * @return static
+     */
+    public function skip($count);
+
+    /**
+     * Skip items in the collection until the given condition is met.
+     *
+     * @param  mixed  $value
+     * @return static
+     */
+    public function skipUntil($value);
+
+    /**
+     * Skip items in the collection while the given condition is met.
+     *
+     * @param  mixed  $value
+     * @return static
+     */
+    public function skipWhile($value);
+
+    /**
+     * Get a slice of items from the enumerable.
+     *
+     * @param  int  $offset
+     * @param  int|null  $length
+     * @return static
+     */
+    public function slice($offset, $length = null);
+
+    /**
+     * Split a collection into a certain number of groups.
+     *
+     * @param  int  $numberOfGroups
+     * @return static
+     */
+    public function split($numberOfGroups);
+
+    /**
+     * Chunk the collection into chunks of the given size.
+     *
+     * @param  int  $size
+     * @return static
+     */
+    public function chunk($size);
+
+    /**
+     * Chunk the collection into chunks with a callback.
+     *
+     * @param  callable  $callback
+     * @return static
+     */
+    public function chunkWhile(callable $callback);
+
+    /**
+     * Sort through each item with a callback.
+     *
+     * @param  callable|null|int  $callback
+     * @return static
+     */
+    public function sort($callback = null);
+
+    /**
+     * Sort items in descending order.
+     *
+     * @param  int  $options
+     * @return static
+     */
+    public function sortDesc($options = SORT_REGULAR);
+
+    /**
+     * Sort the collection using the given callback.
+     *
+     * @param  callable|string  $callback
+     * @param  int  $options
+     * @param  bool  $descending
+     * @return static
+     */
+    public function sortBy($callback, $options = SORT_REGULAR, $descending = false);
+
+    /**
+     * Sort the collection in descending order using the given callback.
+     *
+     * @param  callable|string  $callback
+     * @param  int  $options
+     * @return static
+     */
+    public function sortByDesc($callback, $options = SORT_REGULAR);
+
+    /**
+     * Sort the collection keys.
+     *
+     * @param  int  $options
+     * @param  bool  $descending
+     * @return static
+     */
+    public function sortKeys($options = SORT_REGULAR, $descending = false);
+
+    /**
+     * Sort the collection keys in descending order.
+     *
+     * @param  int  $options
+     * @return static
+     */
+    public function sortKeysDesc($options = SORT_REGULAR);
+
+    /**
+     * Get the sum of the given values.
+     *
+     * @param  callable|string|null  $callback
+     * @return mixed
+     */
+    public function sum($callback = null);
+
+    /**
+     * Take the first or last {$limit} items.
+     *
+     * @param  int  $limit
+     * @return static
+     */
+    public function take($limit);
+
+    /**
+     * Take items in the collection until the given condition is met.
+     *
+     * @param  mixed  $value
+     * @return static
+     */
+    public function takeUntil($value);
+
+    /**
+     * Take items in the collection while the given condition is met.
+     *
+     * @param  mixed  $value
+     * @return static
+     */
+    public function takeWhile($value);
+
+    /**
+     * Pass the collection to the given callback and then return it.
+     *
+     * @param  callable  $callback
+     * @return $this
+     */
+    public function tap(callable $callback);
+
+    /**
+     * Pass the enumerable to the given callback and return the result.
+     *
+     * @param  callable  $callback
+     * @return mixed
+     */
+    public function pipe(callable $callback);
+
+    /**
+     * Get the values of a given key.
+     *
+     * @param  string|array  $value
+     * @param  string|null  $key
+     * @return static
+     */
+    public function pluck($value, $key = null);
+
+    /**
+     * Create a collection of all elements that do not pass a given truth test.
+     *
+     * @param  callable|mixed  $callback
+     * @return static
+     */
+    public function reject($callback = true);
+
+    /**
+     * Return only unique items from the collection array.
+     *
+     * @param  string|callable|null  $key
+     * @param  bool  $strict
+     * @return static
+     */
+    public function unique($key = null, $strict = false);
+
+    /**
+     * Return only unique items from the collection array using strict comparison.
+     *
+     * @param  string|callable|null  $key
+     * @return static
+     */
+    public function uniqueStrict($key = null);
+
+    /**
+     * Reset the keys on the underlying array.
+     *
+     * @return static
+     */
+    public function values();
+
+    /**
+     * Pad collection to the specified length with a value.
+     *
+     * @param  int  $size
+     * @param  mixed  $value
+     * @return static
+     */
+    public function pad($size, $value);
+
+    /**
+     * Count the number of items in the collection using a given truth test.
+     *
+     * @param  callable|null  $callback
+     * @return static
+     */
+    public function countBy($callback = null);
+
+    /**
+     * Zip the collection together with one or more arrays.
+     *
+     * e.g. new Collection([1, 2, 3])->zip([4, 5, 6]);
+     *      => [[1, 4], [2, 5], [3, 6]]
+     *
+     * @param  mixed  ...$items
+     * @return static
+     */
+    public function zip($items);
+
+    /**
+     * Collect the values into a collection.
+     *
+     * @return \Illuminate\Support\Collection
+     */
+    public function collect();
+
+    /**
+     * Convert the collection to its string representation.
+     *
+     * @return string
+     */
+    public function __toString();
+
+    /**
+     * Add a method to the list of proxied methods.
+     *
+     * @param  string  $method
+     * @return void
+     */
+    public static function proxy($method);
+
+    /**
+     * Dynamically access collection proxies.
+     *
+     * @param  string  $key
+     * @return mixed
+     *
+     * @throws \Exception
+     */
+    public function __get($key);
+}

+ 63 - 0
htdocs/includes/webklex/php-imap/vendor/illuminate/collections/HigherOrderCollectionProxy.php

@@ -0,0 +1,63 @@
+<?php
+
+namespace Illuminate\Support;
+
+/**
+ * @mixin \Illuminate\Support\Enumerable
+ */
+class HigherOrderCollectionProxy
+{
+    /**
+     * The collection being operated on.
+     *
+     * @var \Illuminate\Support\Enumerable
+     */
+    protected $collection;
+
+    /**
+     * The method being proxied.
+     *
+     * @var string
+     */
+    protected $method;
+
+    /**
+     * Create a new proxy instance.
+     *
+     * @param  \Illuminate\Support\Enumerable  $collection
+     * @param  string  $method
+     * @return void
+     */
+    public function __construct(Enumerable $collection, $method)
+    {
+        $this->method = $method;
+        $this->collection = $collection;
+    }
+
+    /**
+     * Proxy accessing an attribute onto the collection items.
+     *
+     * @param  string  $key
+     * @return mixed
+     */
+    public function __get($key)
+    {
+        return $this->collection->{$this->method}(function ($value) use ($key) {
+            return is_array($value) ? $value[$key] : $value->{$key};
+        });
+    }
+
+    /**
+     * Proxy a method call onto the collection items.
+     *
+     * @param  string  $method
+     * @param  array  $parameters
+     * @return mixed
+     */
+    public function __call($method, $parameters)
+    {
+        return $this->collection->{$this->method}(function ($value) use ($method, $parameters) {
+            return $value->{$method}(...$parameters);
+        });
+    }
+}

+ 63 - 0
htdocs/includes/webklex/php-imap/vendor/illuminate/collections/HigherOrderWhenProxy.php

@@ -0,0 +1,63 @@
+<?php
+
+namespace Illuminate\Support;
+
+/**
+ * @mixin \Illuminate\Support\Enumerable
+ */
+class HigherOrderWhenProxy
+{
+    /**
+     * The collection being operated on.
+     *
+     * @var \Illuminate\Support\Enumerable
+     */
+    protected $collection;
+
+    /**
+     * The condition for proxying.
+     *
+     * @var bool
+     */
+    protected $condition;
+
+    /**
+     * Create a new proxy instance.
+     *
+     * @param  \Illuminate\Support\Enumerable  $collection
+     * @param  bool  $condition
+     * @return void
+     */
+    public function __construct(Enumerable $collection, $condition)
+    {
+        $this->condition = $condition;
+        $this->collection = $collection;
+    }
+
+    /**
+     * Proxy accessing an attribute onto the collection.
+     *
+     * @param  string  $key
+     * @return mixed
+     */
+    public function __get($key)
+    {
+        return $this->condition
+            ? $this->collection->{$key}
+            : $this->collection;
+    }
+
+    /**
+     * Proxy a method call onto the collection.
+     *
+     * @param  string  $method
+     * @param  array  $parameters
+     * @return mixed
+     */
+    public function __call($method, $parameters)
+    {
+        return $this->condition
+            ? $this->collection->{$method}(...$parameters)
+            : $this->collection;
+    }
+}

+ 9 - 0
htdocs/includes/webklex/php-imap/vendor/illuminate/collections/ItemNotFoundException.php

@@ -0,0 +1,9 @@
+<?php
+
+namespace Illuminate\Support;
+
+use RuntimeException;
+
+class ItemNotFoundException extends RuntimeException
+{
+}

+ 21 - 0
htdocs/includes/webklex/php-imap/vendor/illuminate/collections/LICENSE.md

@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) Taylor Otwell
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.

+ 1585 - 0
htdocs/includes/webklex/php-imap/vendor/illuminate/collections/LazyCollection.php

@@ -0,0 +1,1585 @@
+<?php
+
+namespace Illuminate\Support;
+
+use ArrayIterator;
+use Closure;
+use DateTimeInterface;
+use Illuminate\Contracts\Support\CanBeEscapedWhenCastToString;
+use Illuminate\Support\Traits\EnumeratesValues;
+use Illuminate\Support\Traits\Macroable;
+use IteratorAggregate;
+use stdClass;
+
+class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable
+{
+    use EnumeratesValues, Macroable;
+
+    /**
+     * The source from which to generate items.
+     *
+     * @var callable|static
+     */
+    public $source;
+
+    /**
+     * Create a new lazy collection instance.
+     *
+     * @param  mixed  $source
+     * @return void
+     */
+    public function __construct($source = null)
+    {
+        if ($source instanceof Closure || $source instanceof self) {
+            $this->source = $source;
+        } elseif (is_null($source)) {
+            $this->source = static::empty();
+        } else {
+            $this->source = $this->getArrayableItems($source);
+        }
+    }
+
+    /**
+     * Create a collection with the given range.
+     *
+     * @param  int  $from
+     * @param  int  $to
+     * @return static
+     */
+    public static function range($from, $to)
+    {
+        return new static(function () use ($from, $to) {
+            if ($from <= $to) {
+                for (; $from <= $to; $from++) {
+                    yield $from;
+                }
+            } else {
+                for (; $from >= $to; $from--) {
+                    yield $from;
+                }
+            }
+        });
+    }
+
+    /**
+     * Get all items in the enumerable.
+     *
+     * @return array
+     */
+    public function all()
+    {
+        if (is_array($this->source)) {
+            return $this->source;
+        }
+
+        return iterator_to_array($this->getIterator());
+    }
+
+    /**
+     * Eager load all items into a new lazy collection backed by an array.
+     *
+     * @return static
+     */
+    public function eager()
+    {
+        return new static($this->all());
+    }
+
+    /**
+     * Cache values as they're enumerated.
+     *
+     * @return static
+     */
+    public function remember()
+    {
+        $iterator = $this->getIterator();
+
+        $iteratorIndex = 0;
+
+        $cache = [];
+
+        return new static(function () use ($iterator, &$iteratorIndex, &$cache) {
+            for ($index = 0; true; $index++) {
+                if (array_key_exists($index, $cache)) {
+                    yield $cache[$index][0] => $cache[$index][1];
+
+                    continue;
+                }
+
+                if ($iteratorIndex < $index) {
+                    $iterator->next();
+
+                    $iteratorIndex++;
+                }
+
+                if (! $iterator->valid()) {
+                    break;
+                }
+
+                $cache[$index] = [$iterator->key(), $iterator->current()];
+
+                yield $cache[$index][0] => $cache[$index][1];
+            }
+        });
+    }
+
+    /**
+     * Get the average value of a given key.
+     *
+     * @param  callable|string|null  $callback
+     * @return mixed
+     */
+    public function avg($callback = null)
+    {
+        return $this->collect()->avg($callback);
+    }
+
+    /**
+     * Get the median of a given key.
+     *
+     * @param  string|array|null  $key
+     * @return mixed
+     */
+    public function median($key = null)
+    {
+        return $this->collect()->median($key);
+    }
+
+    /**
+     * Get the mode of a given key.
+     *
+     * @param  string|array|null  $key
+     * @return array|null
+     */
+    public function mode($key = null)
+    {
+        return $this->collect()->mode($key);
+    }
+
+    /**
+     * Collapse the collection of items into a single array.
+     *
+     * @return static
+     */
+    public function collapse()
+    {
+        return new static(function () {
+            foreach ($this as $values) {
+                if (is_array($values) || $values instanceof Enumerable) {
+                    foreach ($values as $value) {
+                        yield $value;
+                    }
+                }
+            }
+        });
+    }
+
+    /**
+     * Determine if an item exists in the enumerable.
+     *
+     * @param  mixed  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return bool
+     */
+    public function contains($key, $operator = null, $value = null)
+    {
+        if (func_num_args() === 1 && $this->useAsCallable($key)) {
+            $placeholder = new stdClass;
+
+            return $this->first($key, $placeholder) !== $placeholder;
+        }
+
+        if (func_num_args() === 1) {
+            $needle = $key;
+
+            foreach ($this as $value) {
+                if ($value == $needle) {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
+        return $this->contains($this->operatorForWhere(...func_get_args()));
+    }
+
+    /**
+     * Determine if an item is not contained in the enumerable.
+     *
+     * @param  mixed  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return bool
+     */
+    public function doesntContain($key, $operator = null, $value = null)
+    {
+        return ! $this->contains(...func_get_args());
+    }
+
+    /**
+     * Cross join the given iterables, returning all possible permutations.
+     *
+     * @param  array  ...$arrays
+     * @return static
+     */
+    public function crossJoin(...$arrays)
+    {
+        return $this->passthru('crossJoin', func_get_args());
+    }
+
+    /**
+     * Count the number of items in the collection by a field or using a callback.
+     *
+     * @param  callable|string  $countBy
+     * @return static
+     */
+    public function countBy($countBy = null)
+    {
+        $countBy = is_null($countBy)
+            ? $this->identity()
+            : $this->valueRetriever($countBy);
+
+        return new static(function () use ($countBy) {
+            $counts = [];
+
+            foreach ($this as $key => $value) {
+                $group = $countBy($value, $key);
+
+                if (empty($counts[$group])) {
+                    $counts[$group] = 0;
+                }
+
+                $counts[$group]++;
+            }
+
+            yield from $counts;
+        });
+    }
+
+    /**
+     * Get the items that are not present in the given items.
+     *
+     * @param  mixed  $items
+     * @return static
+     */
+    public function diff($items)
+    {
+        return $this->passthru('diff', func_get_args());
+    }
+
+    /**
+     * Get the items that are not present in the given items, using the callback.
+     *
+     * @param  mixed  $items
+     * @param  callable  $callback
+     * @return static
+     */
+    public function diffUsing($items, callable $callback)
+    {
+        return $this->passthru('diffUsing', func_get_args());
+    }
+
+    /**
+     * Get the items whose keys and values are not present in the given items.
+     *
+     * @param  mixed  $items
+     * @return static
+     */
+    public function diffAssoc($items)
+    {
+        return $this->passthru('diffAssoc', func_get_args());
+    }
+
+    /**
+     * Get the items whose keys and values are not present in the given items, using the callback.
+     *
+     * @param  mixed  $items
+     * @param  callable  $callback
+     * @return static
+     */
+    public function diffAssocUsing($items, callable $callback)
+    {
+        return $this->passthru('diffAssocUsing', func_get_args());
+    }
+
+    /**
+     * Get the items whose keys are not present in the given items.
+     *
+     * @param  mixed  $items
+     * @return static
+     */
+    public function diffKeys($items)
+    {
+        return $this->passthru('diffKeys', func_get_args());
+    }
+
+    /**
+     * Get the items whose keys are not present in the given items, using the callback.
+     *
+     * @param  mixed  $items
+     * @param  callable  $callback
+     * @return static
+     */
+    public function diffKeysUsing($items, callable $callback)
+    {
+        return $this->passthru('diffKeysUsing', func_get_args());
+    }
+
+    /**
+     * Retrieve duplicate items.
+     *
+     * @param  callable|string|null  $callback
+     * @param  bool  $strict
+     * @return static
+     */
+    public function duplicates($callback = null, $strict = false)
+    {
+        return $this->passthru('duplicates', func_get_args());
+    }
+
+    /**
+     * Retrieve duplicate items using strict comparison.
+     *
+     * @param  callable|string|null  $callback
+     * @return static
+     */
+    public function duplicatesStrict($callback = null)
+    {
+        return $this->passthru('duplicatesStrict', func_get_args());
+    }
+
+    /**
+     * Get all items except for those with the specified keys.
+     *
+     * @param  mixed  $keys
+     * @return static
+     */
+    public function except($keys)
+    {
+        return $this->passthru('except', func_get_args());
+    }
+
+    /**
+     * Run a filter over each of the items.
+     *
+     * @param  callable|null  $callback
+     * @return static
+     */
+    public function filter(callable $callback = null)
+    {
+        if (is_null($callback)) {
+            $callback = function ($value) {
+                return (bool) $value;
+            };
+        }
+
+        return new static(function () use ($callback) {
+            foreach ($this as $key => $value) {
+                if ($callback($value, $key)) {
+                    yield $key => $value;
+                }
+            }
+        });
+    }
+
+    /**
+     * Get the first item from the enumerable passing the given truth test.
+     *
+     * @param  callable|null  $callback
+     * @param  mixed  $default
+     * @return mixed
+     */
+    public function first(callable $callback = null, $default = null)
+    {
+        $iterator = $this->getIterator();
+
+        if (is_null($callback)) {
+            if (! $iterator->valid()) {
+                return value($default);
+            }
+
+            return $iterator->current();
+        }
+
+        foreach ($iterator as $key => $value) {
+            if ($callback($value, $key)) {
+                return $value;
+            }
+        }
+
+        return value($default);
+    }
+
+    /**
+     * Get a flattened list of the items in the collection.
+     *
+     * @param  int  $depth
+     * @return static
+     */
+    public function flatten($depth = INF)
+    {
+        $instance = new static(function () use ($depth) {
+            foreach ($this as $item) {
+                if (! is_array($item) && ! $item instanceof Enumerable) {
+                    yield $item;
+                } elseif ($depth === 1) {
+                    yield from $item;
+                } else {
+                    yield from (new static($item))->flatten($depth - 1);
+                }
+            }
+        });
+
+        return $instance->values();
+    }
+
+    /**
+     * Flip the items in the collection.
+     *
+     * @return static
+     */
+    public function flip()
+    {
+        return new static(function () {
+            foreach ($this as $key => $value) {
+                yield $value => $key;
+            }
+        });
+    }
+
+    /**
+     * Get an item by key.
+     *
+     * @param  mixed  $key
+     * @param  mixed  $default
+     * @return mixed
+     */
+    public function get($key, $default = null)
+    {
+        if (is_null($key)) {
+            return;
+        }
+
+        foreach ($this as $outerKey => $outerValue) {
+            if ($outerKey == $key) {
+                return $outerValue;
+            }
+        }
+
+        return value($default);
+    }
+
+    /**
+     * Group an associative array by a field or using a callback.
+     *
+     * @param  array|callable|string  $groupBy
+     * @param  bool  $preserveKeys
+     * @return static
+     */
+    public function groupBy($groupBy, $preserveKeys = false)
+    {
+        return $this->passthru('groupBy', func_get_args());
+    }
+
+    /**
+     * Key an associative array by a field or using a callback.
+     *
+     * @param  callable|string  $keyBy
+     * @return static
+     */
+    public function keyBy($keyBy)
+    {
+        return new static(function () use ($keyBy) {
+            $keyBy = $this->valueRetriever($keyBy);
+
+            foreach ($this as $key => $item) {
+                $resolvedKey = $keyBy($item, $key);
+
+                if (is_object($resolvedKey)) {
+                    $resolvedKey = (string) $resolvedKey;
+                }
+
+                yield $resolvedKey => $item;
+            }
+        });
+    }
+
+    /**
+     * Determine if an item exists in the collection by key.
+     *
+     * @param  mixed  $key
+     * @return bool
+     */
+    public function has($key)
+    {
+        $keys = array_flip(is_array($key) ? $key : func_get_args());
+        $count = count($keys);
+
+        foreach ($this as $key => $value) {
+            if (array_key_exists($key, $keys) && --$count == 0) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Determine if any of the keys exist in the collection.
+     *
+     * @param  mixed  $key
+     * @return bool
+     */
+    public function hasAny($key)
+    {
+        $keys = array_flip(is_array($key) ? $key : func_get_args());
+
+        foreach ($this as $key => $value) {
+            if (array_key_exists($key, $keys)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Concatenate values of a given key as a string.
+     *
+     * @param  string  $value
+     * @param  string|null  $glue
+     * @return string
+     */
+    public function implode($value, $glue = null)
+    {
+        return $this->collect()->implode(...func_get_args());
+    }
+
+    /**
+     * Intersect the collection with the given items.
+     *
+     * @param  mixed  $items
+     * @return static
+     */
+    public function intersect($items)
+    {
+        return $this->passthru('intersect', func_get_args());
+    }
+
+    /**
+     * Intersect the collection with the given items by key.
+     *
+     * @param  mixed  $items
+     * @return static
+     */
+    public function intersectByKeys($items)
+    {
+        return $this->passthru('intersectByKeys', func_get_args());
+    }
+
+    /**
+     * Determine if the items are empty or not.
+     *
+     * @return bool
+     */
+    public function isEmpty()
+    {
+        return ! $this->getIterator()->valid();
+    }
+
+    /**
+     * Determine if the collection contains a single item.
+     *
+     * @return bool
+     */
+    public function containsOneItem()
+    {
+        return $this->take(2)->count() === 1;
+    }
+
+    /**
+     * Join all items from the collection using a string. The final items can use a separate glue string.
+     *
+     * @param  string  $glue
+     * @param  string  $finalGlue
+     * @return string
+     */
+    public function join($glue, $finalGlue = '')
+    {
+        return $this->collect()->join(...func_get_args());
+    }
+
+    /**
+     * Get the keys of the collection items.
+     *
+     * @return static
+     */
+    public function keys()
+    {
+        return new static(function () {
+            foreach ($this as $key => $value) {
+                yield $key;
+            }
+        });
+    }
+
+    /**
+     * Get the last item from the collection.
+     *
+     * @param  callable|null  $callback
+     * @param  mixed  $default
+     * @return mixed
+     */
+    public function last(callable $callback = null, $default = null)
+    {
+        $needle = $placeholder = new stdClass;
+
+        foreach ($this as $key => $value) {
+            if (is_null($callback) || $callback($value, $key)) {
+                $needle = $value;
+            }
+        }
+
+        return $needle === $placeholder ? value($default) : $needle;
+    }
+
+    /**
+     * Get the values of a given key.
+     *
+     * @param  string|array  $value
+     * @param  string|null  $key
+     * @return static
+     */
+    public function pluck($value, $key = null)
+    {
+        return new static(function () use ($value, $key) {
+            [$value, $key] = $this->explodePluckParameters($value, $key);
+
+            foreach ($this as $item) {
+                $itemValue = data_get($item, $value);
+
+                if (is_null($key)) {
+                    yield $itemValue;
+                } else {
+                    $itemKey = data_get($item, $key);
+
+                    if (is_object($itemKey) && method_exists($itemKey, '__toString')) {
+                        $itemKey = (string) $itemKey;
+                    }
+
+                    yield $itemKey => $itemValue;
+                }
+            }
+        });
+    }
+
+    /**
+     * Run a map over each of the items.
+     *
+     * @param  callable  $callback
+     * @return static
+     */
+    public function map(callable $callback)
+    {
+        return new static(function () use ($callback) {
+            foreach ($this as $key => $value) {
+                yield $key => $callback($value, $key);
+            }
+        });
+    }
+
+    /**
+     * Run a dictionary map over the items.
+     *
+     * The callback should return an associative array with a single key/value pair.
+     *
+     * @param  callable  $callback
+     * @return static
+     */
+    public function mapToDictionary(callable $callback)
+    {
+        return $this->passthru('mapToDictionary', func_get_args());
+    }
+
+    /**
+     * Run an associative map over each of the items.
+     *
+     * The callback should return an associative array with a single key/value pair.
+     *
+     * @param  callable  $callback
+     * @return static
+     */
+    public function mapWithKeys(callable $callback)
+    {
+        return new static(function () use ($callback) {
+            foreach ($this as $key => $value) {
+                yield from $callback($value, $key);
+            }
+        });
+    }
+
+    /**
+     * Merge the collection with the given items.
+     *
+     * @param  mixed  $items
+     * @return static
+     */
+    public function merge($items)
+    {
+        return $this->passthru('merge', func_get_args());
+    }
+
+    /**
+     * Recursively merge the collection with the given items.
+     *
+     * @param  mixed  $items
+     * @return static
+     */
+    public function mergeRecursive($items)
+    {
+        return $this->passthru('mergeRecursive', func_get_args());
+    }
+
+    /**
+     * Create a collection by using this collection for keys and another for its values.
+     *
+     * @param  mixed  $values
+     * @return static
+     */
+    public function combine($values)
+    {
+        return new static(function () use ($values) {
+            $values = $this->makeIterator($values);
+
+            $errorMessage = 'Both parameters should have an equal number of elements';
+
+            foreach ($this as $key) {
+                if (! $values->valid()) {
+                    trigger_error($errorMessage, E_USER_WARNING);
+
+                    break;
+                }
+
+                yield $key => $values->current();
+
+                $values->next();
+            }
+
+            if ($values->valid()) {
+                trigger_error($errorMessage, E_USER_WARNING);
+            }
+        });
+    }
+
+    /**
+     * Union the collection with the given items.
+     *
+     * @param  mixed  $items
+     * @return static
+     */
+    public function union($items)
+    {
+        return $this->passthru('union', func_get_args());
+    }
+
+    /**
+     * Create a new collection consisting of every n-th element.
+     *
+     * @param  int  $step
+     * @param  int  $offset
+     * @return static
+     */
+    public function nth($step, $offset = 0)
+    {
+        return new static(function () use ($step, $offset) {
+            $position = 0;
+
+            foreach ($this->slice($offset) as $item) {
+                if ($position % $step === 0) {
+                    yield $item;
+                }
+
+                $position++;
+            }
+        });
+    }
+
+    /**
+     * Get the items with the specified keys.
+     *
+     * @param  mixed  $keys
+     * @return static
+     */
+    public function only($keys)
+    {
+        if ($keys instanceof Enumerable) {
+            $keys = $keys->all();
+        } elseif (! is_null($keys)) {
+            $keys = is_array($keys) ? $keys : func_get_args();
+        }
+
+        return new static(function () use ($keys) {
+            if (is_null($keys)) {
+                yield from $this;
+            } else {
+                $keys = array_flip($keys);
+
+                foreach ($this as $key => $value) {
+                    if (array_key_exists($key, $keys)) {
+                        yield $key => $value;
+
+                        unset($keys[$key]);
+
+                        if (empty($keys)) {
+                            break;
+                        }
+                    }
+                }
+            }
+        });
+    }
+
+    /**
+     * Push all of the given items onto the collection.
+     *
+     * @param  iterable  $source
+     * @return static
+     */
+    public function concat($source)
+    {
+        return (new static(function () use ($source) {
+            yield from $this;
+            yield from $source;
+        }))->values();
+    }
+
+    /**
+     * Get one or a specified number of items randomly from the collection.
+     *
+     * @param  int|null  $number
+     * @return static|mixed
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function random($number = null)
+    {
+        $result = $this->collect()->random(...func_get_args());
+
+        return is_null($number) ? $result : new static($result);
+    }
+
+    /**
+     * Replace the collection items with the given items.
+     *
+     * @param  mixed  $items
+     * @return static
+     */
+    public function replace($items)
+    {
+        return new static(function () use ($items) {
+            $items = $this->getArrayableItems($items);
+
+            foreach ($this as $key => $value) {
+                if (array_key_exists($key, $items)) {
+                    yield $key => $items[$key];
+
+                    unset($items[$key]);
+                } else {
+                    yield $key => $value;
+                }
+            }
+
+            foreach ($items as $key => $value) {
+                yield $key => $value;
+            }
+        });
+    }
+
+    /**
+     * Recursively replace the collection items with the given items.
+     *
+     * @param  mixed  $items
+     * @return static
+     */
+    public function replaceRecursive($items)
+    {
+        return $this->passthru('replaceRecursive', func_get_args());
+    }
+
+    /**
+     * Reverse items order.
+     *
+     * @return static
+     */
+    public function reverse()
+    {
+        return $this->passthru('reverse', func_get_args());
+    }
+
+    /**
+     * Search the collection for a given value and return the corresponding key if successful.
+     *
+     * @param  mixed  $value
+     * @param  bool  $strict
+     * @return mixed
+     */
+    public function search($value, $strict = false)
+    {
+        $predicate = $this->useAsCallable($value)
+            ? $value
+            : function ($item) use ($value, $strict) {
+                return $strict ? $item === $value : $item == $value;
+            };
+
+        foreach ($this as $key => $item) {
+            if ($predicate($item, $key)) {
+                return $key;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Shuffle the items in the collection.
+     *
+     * @param  int|null  $seed
+     * @return static
+     */
+    public function shuffle($seed = null)
+    {
+        return $this->passthru('shuffle', func_get_args());
+    }
+
+    /**
+     * Create chunks representing a "sliding window" view of the items in the collection.
+     *
+     * @param  int  $size
+     * @param  int  $step
+     * @return static
+     */
+    public function sliding($size = 2, $step = 1)
+    {
+        return new static(function () use ($size, $step) {
+            $iterator = $this->getIterator();
+
+            $chunk = [];
+
+            while ($iterator->valid()) {
+                $chunk[$iterator->key()] = $iterator->current();
+
+                if (count($chunk) == $size) {
+                    yield tap(new static($chunk), function () use (&$chunk, $step) {
+                        $chunk = array_slice($chunk, $step, null, true);
+                    });
+
+                    // If the $step between chunks is bigger than each chunk's $size
+                    // we will skip the extra items (which should never be in any
+                    // chunk) before we continue to the next chunk in the loop.
+                    if ($step > $size) {
+                        $skip = $step - $size;
+
+                        for ($i = 0; $i < $skip && $iterator->valid(); $i++) {
+                            $iterator->next();
+                        }
+                    }
+                }
+
+                $iterator->next();
+            }
+        });
+    }
+
+    /**
+     * Skip the first {$count} items.
+     *
+     * @param  int  $count
+     * @return static
+     */
+    public function skip($count)
+    {
+        return new static(function () use ($count) {
+            $iterator = $this->getIterator();
+
+            while ($iterator->valid() && $count--) {
+                $iterator->next();
+            }
+
+            while ($iterator->valid()) {
+                yield $iterator->key() => $iterator->current();
+
+                $iterator->next();
+            }
+        });
+    }
+
+    /**
+     * Skip items in the collection until the given condition is met.
+     *
+     * @param  mixed  $value
+     * @return static
+     */
+    public function skipUntil($value)
+    {
+        $callback = $this->useAsCallable($value) ? $value : $this->equality($value);
+
+        return $this->skipWhile($this->negate($callback));
+    }
+
+    /**
+     * Skip items in the collection while the given condition is met.
+     *
+     * @param  mixed  $value
+     * @return static
+     */
+    public function skipWhile($value)
+    {
+        $callback = $this->useAsCallable($value) ? $value : $this->equality($value);
+
+        return new static(function () use ($callback) {
+            $iterator = $this->getIterator();
+
+            while ($iterator->valid() && $callback($iterator->current(), $iterator->key())) {
+                $iterator->next();
+            }
+
+            while ($iterator->valid()) {
+                yield $iterator->key() => $iterator->current();
+
+                $iterator->next();
+            }
+        });
+    }
+
+    /**
+     * Get a slice of items from the enumerable.
+     *
+     * @param  int  $offset
+     * @param  int|null  $length
+     * @return static
+     */
+    public function slice($offset, $length = null)
+    {
+        if ($offset < 0 || $length < 0) {
+            return $this->passthru('slice', func_get_args());
+        }
+
+        $instance = $this->skip($offset);
+
+        return is_null($length) ? $instance : $instance->take($length);
+    }
+
+    /**
+     * Split a collection into a certain number of groups.
+     *
+     * @param  int  $numberOfGroups
+     * @return static
+     */
+    public function split($numberOfGroups)
+    {
+        return $this->passthru('split', func_get_args());
+    }
+
+    /**
+     * Get the first item in the collection, but only if exactly one item exists. Otherwise, throw an exception.
+     *
+     * @param  mixed  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return mixed
+     *
+     * @throws \Illuminate\Support\ItemNotFoundException
+     * @throws \Illuminate\Support\MultipleItemsFoundException
+     */
+    public function sole($key = null, $operator = null, $value = null)
+    {
+        $filter = func_num_args() > 1
+            ? $this->operatorForWhere(...func_get_args())
+            : $key;
+
+        return $this
+            ->when($filter)
+            ->filter($filter)
+            ->take(2)
+            ->collect()
+            ->sole();
+    }
+
+    /**
+     * Get the first item in the collection but throw an exception if no matching items exist.
+     *
+     * @param  mixed  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return mixed
+     *
+     * @throws \Illuminate\Support\ItemNotFoundException
+     */
+    public function firstOrFail($key = null, $operator = null, $value = null)
+    {
+        $filter = func_num_args() > 1
+            ? $this->operatorForWhere(...func_get_args())
+            : $key;
+
+        return $this
+            ->when($filter)
+            ->filter($filter)
+            ->take(1)
+            ->collect()
+            ->firstOrFail();
+    }
+
+    /**
+     * Chunk the collection into chunks of the given size.
+     *
+     * @param  int  $size
+     * @return static
+     */
+    public function chunk($size)
+    {
+        if ($size <= 0) {
+            return static::empty();
+        }
+
+        return new static(function () use ($size) {
+            $iterator = $this->getIterator();
+
+            while ($iterator->valid()) {
+                $chunk = [];
+
+                while (true) {
+                    $chunk[$iterator->key()] = $iterator->current();
+
+                    if (count($chunk) < $size) {
+                        $iterator->next();
+
+                        if (! $iterator->valid()) {
+                            break;
+                        }
+                    } else {
+                        break;
+                    }
+                }
+
+                yield new static($chunk);
+
+                $iterator->next();
+            }
+        });
+    }
+
+    /**
+     * Split a collection into a certain number of groups, and fill the first groups completely.
+     *
+     * @param  int  $numberOfGroups
+     * @return static
+     */
+    public function splitIn($numberOfGroups)
+    {
+        return $this->chunk(ceil($this->count() / $numberOfGroups));
+    }
+
+    /**
+     * Chunk the collection into chunks with a callback.
+     *
+     * @param  callable  $callback
+     * @return static
+     */
+    public function chunkWhile(callable $callback)
+    {
+        return new static(function () use ($callback) {
+            $iterator = $this->getIterator();
+
+            $chunk = new Collection;
+
+            if ($iterator->valid()) {
+                $chunk[$iterator->key()] = $iterator->current();
+
+                $iterator->next();
+            }
+
+            while ($iterator->valid()) {
+                if (! $callback($iterator->current(), $iterator->key(), $chunk)) {
+                    yield new static($chunk);
+
+                    $chunk = new Collection;
+                }
+
+                $chunk[$iterator->key()] = $iterator->current();
+
+                $iterator->next();
+            }
+
+            if ($chunk->isNotEmpty()) {
+                yield new static($chunk);
+            }
+        });
+    }
+
+    /**
+     * Sort through each item with a callback.
+     *
+     * @param  callable|null|int  $callback
+     * @return static
+     */
+    public function sort($callback = null)
+    {
+        return $this->passthru('sort', func_get_args());
+    }
+
+    /**
+     * Sort items in descending order.
+     *
+     * @param  int  $options
+     * @return static
+     */
+    public function sortDesc($options = SORT_REGULAR)
+    {
+        return $this->passthru('sortDesc', func_get_args());
+    }
+
+    /**
+     * Sort the collection using the given callback.
+     *
+     * @param  callable|string  $callback
+     * @param  int  $options
+     * @param  bool  $descending
+     * @return static
+     */
+    public function sortBy($callback, $options = SORT_REGULAR, $descending = false)
+    {
+        return $this->passthru('sortBy', func_get_args());
+    }
+
+    /**
+     * Sort the collection in descending order using the given callback.
+     *
+     * @param  callable|string  $callback
+     * @param  int  $options
+     * @return static
+     */
+    public function sortByDesc($callback, $options = SORT_REGULAR)
+    {
+        return $this->passthru('sortByDesc', func_get_args());
+    }
+
+    /**
+     * Sort the collection keys.
+     *
+     * @param  int  $options
+     * @param  bool  $descending
+     * @return static
+     */
+    public function sortKeys($options = SORT_REGULAR, $descending = false)
+    {
+        return $this->passthru('sortKeys', func_get_args());
+    }
+
+    /**
+     * Sort the collection keys in descending order.
+     *
+     * @param  int  $options
+     * @return static
+     */
+    public function sortKeysDesc($options = SORT_REGULAR)
+    {
+        return $this->passthru('sortKeysDesc', func_get_args());
+    }
+
+    /**
+     * Sort the collection keys using a callback.
+     *
+     * @param  callable  $callback
+     * @return static
+     */
+    public function sortKeysUsing(callable $callback)
+    {
+        return $this->passthru('sortKeysUsing', func_get_args());
+    }
+
+    /**
+     * Take the first or last {$limit} items.
+     *
+     * @param  int  $limit
+     * @return static
+     */
+    public function take($limit)
+    {
+        if ($limit < 0) {
+            return $this->passthru('take', func_get_args());
+        }
+
+        return new static(function () use ($limit) {
+            $iterator = $this->getIterator();
+
+            while ($limit--) {
+                if (! $iterator->valid()) {
+                    break;
+                }
+
+                yield $iterator->key() => $iterator->current();
+
+                if ($limit) {
+                    $iterator->next();
+                }
+            }
+        });
+    }
+
+    /**
+     * Take items in the collection until the given condition is met.
+     *
+     * @param  mixed  $value
+     * @return static
+     */
+    public function takeUntil($value)
+    {
+        $callback = $this->useAsCallable($value) ? $value : $this->equality($value);
+
+        return new static(function () use ($callback) {
+            foreach ($this as $key => $item) {
+                if ($callback($item, $key)) {
+                    break;
+                }
+
+                yield $key => $item;
+            }
+        });
+    }
+
+    /**
+     * Take items in the collection until a given point in time.
+     *
+     * @param  \DateTimeInterface  $timeout
+     * @return static
+     */
+    public function takeUntilTimeout(DateTimeInterface $timeout)
+    {
+        $timeout = $timeout->getTimestamp();
+
+        return $this->takeWhile(function () use ($timeout) {
+            return $this->now() < $timeout;
+        });
+    }
+
+    /**
+     * Take items in the collection while the given condition is met.
+     *
+     * @param  mixed  $value
+     * @return static
+     */
+    public function takeWhile($value)
+    {
+        $callback = $this->useAsCallable($value) ? $value : $this->equality($value);
+
+        return $this->takeUntil(function ($item, $key) use ($callback) {
+            return ! $callback($item, $key);
+        });
+    }
+
+    /**
+     * Pass each item in the collection to the given callback, lazily.
+     *
+     * @param  callable  $callback
+     * @return static
+     */
+    public function tapEach(callable $callback)
+    {
+        return new static(function () use ($callback) {
+            foreach ($this as $key => $value) {
+                $callback($value, $key);
+
+                yield $key => $value;
+            }
+        });
+    }
+
+    /**
+     * Convert a flatten "dot" notation array into an expanded array.
+     *
+     * @return static
+     */
+    public function undot()
+    {
+        return $this->passthru('undot', []);
+    }
+
+    /**
+     * Return only unique items from the collection array.
+     *
+     * @param  string|callable|null  $key
+     * @param  bool  $strict
+     * @return static
+     */
+    public function unique($key = null, $strict = false)
+    {
+        $callback = $this->valueRetriever($key);
+
+        return new static(function () use ($callback, $strict) {
+            $exists = [];
+
+            foreach ($this as $key => $item) {
+                if (! in_array($id = $callback($item, $key), $exists, $strict)) {
+                    yield $key => $item;
+
+                    $exists[] = $id;
+                }
+            }
+        });
+    }
+
+    /**
+     * Reset the keys on the underlying array.
+     *
+     * @return static
+     */
+    public function values()
+    {
+        return new static(function () {
+            foreach ($this as $item) {
+                yield $item;
+            }
+        });
+    }
+
+    /**
+     * Zip the collection together with one or more arrays.
+     *
+     * e.g. new LazyCollection([1, 2, 3])->zip([4, 5, 6]);
+     *      => [[1, 4], [2, 5], [3, 6]]
+     *
+     * @param  mixed  ...$items
+     * @return static
+     */
+    public function zip($items)
+    {
+        $iterables = func_get_args();
+
+        return new static(function () use ($iterables) {
+            $iterators = Collection::make($iterables)->map(function ($iterable) {
+                return $this->makeIterator($iterable);
+            })->prepend($this->getIterator());
+
+            while ($iterators->contains->valid()) {
+                yield new static($iterators->map->current());
+
+                $iterators->each->next();
+            }
+        });
+    }
+
+    /**
+     * Pad collection to the specified length with a value.
+     *
+     * @param  int  $size
+     * @param  mixed  $value
+     * @return static
+     */
+    public function pad($size, $value)
+    {
+        if ($size < 0) {
+            return $this->passthru('pad', func_get_args());
+        }
+
+        return new static(function () use ($size, $value) {
+            $yielded = 0;
+
+            foreach ($this as $index => $item) {
+                yield $index => $item;
+
+                $yielded++;
+            }
+
+            while ($yielded++ < $size) {
+                yield $value;
+            }
+        });
+    }
+
+    /**
+     * Get the values iterator.
+     *
+     * @return \Traversable
+     */
+    #[\ReturnTypeWillChange]
+    public function getIterator()
+    {
+        return $this->makeIterator($this->source);
+    }
+
+    /**
+     * Count the number of items in the collection.
+     *
+     * @return int
+     */
+    #[\ReturnTypeWillChange]
+    public function count()
+    {
+        if (is_array($this->source)) {
+            return count($this->source);
+        }
+
+        return iterator_count($this->getIterator());
+    }
+
+    /**
+     * Make an iterator from the given source.
+     *
+     * @param  mixed  $source
+     * @return \Traversable
+     */
+    protected function makeIterator($source)
+    {
+        if ($source instanceof IteratorAggregate) {
+            return $source->getIterator();
+        }
+
+        if (is_array($source)) {
+            return new ArrayIterator($source);
+        }
+
+        return $source();
+    }
+
+    /**
+     * Explode the "value" and "key" arguments passed to "pluck".
+     *
+     * @param  string|array  $value
+     * @param  string|array|null  $key
+     * @return array
+     */
+    protected function explodePluckParameters($value, $key)
+    {
+        $value = is_string($value) ? explode('.', $value) : $value;
+
+        $key = is_null($key) || is_array($key) ? $key : explode('.', $key);
+
+        return [$value, $key];
+    }
+
+    /**
+     * Pass this lazy collection through a method on the collection class.
+     *
+     * @param  string  $method
+     * @param  array  $params
+     * @return static
+     */
+    protected function passthru($method, array $params)
+    {
+        return new static(function () use ($method, $params) {
+            yield from $this->collect()->$method(...$params);
+        });
+    }
+
+    /**
+     * Get the current time.
+     *
+     * @return int
+     */
+    protected function now()
+    {
+        return time();
+    }
+}

+ 9 - 0
htdocs/includes/webklex/php-imap/vendor/illuminate/collections/MultipleItemsFoundException.php

@@ -0,0 +1,9 @@
+<?php
+
+namespace Illuminate\Support;
+
+use RuntimeException;
+
+class MultipleItemsFoundException extends RuntimeException
+{
+}

+ 1116 - 0
htdocs/includes/webklex/php-imap/vendor/illuminate/collections/Traits/EnumeratesValues.php

@@ -0,0 +1,1116 @@
+<?php
+
+namespace Illuminate\Support\Traits;
+
+use CachingIterator;
+use Closure;
+use Exception;
+use Illuminate\Contracts\Support\Arrayable;
+use Illuminate\Contracts\Support\Jsonable;
+use Illuminate\Support\Arr;
+use Illuminate\Support\Collection;
+use Illuminate\Support\Enumerable;
+use Illuminate\Support\HigherOrderCollectionProxy;
+use Illuminate\Support\HigherOrderWhenProxy;
+use JsonSerializable;
+use Symfony\Component\VarDumper\VarDumper;
+use Traversable;
+use UnexpectedValueException;
+use UnitEnum;
+
+/**
+ * @property-read HigherOrderCollectionProxy $average
+ * @property-read HigherOrderCollectionProxy $avg
+ * @property-read HigherOrderCollectionProxy $contains
+ * @property-read HigherOrderCollectionProxy $doesntContain
+ * @property-read HigherOrderCollectionProxy $each
+ * @property-read HigherOrderCollectionProxy $every
+ * @property-read HigherOrderCollectionProxy $filter
+ * @property-read HigherOrderCollectionProxy $first
+ * @property-read HigherOrderCollectionProxy $flatMap
+ * @property-read HigherOrderCollectionProxy $groupBy
+ * @property-read HigherOrderCollectionProxy $keyBy
+ * @property-read HigherOrderCollectionProxy $map
+ * @property-read HigherOrderCollectionProxy $max
+ * @property-read HigherOrderCollectionProxy $min
+ * @property-read HigherOrderCollectionProxy $partition
+ * @property-read HigherOrderCollectionProxy $reject
+ * @property-read HigherOrderCollectionProxy $some
+ * @property-read HigherOrderCollectionProxy $sortBy
+ * @property-read HigherOrderCollectionProxy $sortByDesc
+ * @property-read HigherOrderCollectionProxy $skipUntil
+ * @property-read HigherOrderCollectionProxy $skipWhile
+ * @property-read HigherOrderCollectionProxy $sum
+ * @property-read HigherOrderCollectionProxy $takeUntil
+ * @property-read HigherOrderCollectionProxy $takeWhile
+ * @property-read HigherOrderCollectionProxy $unique
+ * @property-read HigherOrderCollectionProxy $until
+ */
+trait EnumeratesValues
+{
+    /**
+     * Indicates that the object's string representation should be escaped when __toString is invoked.
+     *
+     * @var bool
+     */
+    protected $escapeWhenCastingToString = false;
+
+    /**
+     * The methods that can be proxied.
+     *
+     * @var string[]
+     */
+    protected static $proxies = [
+        'average',
+        'avg',
+        'contains',
+        'doesntContain',
+        'each',
+        'every',
+        'filter',
+        'first',
+        'flatMap',
+        'groupBy',
+        'keyBy',
+        'map',
+        'max',
+        'min',
+        'partition',
+        'reject',
+        'skipUntil',
+        'skipWhile',
+        'some',
+        'sortBy',
+        'sortByDesc',
+        'sum',
+        'takeUntil',
+        'takeWhile',
+        'unique',
+        'until',
+    ];
+
+    /**
+     * Create a new collection instance if the value isn't one already.
+     *
+     * @param  mixed  $items
+     * @return static
+     */
+    public static function make($items = [])
+    {
+        return new static($items);
+    }
+
+    /**
+     * Wrap the given value in a collection if applicable.
+     *
+     * @param  mixed  $value
+     * @return static
+     */
+    public static function wrap($value)
+    {
+        return $value instanceof Enumerable
+            ? new static($value)
+            : new static(Arr::wrap($value));
+    }
+
+    /**
+     * Get the underlying items from the given collection if applicable.
+     *
+     * @param  array|static  $value
+     * @return array
+     */
+    public static function unwrap($value)
+    {
+        return $value instanceof Enumerable ? $value->all() : $value;
+    }
+
+    /**
+     * Create a new instance with no items.
+     *
+     * @return static
+     */
+    public static function empty()
+    {
+        return new static([]);
+    }
+
+    /**
+     * Create a new collection by invoking the callback a given amount of times.
+     *
+     * @param  int  $number
+     * @param  callable|null  $callback
+     * @return static
+     */
+    public static function times($number, callable $callback = null)
+    {
+        if ($number < 1) {
+            return new static;
+        }
+
+        return static::range(1, $number)
+            ->when($callback)
+            ->map($callback);
+    }
+
+    /**
+     * Alias for the "avg" method.
+     *
+     * @param  callable|string|null  $callback
+     * @return mixed
+     */
+    public function average($callback = null)
+    {
+        return $this->avg($callback);
+    }
+
+    /**
+     * Alias for the "contains" method.
+     *
+     * @param  mixed  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return bool
+     */
+    public function some($key, $operator = null, $value = null)
+    {
+        return $this->contains(...func_get_args());
+    }
+
+    /**
+     * Determine if an item exists, using strict comparison.
+     *
+     * @param  mixed  $key
+     * @param  mixed  $value
+     * @return bool
+     */
+    public function containsStrict($key, $value = null)
+    {
+        if (func_num_args() === 2) {
+            return $this->contains(function ($item) use ($key, $value) {
+                return data_get($item, $key) === $value;
+            });
+        }
+
+        if ($this->useAsCallable($key)) {
+            return ! is_null($this->first($key));
+        }
+
+        foreach ($this as $item) {
+            if ($item === $key) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Dump the items and end the script.
+     *
+     * @param  mixed  ...$args
+     * @return void
+     */
+    public function dd(...$args)
+    {
+        $this->dump(...$args);
+
+        exit(1);
+    }
+
+    /**
+     * Dump the items.
+     *
+     * @return $this
+     */
+    public function dump()
+    {
+        (new Collection(func_get_args()))
+            ->push($this->all())
+            ->each(function ($item) {
+                VarDumper::dump($item);
+            });
+
+        return $this;
+    }
+
+    /**
+     * Execute a callback over each item.
+     *
+     * @param  callable  $callback
+     * @return $this
+     */
+    public function each(callable $callback)
+    {
+        foreach ($this as $key => $item) {
+            if ($callback($item, $key) === false) {
+                break;
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Execute a callback over each nested chunk of items.
+     *
+     * @param  callable  $callback
+     * @return static
+     */
+    public function eachSpread(callable $callback)
+    {
+        return $this->each(function ($chunk, $key) use ($callback) {
+            $chunk[] = $key;
+
+            return $callback(...$chunk);
+        });
+    }
+
+    /**
+     * Determine if all items pass the given truth test.
+     *
+     * @param  string|callable  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return bool
+     */
+    public function every($key, $operator = null, $value = null)
+    {
+        if (func_num_args() === 1) {
+            $callback = $this->valueRetriever($key);
+
+            foreach ($this as $k => $v) {
+                if (! $callback($v, $k)) {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        return $this->every($this->operatorForWhere(...func_get_args()));
+    }
+
+    /**
+     * Get the first item by the given key value pair.
+     *
+     * @param  string  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return mixed
+     */
+    public function firstWhere($key, $operator = null, $value = null)
+    {
+        return $this->first($this->operatorForWhere(...func_get_args()));
+    }
+
+    /**
+     * Determine if the collection is not empty.
+     *
+     * @return bool
+     */
+    public function isNotEmpty()
+    {
+        return ! $this->isEmpty();
+    }
+
+    /**
+     * Run a map over each nested chunk of items.
+     *
+     * @param  callable  $callback
+     * @return static
+     */
+    public function mapSpread(callable $callback)
+    {
+        return $this->map(function ($chunk, $key) use ($callback) {
+            $chunk[] = $key;
+
+            return $callback(...$chunk);
+        });
+    }
+
+    /**
+     * Run a grouping map over the items.
+     *
+     * The callback should return an associative array with a single key/value pair.
+     *
+     * @param  callable  $callback
+     * @return static
+     */
+    public function mapToGroups(callable $callback)
+    {
+        $groups = $this->mapToDictionary($callback);
+
+        return $groups->map([$this, 'make']);
+    }
+
+    /**
+     * Map a collection and flatten the result by a single level.
+     *
+     * @param  callable  $callback
+     * @return static
+     */
+    public function flatMap(callable $callback)
+    {
+        return $this->map($callback)->collapse();
+    }
+
+    /**
+     * Map the values into a new class.
+     *
+     * @param  string  $class
+     * @return static
+     */
+    public function mapInto($class)
+    {
+        return $this->map(function ($value, $key) use ($class) {
+            return new $class($value, $key);
+        });
+    }
+
+    /**
+     * Get the min value of a given key.
+     *
+     * @param  callable|string|null  $callback
+     * @return mixed
+     */
+    public function min($callback = null)
+    {
+        $callback = $this->valueRetriever($callback);
+
+        return $this->map(function ($value) use ($callback) {
+            return $callback($value);
+        })->filter(function ($value) {
+            return ! is_null($value);
+        })->reduce(function ($result, $value) {
+            return is_null($result) || $value < $result ? $value : $result;
+        });
+    }
+
+    /**
+     * Get the max value of a given key.
+     *
+     * @param  callable|string|null  $callback
+     * @return mixed
+     */
+    public function max($callback = null)
+    {
+        $callback = $this->valueRetriever($callback);
+
+        return $this->filter(function ($value) {
+            return ! is_null($value);
+        })->reduce(function ($result, $item) use ($callback) {
+            $value = $callback($item);
+
+            return is_null($result) || $value > $result ? $value : $result;
+        });
+    }
+
+    /**
+     * "Paginate" the collection by slicing it into a smaller collection.
+     *
+     * @param  int  $page
+     * @param  int  $perPage
+     * @return static
+     */
+    public function forPage($page, $perPage)
+    {
+        $offset = max(0, ($page - 1) * $perPage);
+
+        return $this->slice($offset, $perPage);
+    }
+
+    /**
+     * Partition the collection into two arrays using the given callback or key.
+     *
+     * @param  callable|string  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return static
+     */
+    public function partition($key, $operator = null, $value = null)
+    {
+        $passed = [];
+        $failed = [];
+
+        $callback = func_num_args() === 1
+                ? $this->valueRetriever($key)
+                : $this->operatorForWhere(...func_get_args());
+
+        foreach ($this as $key => $item) {
+            if ($callback($item, $key)) {
+                $passed[$key] = $item;
+            } else {
+                $failed[$key] = $item;
+            }
+        }
+
+        return new static([new static($passed), new static($failed)]);
+    }
+
+    /**
+     * Get the sum of the given values.
+     *
+     * @param  callable|string|null  $callback
+     * @return mixed
+     */
+    public function sum($callback = null)
+    {
+        $callback = is_null($callback)
+            ? $this->identity()
+            : $this->valueRetriever($callback);
+
+        return $this->reduce(function ($result, $item) use ($callback) {
+            return $result + $callback($item);
+        }, 0);
+    }
+
+    /**
+     * Apply the callback if the value is truthy.
+     *
+     * @param  bool|mixed  $value
+     * @param  callable|null  $callback
+     * @param  callable|null  $default
+     * @return static|mixed
+     */
+    public function when($value, callable $callback = null, callable $default = null)
+    {
+        if (! $callback) {
+            return new HigherOrderWhenProxy($this, $value);
+        }
+
+        if ($value) {
+            return $callback($this, $value);
+        } elseif ($default) {
+            return $default($this, $value);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Apply the callback if the collection is empty.
+     *
+     * @param  callable  $callback
+     * @param  callable|null  $default
+     * @return static|mixed
+     */
+    public function whenEmpty(callable $callback, callable $default = null)
+    {
+        return $this->when($this->isEmpty(), $callback, $default);
+    }
+
+    /**
+     * Apply the callback if the collection is not empty.
+     *
+     * @param  callable  $callback
+     * @param  callable|null  $default
+     * @return static|mixed
+     */
+    public function whenNotEmpty(callable $callback, callable $default = null)
+    {
+        return $this->when($this->isNotEmpty(), $callback, $default);
+    }
+
+    /**
+     * Apply the callback if the value is falsy.
+     *
+     * @param  bool  $value
+     * @param  callable  $callback
+     * @param  callable|null  $default
+     * @return static|mixed
+     */
+    public function unless($value, callable $callback, callable $default = null)
+    {
+        return $this->when(! $value, $callback, $default);
+    }
+
+    /**
+     * Apply the callback unless the collection is empty.
+     *
+     * @param  callable  $callback
+     * @param  callable|null  $default
+     * @return static|mixed
+     */
+    public function unlessEmpty(callable $callback, callable $default = null)
+    {
+        return $this->whenNotEmpty($callback, $default);
+    }
+
+    /**
+     * Apply the callback unless the collection is not empty.
+     *
+     * @param  callable  $callback
+     * @param  callable|null  $default
+     * @return static|mixed
+     */
+    public function unlessNotEmpty(callable $callback, callable $default = null)
+    {
+        return $this->whenEmpty($callback, $default);
+    }
+
+    /**
+     * Filter items by the given key value pair.
+     *
+     * @param  string  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return static
+     */
+    public function where($key, $operator = null, $value = null)
+    {
+        return $this->filter($this->operatorForWhere(...func_get_args()));
+    }
+
+    /**
+     * Filter items where the value for the given key is null.
+     *
+     * @param  string|null  $key
+     * @return static
+     */
+    public function whereNull($key = null)
+    {
+        return $this->whereStrict($key, null);
+    }
+
+    /**
+     * Filter items where the value for the given key is not null.
+     *
+     * @param  string|null  $key
+     * @return static
+     */
+    public function whereNotNull($key = null)
+    {
+        return $this->where($key, '!==', null);
+    }
+
+    /**
+     * Filter items by the given key value pair using strict comparison.
+     *
+     * @param  string  $key
+     * @param  mixed  $value
+     * @return static
+     */
+    public function whereStrict($key, $value)
+    {
+        return $this->where($key, '===', $value);
+    }
+
+    /**
+     * Filter items by the given key value pair.
+     *
+     * @param  string  $key
+     * @param  mixed  $values
+     * @param  bool  $strict
+     * @return static
+     */
+    public function whereIn($key, $values, $strict = false)
+    {
+        $values = $this->getArrayableItems($values);
+
+        return $this->filter(function ($item) use ($key, $values, $strict) {
+            return in_array(data_get($item, $key), $values, $strict);
+        });
+    }
+
+    /**
+     * Filter items by the given key value pair using strict comparison.
+     *
+     * @param  string  $key
+     * @param  mixed  $values
+     * @return static
+     */
+    public function whereInStrict($key, $values)
+    {
+        return $this->whereIn($key, $values, true);
+    }
+
+    /**
+     * Filter items such that the value of the given key is between the given values.
+     *
+     * @param  string  $key
+     * @param  array  $values
+     * @return static
+     */
+    public function whereBetween($key, $values)
+    {
+        return $this->where($key, '>=', reset($values))->where($key, '<=', end($values));
+    }
+
+    /**
+     * Filter items such that the value of the given key is not between the given values.
+     *
+     * @param  string  $key
+     * @param  array  $values
+     * @return static
+     */
+    public function whereNotBetween($key, $values)
+    {
+        return $this->filter(function ($item) use ($key, $values) {
+            return data_get($item, $key) < reset($values) || data_get($item, $key) > end($values);
+        });
+    }
+
+    /**
+     * Filter items by the given key value pair.
+     *
+     * @param  string  $key
+     * @param  mixed  $values
+     * @param  bool  $strict
+     * @return static
+     */
+    public function whereNotIn($key, $values, $strict = false)
+    {
+        $values = $this->getArrayableItems($values);
+
+        return $this->reject(function ($item) use ($key, $values, $strict) {
+            return in_array(data_get($item, $key), $values, $strict);
+        });
+    }
+
+    /**
+     * Filter items by the given key value pair using strict comparison.
+     *
+     * @param  string  $key
+     * @param  mixed  $values
+     * @return static
+     */
+    public function whereNotInStrict($key, $values)
+    {
+        return $this->whereNotIn($key, $values, true);
+    }
+
+    /**
+     * Filter the items, removing any items that don't match the given type(s).
+     *
+     * @param  string|string[]  $type
+     * @return static
+     */
+    public function whereInstanceOf($type)
+    {
+        return $this->filter(function ($value) use ($type) {
+            if (is_array($type)) {
+                foreach ($type as $classType) {
+                    if ($value instanceof $classType) {
+                        return true;
+                    }
+                }
+
+                return false;
+            }
+
+            return $value instanceof $type;
+        });
+    }
+
+    /**
+     * Pass the collection to the given callback and return the result.
+     *
+     * @param  callable  $callback
+     * @return mixed
+     */
+    public function pipe(callable $callback)
+    {
+        return $callback($this);
+    }
+
+    /**
+     * Pass the collection into a new class.
+     *
+     * @param  string  $class
+     * @return mixed
+     */
+    public function pipeInto($class)
+    {
+        return new $class($this);
+    }
+
+    /**
+     * Pass the collection through a series of callable pipes and return the result.
+     *
+     * @param  array<callable>  $pipes
+     * @return mixed
+     */
+    public function pipeThrough($pipes)
+    {
+        return static::make($pipes)->reduce(
+            function ($carry, $pipe) {
+                return $pipe($carry);
+            },
+            $this,
+        );
+    }
+
+    /**
+     * Pass the collection to the given callback and then return it.
+     *
+     * @param  callable  $callback
+     * @return $this
+     */
+    public function tap(callable $callback)
+    {
+        $callback(clone $this);
+
+        return $this;
+    }
+
+    /**
+     * Reduce the collection to a single value.
+     *
+     * @param  callable  $callback
+     * @param  mixed  $initial
+     * @return mixed
+     */
+    public function reduce(callable $callback, $initial = null)
+    {
+        $result = $initial;
+
+        foreach ($this as $key => $value) {
+            $result = $callback($result, $value, $key);
+        }
+
+        return $result;
+    }
+
+    /**
+     * Reduce the collection to multiple aggregate values.
+     *
+     * @param  callable  $callback
+     * @param  mixed  ...$initial
+     * @return array
+     *
+     * @deprecated Use "reduceSpread" instead
+     *
+     * @throws \UnexpectedValueException
+     */
+    public function reduceMany(callable $callback, ...$initial)
+    {
+        return $this->reduceSpread($callback, ...$initial);
+    }
+
+    /**
+     * Reduce the collection to multiple aggregate values.
+     *
+     * @param  callable  $callback
+     * @param  mixed  ...$initial
+     * @return array
+     *
+     * @throws \UnexpectedValueException
+     */
+    public function reduceSpread(callable $callback, ...$initial)
+    {
+        $result = $initial;
+
+        foreach ($this as $key => $value) {
+            $result = call_user_func_array($callback, array_merge($result, [$value, $key]));
+
+            if (! is_array($result)) {
+                throw new UnexpectedValueException(sprintf(
+                    "%s::reduceMany expects reducer to return an array, but got a '%s' instead.",
+                    class_basename(static::class), gettype($result)
+                ));
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * Reduce an associative collection to a single value.
+     *
+     * @param  callable  $callback
+     * @param  mixed  $initial
+     * @return mixed
+     */
+    public function reduceWithKeys(callable $callback, $initial = null)
+    {
+        return $this->reduce($callback, $initial);
+    }
+
+    /**
+     * Create a collection of all elements that do not pass a given truth test.
+     *
+     * @param  callable|mixed  $callback
+     * @return static
+     */
+    public function reject($callback = true)
+    {
+        $useAsCallable = $this->useAsCallable($callback);
+
+        return $this->filter(function ($value, $key) use ($callback, $useAsCallable) {
+            return $useAsCallable
+                ? ! $callback($value, $key)
+                : $value != $callback;
+        });
+    }
+
+    /**
+     * Return only unique items from the collection array using strict comparison.
+     *
+     * @param  string|callable|null  $key
+     * @return static
+     */
+    public function uniqueStrict($key = null)
+    {
+        return $this->unique($key, true);
+    }
+
+    /**
+     * Collect the values into a collection.
+     *
+     * @return \Illuminate\Support\Collection
+     */
+    public function collect()
+    {
+        return new Collection($this->all());
+    }
+
+    /**
+     * Get the collection of items as a plain array.
+     *
+     * @return array
+     */
+    public function toArray()
+    {
+        return $this->map(function ($value) {
+            return $value instanceof Arrayable ? $value->toArray() : $value;
+        })->all();
+    }
+
+    /**
+     * Convert the object into something JSON serializable.
+     *
+     * @return array
+     */
+    #[\ReturnTypeWillChange]
+    public function jsonSerialize()
+    {
+        return array_map(function ($value) {
+            if ($value instanceof JsonSerializable) {
+                return $value->jsonSerialize();
+            } elseif ($value instanceof Jsonable) {
+                return json_decode($value->toJson(), true);
+            } elseif ($value instanceof Arrayable) {
+                return $value->toArray();
+            }
+
+            return $value;
+        }, $this->all());
+    }
+
+    /**
+     * Get the collection of items as JSON.
+     *
+     * @param  int  $options
+     * @return string
+     */
+    public function toJson($options = 0)
+    {
+        return json_encode($this->jsonSerialize(), $options);
+    }
+
+    /**
+     * Get a CachingIterator instance.
+     *
+     * @param  int  $flags
+     * @return \CachingIterator
+     */
+    public function getCachingIterator($flags = CachingIterator::CALL_TOSTRING)
+    {
+        return new CachingIterator($this->getIterator(), $flags);
+    }
+
+    /**
+     * Convert the collection to its string representation.
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        return $this->escapeWhenCastingToString
+                    ? e($this->toJson())
+                    : $this->toJson();
+    }
+
+    /**
+     * Indicate that the model's string representation should be escaped when __toString is invoked.
+     *
+     * @param  bool  $escape
+     * @return $this
+     */
+    public function escapeWhenCastingToString($escape = true)
+    {
+        $this->escapeWhenCastingToString = $escape;
+
+        return $this;
+    }
+
+    /**
+     * Add a method to the list of proxied methods.
+     *
+     * @param  string  $method
+     * @return void
+     */
+    public static function proxy($method)
+    {
+        static::$proxies[] = $method;
+    }
+
+    /**
+     * Dynamically access collection proxies.
+     *
+     * @param  string  $key
+     * @return mixed
+     *
+     * @throws \Exception
+     */
+    public function __get($key)
+    {
+        if (! in_array($key, static::$proxies)) {
+            throw new Exception("Property [{$key}] does not exist on this collection instance.");
+        }
+
+        return new HigherOrderCollectionProxy($this, $key);
+    }
+
+    /**
+     * Results array of items from Collection or Arrayable.
+     *
+     * @param  mixed  $items
+     * @return array
+     */
+    protected function getArrayableItems($items)
+    {
+        if (is_array($items)) {
+            return $items;
+        } elseif ($items instanceof Enumerable) {
+            return $items->all();
+        } elseif ($items instanceof Arrayable) {
+            return $items->toArray();
+        } elseif ($items instanceof Jsonable) {
+            return json_decode($items->toJson(), true);
+        } elseif ($items instanceof JsonSerializable) {
+            return (array) $items->jsonSerialize();
+        } elseif ($items instanceof Traversable) {
+            return iterator_to_array($items);
+        } elseif ($items instanceof UnitEnum) {
+            return [$items];
+        }
+
+        return (array) $items;
+    }
+
+    /**
+     * Get an operator checker callback.
+     *
+     * @param  string  $key
+     * @param  string|null  $operator
+     * @param  mixed  $value
+     * @return \Closure
+     */
+    protected function operatorForWhere($key, $operator = null, $value = null)
+    {
+        if (func_num_args() === 1) {
+            $value = true;
+
+            $operator = '=';
+        }
+
+        if (func_num_args() === 2) {
+            $value = $operator;
+
+            $operator = '=';
+        }
+
+        return function ($item) use ($key, $operator, $value) {
+            $retrieved = data_get($item, $key);
+
+            $strings = array_filter([$retrieved, $value], function ($value) {
+                return is_string($value) || (is_object($value) && method_exists($value, '__toString'));
+            });
+
+            if (count($strings) < 2 && count(array_filter([$retrieved, $value], 'is_object')) == 1) {
+                return in_array($operator, ['!=', '<>', '!==']);
+            }
+
+            switch ($operator) {
+                default:
+                case '=':
+                case '==':  return $retrieved == $value;
+                case '!=':
+                case '<>':  return $retrieved != $value;
+                case '<':   return $retrieved < $value;
+                case '>':   return $retrieved > $value;
+                case '<=':  return $retrieved <= $value;
+                case '>=':  return $retrieved >= $value;
+                case '===': return $retrieved === $value;
+                case '!==': return $retrieved !== $value;
+            }
+        };
+    }
+
+    /**
+     * Determine if the given value is callable, but not a string.
+     *
+     * @param  mixed  $value
+     * @return bool
+     */
+    protected function useAsCallable($value)
+    {
+        return ! is_string($value) && is_callable($value);
+    }
+
+    /**
+     * Get a value retrieving callback.
+     *
+     * @param  callable|string|null  $value
+     * @return callable
+     */
+    protected function valueRetriever($value)
+    {
+        if ($this->useAsCallable($value)) {
+            return $value;
+        }
+
+        return function ($item) use ($value) {
+            return data_get($item, $value);
+        };
+    }
+
+    /**
+     * Make a function to check an item's equality.
+     *
+     * @param  mixed  $value
+     * @return \Closure
+     */
+    protected function equality($value)
+    {
+        return function ($item) use ($value) {
+            return $item === $value;
+        };
+    }
+
+    /**
+     * Make a function using another function, by negating its result.
+     *
+     * @param  \Closure  $callback
+     * @return \Closure
+     */
+    protected function negate(Closure $callback)
+    {
+        return function (...$params) use ($callback) {
+            return ! $callback(...$params);
+        };
+    }
+
+    /**
+     * Make a function that returns what's passed to it.
+     *
+     * @return \Closure
+     */
+    protected function identity()
+    {
+        return function ($value) {
+            return $value;
+        };
+    }
+}

+ 41 - 0
htdocs/includes/webklex/php-imap/vendor/illuminate/collections/composer.json

@@ -0,0 +1,41 @@
+{
+    "name": "illuminate/collections",
+    "description": "The Illuminate Collections package.",
+    "license": "MIT",
+    "homepage": "https://laravel.com",
+    "support": {
+        "issues": "https://github.com/laravel/framework/issues",
+        "source": "https://github.com/laravel/framework"
+    },
+    "authors": [
+        {
+            "name": "Taylor Otwell",
+            "email": "taylor@laravel.com"
+        }
+    ],
+    "require": {
+        "php": "^7.3|^8.0",
+        "illuminate/contracts": "^8.0",
+        "illuminate/macroable": "^8.0"
+    },
+    "autoload": {
+        "psr-4": {
+            "Illuminate\\Support\\": ""
+        },
+        "files": [
+            "helpers.php"
+        ]
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "8.x-dev"
+        }
+    },
+    "suggest": {
+        "symfony/var-dumper": "Required to use the dump method (^5.4)."
+    },
+    "config": {
+        "sort-packages": true
+    },
+    "minimum-stability": "dev"
+}

+ 186 - 0
htdocs/includes/webklex/php-imap/vendor/illuminate/collections/helpers.php

@@ -0,0 +1,186 @@
+<?php
+
+use Illuminate\Support\Arr;
+use Illuminate\Support\Collection;
+
+if (! function_exists('collect')) {
+    /**
+     * Create a collection from the given value.
+     *
+     * @param  mixed  $value
+     * @return \Illuminate\Support\Collection
+     */
+    function collect($value = null)
+    {
+        return new Collection($value);
+    }
+}
+
+if (! function_exists('data_fill')) {
+    /**
+     * Fill in data where it's missing.
+     *
+     * @param  mixed  $target
+     * @param  string|array  $key
+     * @param  mixed  $value
+     * @return mixed
+     */
+    function data_fill(&$target, $key, $value)
+    {
+        return data_set($target, $key, $value, false);
+    }
+}
+
+if (! function_exists('data_get')) {
+    /**
+     * Get an item from an array or object using "dot" notation.
+     *
+     * @param  mixed  $target
+     * @param  string|array|int|null  $key
+     * @param  mixed  $default
+     * @return mixed
+     */
+    function data_get($target, $key, $default = null)
+    {
+        if (is_null($key)) {
+            return $target;
+        }
+
+        $key = is_array($key) ? $key : explode('.', $key);
+
+        foreach ($key as $i => $segment) {
+            unset($key[$i]);
+
+            if (is_null($segment)) {
+                return $target;
+            }
+
+            if ($segment === '*') {
+                if ($target instanceof Collection) {
+                    $target = $target->all();
+                } elseif (! is_array($target)) {
+                    return value($default);
+                }
+
+                $result = [];
+
+                foreach ($target as $item) {
+                    $result[] = data_get($item, $key);
+                }
+
+                return in_array('*', $key) ? Arr::collapse($result) : $result;
+            }
+
+            if (Arr::accessible($target) && Arr::exists($target, $segment)) {
+                $target = $target[$segment];
+            } elseif (is_object($target) && isset($target->{$segment})) {
+                $target = $target->{$segment};
+            } else {
+                return value($default);
+            }
+        }
+
+        return $target;
+    }
+}
+
+if (! function_exists('data_set')) {
+    /**
+     * Set an item on an array or object using dot notation.
+     *
+     * @param  mixed  $target
+     * @param  string|array  $key
+     * @param  mixed  $value
+     * @param  bool  $overwrite
+     * @return mixed
+     */
+    function data_set(&$target, $key, $value, $overwrite = true)
+    {
+        $segments = is_array($key) ? $key : explode('.', $key);
+
+        if (($segment = array_shift($segments)) === '*') {
+            if (! Arr::accessible($target)) {
+                $target = [];
+            }
+
+            if ($segments) {
+                foreach ($target as &$inner) {
+                    data_set($inner, $segments, $value, $overwrite);
+                }
+            } elseif ($overwrite) {
+                foreach ($target as &$inner) {
+                    $inner = $value;
+                }
+            }
+        } elseif (Arr::accessible($target)) {
+            if ($segments) {
+                if (! Arr::exists($target, $segment)) {
+                    $target[$segment] = [];
+                }
+
+                data_set($target[$segment], $segments, $value, $overwrite);
+            } elseif ($overwrite || ! Arr::exists($target, $segment)) {
+                $target[$segment] = $value;
+            }
+        } elseif (is_object($target)) {
+            if ($segments) {
+                if (! isset($target->{$segment})) {
+                    $target->{$segment} = [];
+                }
+
+                data_set($target->{$segment}, $segments, $value, $overwrite);
+            } elseif ($overwrite || ! isset($target->{$segment})) {
+                $target->{$segment} = $value;
+            }
+        } else {
+            $target = [];
+
+            if ($segments) {
+                data_set($target[$segment], $segments, $value, $overwrite);
+            } elseif ($overwrite) {
+                $target[$segment] = $value;
+            }
+        }
+
+        return $target;
+    }
+}
+
+if (! function_exists('head')) {
+    /**
+     * Get the first element of an array. Useful for method chaining.
+     *
+     * @param  array  $array
+     * @return mixed
+     */
+    function head($array)
+    {
+        return reset($array);
+    }
+}
+
+if (! function_exists('last')) {
+    /**
+     * Get the last element from an array.
+     *
+     * @param  array  $array
+     * @return mixed
+     */
+    function last($array)
+    {
+        return end($array);
+    }
+}
+
+if (! function_exists('value')) {
+    /**
+     * Return the default value of the given value.
+     *
+     * @param  mixed  $value
+     * @return mixed
+     */
+    function value($value, ...$args)
+    {
+        return $value instanceof Closure ? $value(...$args) : $value;
+    }
+}

+ 15 - 0
htdocs/includes/webklex/php-imap/vendor/illuminate/contracts/Auth/Access/Authorizable.php

@@ -0,0 +1,15 @@
+<?php
+
+namespace Illuminate\Contracts\Auth\Access;
+
+interface Authorizable
+{
+    /**
+     * Determine if the entity has a given ability.
+     *
+     * @param  iterable|string  $abilities
+     * @param  array|mixed  $arguments
+     * @return bool
+     */
+    public function can($abilities, $arguments = []);
+}

+ 150 - 0
htdocs/includes/webklex/php-imap/vendor/illuminate/contracts/Auth/Access/Gate.php

@@ -0,0 +1,150 @@
+<?php
+
+namespace Illuminate\Contracts\Auth\Access;
+
+interface Gate
+{
+    /**
+     * Determine if a given ability has been defined.
+     *
+     * @param  string  $ability
+     * @return bool
+     */
+    public function has($ability);
+
+    /**
+     * Define a new ability.
+     *
+     * @param  string  $ability
+     * @param  callable|string  $callback
+     * @return $this
+     */
+    public function define($ability, $callback);
+
+    /**
+     * Define abilities for a resource.
+     *
+     * @param  string  $name
+     * @param  string  $class
+     * @param  array|null  $abilities
+     * @return $this
+     */
+    public function resource($name, $class, array $abilities = null);
+
+    /**
+     * Define a policy class for a given class type.
+     *
+     * @param  string  $class
+     * @param  string  $policy
+     * @return $this
+     */
+    public function policy($class, $policy);
+
+    /**
+     * Register a callback to run before all Gate checks.
+     *
+     * @param  callable  $callback
+     * @return $this
+     */
+    public function before(callable $callback);
+
+    /**
+     * Register a callback to run after all Gate checks.
+     *
+     * @param  callable  $callback
+     * @return $this
+     */
+    public function after(callable $callback);
+
+    /**
+     * Determine if the given ability should be granted for the current user.
+     *
+     * @param  string  $ability
+     * @param  array|mixed  $arguments
+     * @return bool
+     */
+    public function allows($ability, $arguments = []);
+
+    /**
+     * Determine if the given ability should be denied for the current user.
+     *
+     * @param  string  $ability
+     * @param  array|mixed  $arguments
+     * @return bool
+     */
+    public function denies($ability, $arguments = []);
+
+    /**
+     * Determine if all of the given abilities should be granted for the current user.
+     *
+     * @param  iterable|string  $abilities
+     * @param  array|mixed  $arguments
+     * @return bool
+     */
+    public function check($abilities, $arguments = []);
+
+    /**
+     * Determine if any one of the given abilities should be granted for the current user.
+     *
+     * @param  iterable|string  $abilities
+     * @param  array|mixed  $arguments
+     * @return bool
+     */
+    public function any($abilities, $arguments = []);
+
+    /**
+     * Determine if the given ability should be granted for the current user.
+     *
+     * @param  string  $ability
+     * @param  array|mixed  $arguments
+     * @return \Illuminate\Auth\Access\Response
+     *
+     * @throws \Illuminate\Auth\Access\AuthorizationException
+     */
+    public function authorize($ability, $arguments = []);
+
+    /**
+     * Inspect the user for the given ability.
+     *
+     * @param  string  $ability
+     * @param  array|mixed  $arguments
+     * @return \Illuminate\Auth\Access\Response
+     */
+    public function inspect($ability, $arguments = []);
+
+    /**
+     * Get the raw result from the authorization callback.
+     *
+     * @param  string  $ability
+     * @param  array|mixed  $arguments
+     * @return mixed
+     *
+     * @throws \Illuminate\Auth\Access\AuthorizationException
+     */
+    public function raw($ability, $arguments = []);
+
+    /**
+     * Get a policy instance for a given class.
+     *
+     * @param  object|string  $class
+     * @return mixed
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function getPolicyFor($class);
+
+    /**
+     * Get a guard instance for the given user.
+     *
+     * @param  \Illuminate\Contracts\Auth\Authenticatable|mixed  $user
+     * @return static
+     */
+    public function forUser($user);
+
+    /**
+     * Get all of the defined abilities.
+     *
+     * @return array
+     */
+    public function abilities();
+}

+ 49 - 0
htdocs/includes/webklex/php-imap/vendor/illuminate/contracts/Auth/Authenticatable.php

@@ -0,0 +1,49 @@
+<?php
+
+namespace Illuminate\Contracts\Auth;
+
+interface Authenticatable
+{
+    /**
+     * Get the name of the unique identifier for the user.
+     *
+     * @return string
+     */
+    public function getAuthIdentifierName();
+
+    /**
+     * Get the unique identifier for the user.
+     *
+     * @return mixed
+     */
+    public function getAuthIdentifier();
+
+    /**
+     * Get the password for the user.
+     *
+     * @return string
+     */
+    public function getAuthPassword();
+
+    /**
+     * Get the token value for the "remember me" session.
+     *
+     * @return string
+     */
+    public function getRememberToken();
+
+    /**
+     * Set the token value for the "remember me" session.
+     *
+     * @param  string  $value
+     * @return void
+     */
+    public function setRememberToken($value);
+
+    /**
+     * Get the column name for the "remember me" token.
+     *
+     * @return string
+     */
+    public function getRememberTokenName();
+}

+ 21 - 0
htdocs/includes/webklex/php-imap/vendor/illuminate/contracts/Auth/CanResetPassword.php

@@ -0,0 +1,21 @@
+<?php
+
+namespace Illuminate\Contracts\Auth;
+
+interface CanResetPassword
+{
+    /**
+     * Get the e-mail address where password reset links are sent.
+     *
+     * @return string
+     */
+    public function getEmailForPasswordReset();
+
+    /**
+     * Send the password reset notification.
+     *
+     * @param  string  $token
+     * @return void
+     */
+    public function sendPasswordResetNotification($token);
+}

+ 22 - 0
htdocs/includes/webklex/php-imap/vendor/illuminate/contracts/Auth/Factory.php

@@ -0,0 +1,22 @@
+<?php
+
+namespace Illuminate\Contracts\Auth;
+
+interface Factory
+{
+    /**
+     * Get a guard instance by name.
+     *
+     * @param  string|null  $name
+     * @return \Illuminate\Contracts\Auth\Guard|\Illuminate\Contracts\Auth\StatefulGuard
+     */
+    public function guard($name = null);
+
+    /**
+     * Set the default guard the factory should serve.
+     *
+     * @param  string  $name
+     * @return void
+     */
+    public function shouldUse($name);
+}

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác