diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..754ee70 --- /dev/null +++ b/composer.json @@ -0,0 +1,19 @@ +{ + "require": { + "guzzlehttp/guzzle": "^7.9", + "symfony/mailer": "^7.2", + "symfony/http-client": "^7.2", + "phpseclib/phpseclib": "^3.0", + "openai-php/client": "^0.10.3", + "php-imap/php-imap": "^5.0", + "symfony/mime": "^7.2" + }, + "config": { + "allow-plugins": { + "php-http/discovery": true + } + }, + "require-dev": { + "phpunit/phpunit": "^11.5" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..ea87ca1 --- /dev/null +++ b/composer.lock @@ -0,0 +1,3989 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "2935caf8cf4375027805e7ce846b022d", + "packages": [ + { + "name": "doctrine/deprecations", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9", + "phpstan/phpstan": "1.4.10 || 1.10.15", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psalm/plugin-phpunit": "0.18.4", + "psr/log": "^1 || ^2 || ^3", + "vimeo/psalm": "4.30.0 || 5.12.0" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.3" + }, + "time": "2024-01-30T19:34:25+00:00" + }, + { + "name": "doctrine/lexer", + "version": "2.1.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "861c870e8b75f7c8f69c146c7f89cc1c0f1b49b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/861c870e8b75f7c8f69c146c7f89cc1c0f1b49b6", + "reference": "861c870e8b75f7c8f69c146c7f89cc1c0f1b49b6", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9 || ^12", + "phpstan/phpstan": "^1.3", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6", + "psalm/plugin-phpunit": "^0.18.3", + "vimeo/psalm": "^4.11 || ^5.21" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Lexer\\": "src" + } + }, + "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": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", + "keywords": [ + "annotations", + "docblock", + "lexer", + "parser", + "php" + ], + "support": { + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/2.1.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%2Flexer", + "type": "tidelift" + } + ], + "time": "2024-02-05T11:35:39+00:00" + }, + { + "name": "egulias/email-validator", + "version": "3.2.6", + "source": { + "type": "git", + "url": "https://github.com/egulias/EmailValidator.git", + "reference": "e5997fa97e8790cdae03a9cbd5e78e45e3c7bda7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/e5997fa97e8790cdae03a9cbd5e78e45e3c7bda7", + "reference": "e5997fa97e8790cdae03a9cbd5e78e45e3c7bda7", + "shasum": "" + }, + "require": { + "doctrine/lexer": "^1.2|^2", + "php": ">=7.2", + "symfony/polyfill-intl-idn": "^1.15" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.8|^9.3.3", + "vimeo/psalm": "^4" + }, + "suggest": { + "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Egulias\\EmailValidator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eduardo Gulias Davis" + } + ], + "description": "A library for validating emails against several RFCs", + "homepage": "https://github.com/egulias/EmailValidator", + "keywords": [ + "email", + "emailvalidation", + "emailvalidator", + "validation", + "validator" + ], + "support": { + "issues": "https://github.com/egulias/EmailValidator/issues", + "source": "https://github.com/egulias/EmailValidator/tree/3.2.6" + }, + "funding": [ + { + "url": "https://github.com/egulias", + "type": "github" + } + ], + "time": "2023-06-01T07:04:22+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "7.9.2", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "d281ed313b989f213357e3be1a179f02196ac99b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b", + "reference": "d281ed313b989f213357e3be1a179f02196ac99b", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^1.5.3 || ^2.0.3", + "guzzlehttp/psr7": "^2.7.0", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-curl": "*", + "guzzle/client-integration-tests": "3.0.2", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.39 || ^9.6.20", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.9.2" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2024-07-24T11:22:20+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/f9c436286ab2892c7db7be8c8da4ef61ccf7b455", + "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.39 || ^9.6.20" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2024-10-17T10:06:22+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.7.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201", + "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "http-interop/http-factory-tests": "0.9.0", + "phpunit/phpunit": "^8.5.39 || ^9.6.20" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.7.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2024-07-18T11:15:46+00:00" + }, + { + "name": "openai-php/client", + "version": "v0.10.3", + "source": { + "type": "git", + "url": "https://github.com/openai-php/client.git", + "reference": "4a565d145e0fb3ea1baba8fffe39d86c56b6dc2c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/openai-php/client/zipball/4a565d145e0fb3ea1baba8fffe39d86c56b6dc2c", + "reference": "4a565d145e0fb3ea1baba8fffe39d86c56b6dc2c", + "shasum": "" + }, + "require": { + "php": "^8.1.0", + "php-http/discovery": "^1.20.0", + "php-http/multipart-stream-builder": "^1.4.2", + "psr/http-client": "^1.0.3", + "psr/http-client-implementation": "^1.0.1", + "psr/http-factory-implementation": "*", + "psr/http-message": "^1.1.0|^2.0.0" + }, + "require-dev": { + "guzzlehttp/guzzle": "^7.9.2", + "guzzlehttp/psr7": "^2.7.0", + "laravel/pint": "^1.18.1", + "mockery/mockery": "^1.6.12", + "nunomaduro/collision": "^7.11.0|^8.5.0", + "pestphp/pest": "^2.36.0|^3.5.0", + "pestphp/pest-plugin-arch": "^2.7|^3.0", + "pestphp/pest-plugin-type-coverage": "^2.8.7|^3.1.0", + "phpstan/phpstan": "^1.12.7", + "symfony/var-dumper": "^6.4.11|^7.1.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/OpenAI.php" + ], + "psr-4": { + "OpenAI\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + }, + { + "name": "Sandro Gehri" + } + ], + "description": "OpenAI PHP is a supercharged PHP API client that allows you to interact with the Open AI API", + "keywords": [ + "GPT-3", + "api", + "client", + "codex", + "dall-e", + "language", + "natural", + "openai", + "php", + "processing", + "sdk" + ], + "support": { + "issues": "https://github.com/openai-php/client/issues", + "source": "https://github.com/openai-php/client/tree/v0.10.3" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/gehrisandro", + "type": "github" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + } + ], + "time": "2024-11-12T20:51:16+00:00" + }, + { + "name": "paragonie/constant_time_encoding", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/paragonie/constant_time_encoding.git", + "reference": "df1e7fde177501eee2037dd159cf04f5f301a512" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/df1e7fde177501eee2037dd159cf04f5f301a512", + "reference": "df1e7fde177501eee2037dd159cf04f5f301a512", + "shasum": "" + }, + "require": { + "php": "^8" + }, + "require-dev": { + "phpunit/phpunit": "^9", + "vimeo/psalm": "^4|^5" + }, + "type": "library", + "autoload": { + "psr-4": { + "ParagonIE\\ConstantTime\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com", + "role": "Maintainer" + }, + { + "name": "Steve 'Sc00bz' Thomas", + "email": "steve@tobtu.com", + "homepage": "https://www.tobtu.com", + "role": "Original Developer" + } + ], + "description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)", + "keywords": [ + "base16", + "base32", + "base32_decode", + "base32_encode", + "base64", + "base64_decode", + "base64_encode", + "bin2hex", + "encoding", + "hex", + "hex2bin", + "rfc4648" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/constant_time_encoding/issues", + "source": "https://github.com/paragonie/constant_time_encoding" + }, + "time": "2024-05-08T12:36:18+00:00" + }, + { + "name": "paragonie/random_compat", + "version": "v9.99.100", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a", + "shasum": "" + }, + "require": { + "php": ">= 7" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*", + "vimeo/psalm": "^1" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "polyfill", + "pseudorandom", + "random" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/random_compat/issues", + "source": "https://github.com/paragonie/random_compat" + }, + "time": "2020-10-15T08:29:30+00:00" + }, + { + "name": "php-http/discovery", + "version": "1.20.0", + "source": { + "type": "git", + "url": "https://github.com/php-http/discovery.git", + "reference": "82fe4c73ef3363caed49ff8dd1539ba06044910d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/discovery/zipball/82fe4c73ef3363caed49ff8dd1539ba06044910d", + "reference": "82fe4c73ef3363caed49ff8dd1539ba06044910d", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0|^2.0", + "php": "^7.1 || ^8.0" + }, + "conflict": { + "nyholm/psr7": "<1.0", + "zendframework/zend-diactoros": "*" + }, + "provide": { + "php-http/async-client-implementation": "*", + "php-http/client-implementation": "*", + "psr/http-client-implementation": "*", + "psr/http-factory-implementation": "*", + "psr/http-message-implementation": "*" + }, + "require-dev": { + "composer/composer": "^1.0.2|^2.0", + "graham-campbell/phpspec-skip-example-extension": "^5.0", + "php-http/httplug": "^1.0 || ^2.0", + "php-http/message-factory": "^1.0", + "phpspec/phpspec": "^5.1 || ^6.1 || ^7.3", + "sebastian/comparator": "^3.0.5 || ^4.0.8", + "symfony/phpunit-bridge": "^6.4.4 || ^7.0.1" + }, + "type": "composer-plugin", + "extra": { + "class": "Http\\Discovery\\Composer\\Plugin", + "plugin-optional": true + }, + "autoload": { + "psr-4": { + "Http\\Discovery\\": "src/" + }, + "exclude-from-classmap": [ + "src/Composer/Plugin.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" + } + ], + "description": "Finds and installs PSR-7, PSR-17, PSR-18 and HTTPlug implementations", + "homepage": "http://php-http.org", + "keywords": [ + "adapter", + "client", + "discovery", + "factory", + "http", + "message", + "psr17", + "psr7" + ], + "support": { + "issues": "https://github.com/php-http/discovery/issues", + "source": "https://github.com/php-http/discovery/tree/1.20.0" + }, + "time": "2024-10-02T11:20:13+00:00" + }, + { + "name": "php-http/multipart-stream-builder", + "version": "1.4.2", + "source": { + "type": "git", + "url": "https://github.com/php-http/multipart-stream-builder.git", + "reference": "10086e6de6f53489cca5ecc45b6f468604d3460e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/multipart-stream-builder/zipball/10086e6de6f53489cca5ecc45b6f468604d3460e", + "reference": "10086e6de6f53489cca5ecc45b6f468604d3460e", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0", + "php-http/discovery": "^1.15", + "psr/http-factory-implementation": "^1.0" + }, + "require-dev": { + "nyholm/psr7": "^1.0", + "php-http/message": "^1.5", + "php-http/message-factory": "^1.0.2", + "phpunit/phpunit": "^7.5.15 || ^8.5 || ^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Http\\Message\\MultipartStream\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com" + } + ], + "description": "A builder class that help you create a multipart stream", + "homepage": "http://php-http.org", + "keywords": [ + "factory", + "http", + "message", + "multipart stream", + "stream" + ], + "support": { + "issues": "https://github.com/php-http/multipart-stream-builder/issues", + "source": "https://github.com/php-http/multipart-stream-builder/tree/1.4.2" + }, + "time": "2024-09-04T13:22:54+00:00" + }, + { + "name": "php-imap/php-imap", + "version": "5.0.1", + "source": { + "type": "git", + "url": "https://github.com/barbushin/php-imap.git", + "reference": "94107fdd1383285459a7f6c2dd2f39e25a1b8373" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/barbushin/php-imap/zipball/94107fdd1383285459a7f6c2dd2f39e25a1b8373", + "reference": "94107fdd1383285459a7f6c2dd2f39e25a1b8373", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "ext-iconv": "*", + "ext-imap": "*", + "ext-json": "*", + "ext-mbstring": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.4", + "maglnet/composer-require-checker": "^2.0|^3.2", + "nikic/php-parser": "^4.3,<4.7|^4.10", + "paragonie/hidden-string": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.3", + "phpunit/phpunit": "^8.5|^9.5", + "povils/phpmnd": "^2.2", + "psalm/plugin-phpunit": "^0.10.0|^0.15.1", + "roave/security-advisories": "dev-master", + "sebastian/phpcpd": "^4.1|^6.0" + }, + "suggest": { + "ext-fileinfo": "To facilitate IncomingMailAttachment::getFileInfo() auto-detection" + }, + "type": "library", + "autoload": { + "psr-4": { + "PhpImap\\": "src/PhpImap" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sergey Barbushin", + "email": "barbushin@gmail.com", + "homepage": "http://linkedin.com/in/barbushin" + } + ], + "description": "Manage mailboxes, filter/get/delete emails in PHP (supports IMAP/POP3/NNTP)", + "homepage": "https://github.com/barbushin/php-imap", + "keywords": [ + "imap", + "mail", + "mailbox", + "php", + "pop3", + "receive emails" + ], + "support": { + "issues": "https://github.com/barbushin/php-imap/issues", + "source": "https://github.com/barbushin/php-imap/tree/5.0.1" + }, + "time": "2022-12-05T15:47:03+00:00" + }, + { + "name": "phpseclib/phpseclib", + "version": "3.0.42", + "source": { + "type": "git", + "url": "https://github.com/phpseclib/phpseclib.git", + "reference": "db92f1b1987b12b13f248fe76c3a52cadb67bb98" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/db92f1b1987b12b13f248fe76c3a52cadb67bb98", + "reference": "db92f1b1987b12b13f248fe76c3a52cadb67bb98", + "shasum": "" + }, + "require": { + "paragonie/constant_time_encoding": "^1|^2|^3", + "paragonie/random_compat": "^1.4|^2.0|^9.99.99", + "php": ">=5.6.1" + }, + "require-dev": { + "phpunit/phpunit": "*" + }, + "suggest": { + "ext-dom": "Install the DOM extension to load XML formatted public keys.", + "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.", + "ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.", + "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.", + "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations." + }, + "type": "library", + "autoload": { + "files": [ + "phpseclib/bootstrap.php" + ], + "psr-4": { + "phpseclib3\\": "phpseclib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jim Wigginton", + "email": "terrafrost@php.net", + "role": "Lead Developer" + }, + { + "name": "Patrick Monnerat", + "email": "pm@datasphere.ch", + "role": "Developer" + }, + { + "name": "Andreas Fischer", + "email": "bantu@phpbb.com", + "role": "Developer" + }, + { + "name": "Hans-Jürgen Petrich", + "email": "petrich@tronic-media.com", + "role": "Developer" + }, + { + "name": "Graham Campbell", + "email": "graham@alt-three.com", + "role": "Developer" + } + ], + "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.", + "homepage": "http://phpseclib.sourceforge.net", + "keywords": [ + "BigInteger", + "aes", + "asn.1", + "asn1", + "blowfish", + "crypto", + "cryptography", + "encryption", + "rsa", + "security", + "sftp", + "signature", + "signing", + "ssh", + "twofish", + "x.509", + "x509" + ], + "support": { + "issues": "https://github.com/phpseclib/phpseclib/issues", + "source": "https://github.com/phpseclib/phpseclib/tree/3.0.42" + }, + "funding": [ + { + "url": "https://github.com/terrafrost", + "type": "github" + }, + { + "url": "https://www.patreon.com/phpseclib", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib", + "type": "tidelift" + } + ], + "time": "2024-09-16T03:06:04+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "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/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "time": "2024-04-15T12:06:14+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.5.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "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/v3.5.1" + }, + "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" + } + ], + "time": "2024-09-25T14:20:29+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "910c5db85a5356d0fea57680defec4e99eb9c8c1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/910c5db85a5356d0fea57680defec4e99eb9c8c1", + "reference": "910c5db85a5356d0fea57680defec4e99eb9c8c1", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/event-dispatcher-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/service-contracts": "<2.5" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/error-handler": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "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 that allow your application components to communicate with each other by dispatching events and listening to them", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v7.2.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" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v3.5.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "7642f5e970b672283b7823222ae8ef8bbc160b9f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/7642f5e970b672283b7823222ae8ef8bbc160b9f", + "reference": "7642f5e970b672283b7823222ae8ef8bbc160b9f", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/event-dispatcher": "^1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "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 dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.5.1" + }, + "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" + } + ], + "time": "2024-09-25T14:20:29+00:00" + }, + { + "name": "symfony/http-client", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client.git", + "reference": "955e43336aff03df1e8a8e17daefabb0127a313b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client/zipball/955e43336aff03df1e8a8e17daefabb0127a313b", + "reference": "955e43336aff03df1e8a8e17daefabb0127a313b", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-client-contracts": "~3.4.3|^3.5.1", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "amphp/amp": "<2.5", + "php-http/discovery": "<1.15", + "symfony/http-foundation": "<6.4" + }, + "provide": { + "php-http/async-client-implementation": "*", + "php-http/client-implementation": "*", + "psr/http-client-implementation": "1.0", + "symfony/http-client-implementation": "3.0" + }, + "require-dev": { + "amphp/http-client": "^4.2.1|^5.0", + "amphp/http-tunnel": "^1.0|^2.0", + "amphp/socket": "^1.1", + "guzzlehttp/promises": "^1.4|^2.0", + "nyholm/psr7": "^1.0", + "php-http/httplug": "^1.0|^2.0", + "psr/http-client": "^1.0", + "symfony/amphp-http-client-meta": "^1.0|^2.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/rate-limiter": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpClient\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "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": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", + "homepage": "https://symfony.com", + "keywords": [ + "http" + ], + "support": { + "source": "https://github.com/symfony/http-client/tree/v7.2.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" + } + ], + "time": "2024-11-29T08:22:02+00:00" + }, + { + "name": "symfony/http-client-contracts", + "version": "v3.5.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client-contracts.git", + "reference": "c2f3ad828596624ca39ea40f83617ef51ca8bbf9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/c2f3ad828596624ca39ea40f83617ef51ca8bbf9", + "reference": "c2f3ad828596624ca39ea40f83617ef51ca8bbf9", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\HttpClient\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "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 HTTP clients", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/http-client-contracts/tree/v3.5.1" + }, + "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" + } + ], + "time": "2024-11-25T12:02:18+00:00" + }, + { + "name": "symfony/mailer", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/mailer.git", + "reference": "e4d358702fb66e4c8a2af08e90e7271a62de39cc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mailer/zipball/e4d358702fb66e4c8a2af08e90e7271a62de39cc", + "reference": "e4d358702fb66e4c8a2af08e90e7271a62de39cc", + "shasum": "" + }, + "require": { + "egulias/email-validator": "^2.1.10|^3|^4", + "php": ">=8.2", + "psr/event-dispatcher": "^1", + "psr/log": "^1|^2|^3", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/mime": "^7.2", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<6.4", + "symfony/messenger": "<6.4", + "symfony/mime": "<6.4", + "symfony/twig-bridge": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/twig-bridge": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mailer\\": "" + }, + "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": "Helps sending emails", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/mailer/tree/v7.2.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" + } + ], + "time": "2024-11-25T15:21:05+00:00" + }, + { + "name": "symfony/mime", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/mime.git", + "reference": "cc84a4b81f62158c3846ac7ff10f696aae2b524d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mime/zipball/cc84a4b81f62158c3846ac7ff10f696aae2b524d", + "reference": "cc84a4b81f62158c3846ac7ff10f696aae2b524d", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-intl-idn": "^1.10", + "symfony/polyfill-mbstring": "^1.0" + }, + "conflict": { + "egulias/email-validator": "~3.0.0", + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/mailer": "<6.4", + "symfony/serializer": "<6.4.3|>7.0,<7.0.3" + }, + "require-dev": { + "egulias/email-validator": "^2.1.10|^3.1|^4", + "league/html-to-markdown": "^5.0", + "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/property-access": "^6.4|^7.0", + "symfony/property-info": "^6.4|^7.0", + "symfony/serializer": "^6.4.3|^7.0.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mime\\": "" + }, + "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": "Allows manipulating MIME messages", + "homepage": "https://symfony.com", + "keywords": [ + "mime", + "mime-type" + ], + "support": { + "source": "https://github.com/symfony/mime/tree/v7.2.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" + } + ], + "time": "2024-11-23T09:19:39+00:00" + }, + { + "name": "symfony/polyfill-intl-idn", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/c36586dcf89a12315939e00ec9b4474adcb1d773", + "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773", + "shasum": "" + }, + "require": { + "php": ">=7.2", + "symfony/polyfill-intl-normalizer": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + }, + { + "name": "Trevor Rowbotham", + "email": "trevor.rowbotham@pm.me" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.31.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" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "3833d7255cc303546435cb650316bff708a1c75c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "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 intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.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" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "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.31.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" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.5.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e53260aabf78fb3d63f8d79d69ece59f80d5eda0", + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "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 writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.5.1" + }, + "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" + } + ], + "time": "2024-09-25T14:20:29+00:00" + } + ], + "packages-dev": [ + { + "name": "myclabs/deep-copy", + "version": "1.12.1", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845", + "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.12.1" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2024-11-08T17:47:46+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.3.1", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/8eea230464783aa9671db8eea6f8c6ac5285794b", + "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.3.1" + }, + "time": "2024-10-08T18:51:32+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "11.0.7", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "f7f08030e8811582cc459871d28d6f5a1a4d35ca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f7f08030e8811582cc459871d28d6f5a1a4d35ca", + "reference": "f7f08030e8811582cc459871d28d6f5a1a4d35ca", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^5.3.1", + "php": ">=8.2", + "phpunit/php-file-iterator": "^5.1.0", + "phpunit/php-text-template": "^4.0.1", + "sebastian/code-unit-reverse-lookup": "^4.0.1", + "sebastian/complexity": "^4.0.1", + "sebastian/environment": "^7.2.0", + "sebastian/lines-of-code": "^3.0.1", + "sebastian/version": "^5.0.2", + "theseer/tokenizer": "^1.2.3" + }, + "require-dev": { + "phpunit/phpunit": "^11.4.1" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "11.0.x-dev" + } + }, + "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 provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.7" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-10-09T06:21:38+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "5.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/118cfaaa8bc5aef3287bf315b6060b1174754af6", + "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.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": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-08-27T05:02:59+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "5.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/c1ca3814734c07492b3d4c5f794f4b0995333da2", + "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^11.0" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/5.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:07:44+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/3e0404dc6b300e6bf56415467ebcb3fe4f33e964", + "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "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", + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:08:43+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "7.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", + "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.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", + "security": "https://github.com/sebastianbergmann/php-timer/security/policy", + "source": "https://github.com/sebastianbergmann/php-timer/tree/7.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:09:35+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "11.5.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "0569902506a6c0878930b87ea79ec3b50ea563f7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/0569902506a6c0878930b87ea79ec3b50ea563f7", + "reference": "0569902506a6c0878930b87ea79ec3b50ea563f7", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.12.1", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.2", + "phpunit/php-code-coverage": "^11.0.7", + "phpunit/php-file-iterator": "^5.1.0", + "phpunit/php-invoker": "^5.0.1", + "phpunit/php-text-template": "^4.0.1", + "phpunit/php-timer": "^7.0.1", + "sebastian/cli-parser": "^3.0.2", + "sebastian/code-unit": "^3.0.1", + "sebastian/comparator": "^6.2.1", + "sebastian/diff": "^6.0.2", + "sebastian/environment": "^7.2.0", + "sebastian/exporter": "^6.3.0", + "sebastian/global-state": "^7.0.2", + "sebastian/object-enumerator": "^6.0.1", + "sebastian/type": "^5.1.0", + "sebastian/version": "^5.0.2", + "staabm/side-effects-detector": "^1.0.5" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "11.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "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", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.0" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2024-12-06T05:57:38+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/15c5dd40dc4f38794d383bb95465193f5e0ae180", + "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "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 for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:41:36+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "6bb7d09d6623567178cf54126afa9c2310114268" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/6bb7d09d6623567178cf54126afa9c2310114268", + "reference": "6bb7d09d6623567178cf54126afa9c2310114268", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "security": "https://github.com/sebastianbergmann/code-unit/security/policy", + "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:44:28+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "183a9b2632194febd219bb9246eee421dad8d45e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/183a9b2632194febd219bb9246eee421dad8d45e", + "reference": "183a9b2632194febd219bb9246eee421dad8d45e", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "security": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/security/policy", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:45:54+00:00" + }, + { + "name": "sebastian/comparator", + "version": "6.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "43d129d6a0f81c78bee378b46688293eb7ea3739" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/43d129d6a0f81c78bee378b46688293eb7ea3739", + "reference": "43d129d6a0f81c78bee378b46688293eb7ea3739", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.2", + "sebastian/diff": "^6.0", + "sebastian/exporter": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/6.2.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-10-31T05:30:08+00:00" + }, + { + "name": "sebastian/complexity", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "ee41d384ab1906c68852636b6de493846e13e5a0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/ee41d384ab1906c68852636b6de493846e13e5a0", + "reference": "ee41d384ab1906c68852636b6de493846e13e5a0", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "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 for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:49:50+00:00" + }, + { + "name": "sebastian/diff", + "version": "6.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b4ccd857127db5d41a5b676f24b51371d76d8544", + "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/6.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:53:05+00:00" + }, + { + "name": "sebastian/environment", + "version": "7.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "855f3ae0ab316bbafe1ba4e16e9f3c078d24a0c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/855f3ae0ab316bbafe1ba4e16e9f3c078d24a0c5", + "reference": "855f3ae0ab316bbafe1ba4e16e9f3c078d24a0c5", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.2-dev" + } + }, + "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": "https://github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/7.2.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:54:44+00:00" + }, + { + "name": "sebastian/exporter", + "version": "6.3.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "3473f61172093b2da7de1fb5782e1f24cc036dc3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/3473f61172093b2da7de1fb5782e1f24cc036dc3", + "reference": "3473f61172093b2da7de1fb5782e1f24cc036dc3", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=8.2", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-12-05T09:17:50+00:00" + }, + { + "name": "sebastian/global-state", + "version": "7.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "3be331570a721f9a4b5917f4209773de17f747d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/3be331570a721f9a4b5917f4209773de17f747d7", + "reference": "3be331570a721f9a4b5917f4209773de17f747d7", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "sebastian/object-reflector": "^4.0", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "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": "https://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/7.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:57:36+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d36ad0d782e5756913e42ad87cb2890f4ffe467a", + "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "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 for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/3.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:58:38+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "6.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "f5b498e631a74204185071eb41f33f38d64608aa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/f5b498e631a74204185071eb41f33f38d64608aa", + "reference": "f5b498e631a74204185071eb41f33f38d64608aa", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "sebastian/object-reflector": "^4.0", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/6.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:00:13+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/6e1a43b411b2ad34146dee7524cb13a068bb35f9", + "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:01:32+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "6.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "694d156164372abbd149a4b85ccda2e4670c0e16" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/694d156164372abbd149a4b85ccda2e4670c0e16", + "reference": "694d156164372abbd149a4b85ccda2e4670c0e16", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:10:34+00:00" + }, + { + "name": "sebastian/type", + "version": "5.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "461b9c5da241511a2a0e8f240814fb23ce5c0aac" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/461b9c5da241511a2a0e8f240814fb23ce5c0aac", + "reference": "461b9c5da241511a2a0e8f240814fb23ce5c0aac", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "security": "https://github.com/sebastianbergmann/type/security/policy", + "source": "https://github.com/sebastianbergmann/type/tree/5.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-09-17T13:12:04+00:00" + }, + { + "name": "sebastian/version", + "version": "5.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c687e3387b99f5b03b6caa64c74b63e2936ff874", + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "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", + "security": "https://github.com/sebastianbergmann/version/security/policy", + "source": "https://github.com/sebastianbergmann/version/tree/5.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-10-09T05:16:32+00:00" + }, + { + "name": "staabm/side-effects-detector", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/staabm/side-effects-detector.git", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.6", + "phpunit/phpunit": "^9.6.21", + "symfony/var-dumper": "^5.4.43", + "tomasvotruba/type-coverage": "1.0.0", + "tomasvotruba/unused-public": "1.0.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A static analysis tool to detect side effects in PHP code", + "keywords": [ + "static analysis" + ], + "support": { + "issues": "https://github.com/staabm/side-effects-detector/issues", + "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" + }, + "funding": [ + { + "url": "https://github.com/staabm", + "type": "github" + } + ], + "time": "2024-10-20T05:08:20+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:36:25+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": {}, + "prefer-stable": false, + "prefer-lowest": false, + "platform": {}, + "platform-dev": {}, + "plugin-api-version": "2.6.0" +} diff --git a/includes/class-rl-mailwarmer-conversation-helper.php b/includes/class-rl-mailwarmer-conversation-helper.php new file mode 100644 index 0000000..1def1fe --- /dev/null +++ b/includes/class-rl-mailwarmer-conversation-helper.php @@ -0,0 +1,432 @@ +prefix . 'rl_mailwarmer_conversations'; + // $current_time = current_time('mysql'); + $current_time = date('Y-m-d H:i:s', strtotime('-24 hours')); + $future_time = date('Y-m-d H:i:s', strtotime('+24 hours')); + + // Fetch up to 50 conversations starting within the next 24 hours + $conversations = $wpdb->get_results( + $wpdb->prepare( + "SELECT * FROM $conversation_table WHERE status = %s AND first_message_timestamp BETWEEN %s AND %s LIMIT 1", + 'new', + $current_time, + $future_time + ), + ARRAY_A + ); + // log_to_file("process_upcoming_conversations - Conversations: ", $conversations); + + + foreach ($conversations as $conversation) { + // log_to_file("process_upcoming_conversations - Conversation: ", $conversation); + try { + // Generate conversation content using AI + // $ai_response = self::fetch_conversation_from_ai($conversation); + $prompt = $conversation['prompt']; + + // log_to_file("process_upcoming_conversations - AI prompt: ", $prompt); + + // $ai_response = RL_MailWarmer_Campaign_Helper::clean_ai_response(RL_MailWarmer_Campaign_Helper::generate_ai_conversation($prompt)); + // $ai_response = RL_MailWarmer_Campaign_Helper::generate_ai_conversation($prompt); + $ai_response = get_post_meta($conversation['campaign_id'], 'last_ai_response', true); + // log_to_file("process_upcoming_conversations - AI Response: ", $ai_response); + + if (is_wp_error($ai_response)) { + return $ai_response; // Handle AI generation failure gracefully + } + + $updated_conversation_steps = self::merge_timeline_with_ai_response($conversation['conversation_steps'], $ai_response); + // log_to_file("process_upcoming_conversations - Merged steps: ", $updated_conversation_steps); + // // Update conversation status to "generated" + $conversation_table = $wpdb->prefix . 'rl_mailwarmer_conversation'; + $status = 'new'; + $result = $wpdb->update( + $conversation_table, + // [], + ['status' => $status, 'conversation_steps' => $updated_conversation_steps], + ['id' => $conversation['id']], + ['%s'], + ['%s'], + ['%d'] + ); + // log_to_file("process_upcoming_conversations - Update DB Result: ", $result); + + $update_messages_db = self::update_messages_with_merged_output($conversation['id'], $updated_conversation_steps); + log_to_file("process_upcoming_conversations - Result of update_messages_with_merged_output(): ", $update_messages_db); + + // Update the conversation with generated steps + // log_to_file("process_upcoming_conversations - Updating conversation_steps"); + // if (RL_MailWarmer_Campaign_Helper::update_conversation_steps($conversation['id'], $updated_conversation_steps)) { + // // log_to_file("process_upcoming_conversations - Updated conversation_steps!"); + // } + + // Save the generated content to the conversation + // RL_MailWarmer_DB_Helper::update_conversation_steps($conversation['id'], $ai_response); + + + } catch (Exception $e) { + error_log("Failed to generate conversation ID {$conversation['id']}: " . $e->getMessage()); + } + } + } + + private static function update_messages_with_merged_output($conversation_id, $merged_output) { + log_to_file("update_messages_with_merged_output - merged_output: ", $merged_output); + global $wpdb; + $message_table = $wpdb->prefix . 'rl_mailwarmer_messages'; + // $merged_output = json_decode($merged_output); + + // Fetch all messages for the given conversation ID + $messages = $wpdb->get_results( + $wpdb->prepare( + "SELECT * FROM $message_table WHERE conversation_id = %d ORDER BY `scheduled_for_timestamp` ASC", + $conversation_id + ), + ARRAY_A + ); + log_to_file("update_messages_with_merged_output - Found Messages: ", $messages); + + if (empty($messages)) { + throw new Exception("No messages found for conversation ID $conversation_id."); + } + + // Iterate through messages and merged output + foreach ($messages as $index => $message) { + if (!isset($merged_output[$index])) { + continue; // Skip if there's no corresponding merged output + } + + $merged = $merged_output[$index]; + // log_to_file("update_messages_with_merged_output - merged: $merged"); + + // Prepare updated data + // $updated_data = [ + // 'status' => $merged['status'], + // 'scheduled_for_timestamp' => $merged['scheduled_for'], + // 'from_email' => $merged['from'], + // 'to_email' => json_encode($merged['to']), + // 'cc_email' => json_encode($merged['cc']), + // 'subject' => $merged['subject'], + // 'body' => $merged['body'], + // ]; + // log_to_file("update_messages_with_merged_output - Updated Data: ", $updated_data); + + // Update the database + // $wpdb->update( + // $message_table, + // $updated_data, + // ['id' => $message['id']], // Where clause + // [ + // '%s', // status + // '%s', // scheduled_for_timestamp + // '%s', // from_email + // '%s', // to_email + // '%s', // cc_email + // '%s', // subject + // '%s', // body + // ], + // ['%d'] // ID + // ); + } + + return true; + } + + /** + * Fetch a conversation from ChatGPT based on conversation parameters. + * + * @param array $conversation The conversation data. + * @return string The AI-generated conversation as JSON. + * @throws Exception If the API call fails. + */ + private static function fetch_conversation_from_ai($conversation) { + // Prepare the prompt for the AI request + $prompt = [ + 'profession' => $conversation['profession'], + 'from' => $conversation['initiated_by'], + 'to_pool' => json_decode($conversation['to_pool'], true), + 'subject' => $conversation['subject'], + 'num_of_replies' => $conversation['num_responses'], + 'num_of_participants' => $conversation['num_participants'], + 'max_days' => 3, + 'can_reply' => json_decode($conversation['reply_pool'], true), + 'available_to_cc' => json_decode($conversation['cc_pool'], true), + 'start_date' => $conversation['first_message_timestamp'], + 'end_date' => date('Y-m-d H:i:s', strtotime('+3 days', strtotime($conversation['first_message_timestamp']))) + ]; + + // Call OpenAI API + $response = RL_MailWarmer_Api_Helper::call_openai_api($prompt); + + if (!$response || !isset($response['choices'][0]['text'])) { + throw new Exception('Invalid response from OpenAI API'); + } + + return $response['choices'][0]['text']; + } + + public static function merge_timeline_with_ai_response($conversation_json, $ai_response_json) { + // Decode the JSON inputs into arrays + $conversation = json_decode($conversation_json, true); + $ai_response = json_decode($ai_response_json, true); + + // Ensure both arrays have the same number of elements + $merged = []; + $count_timeline = count($conversation); + $count_ai_response = count($ai_response); + + if ($count_ai_response > $count_timeline) { + // Trim the AI responses to match the timeline count + $ai_response = array_slice($ai_response, 0, $count_timeline); + } elseif ($count_ai_response < $count_timeline) { + throw new Exception('The number of AI responses is less than the timeline steps.'); + } + + // Merge corresponding elements + foreach ($conversation as $index => $timeline_step) { + $ai_message = $ai_response[$index]; + $merged[] = array_merge($timeline_step, $ai_message); + } + + // Return the merged result as JSON + return json_encode($merged, JSON_PRETTY_PRINT); + } +} + + +add_action('admin_menu', function () { + add_menu_page( + __('Conversations', 'rl-mailwarmer'), // Page title + __('Conversations', 'rl-mailwarmer'), // Menu title + 'manage_options', // Capability + 'rl-mailwarmer-conversations', // Menu slug + 'rl_mailwarmer_render_conversations_page', // Callback function + 'dashicons-email', // Icon + 8 // Position + ); +}); + +/** + * Render the Conversations admin page with checkboxes and a Delete button. + */ +function rl_mailwarmer_render_conversations_page() { + if (!current_user_can('manage_options')) { + return; + } + + global $wpdb; + + $table_name = $wpdb->prefix . 'rl_mailwarmer_conversations'; // Conversation table + $conversations = $wpdb->get_results("SELECT * FROM $table_name ORDER BY created_at DESC"); + + ?> +
+

+ + +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + id); ?>campaign_id); ?>created_at); ?>conversation_steps); ?> +
+ +
prompt); ?>
+
+
+

+ +

+
+ +
+ + + get_var("SELECT COUNT(*) FROM {$wpdb->prefix}rl_mailwarmer_conversation"); + $conversations = $wpdb->get_results($wpdb->prepare( + "SELECT * FROM {$wpdb->prefix}rl_mailwarmer_conversation ORDER BY created_at DESC LIMIT %d OFFSET %d", + $per_page, + $offset + )); + + return ['conversations' => $conversations, 'total' => $total]; +} + +add_action('wp_ajax_rl_delete_conversations', function () { + global $wpdb; + + if (!current_user_can('manage_options')) { + wp_send_json_error(['message' => __('Permission denied.', 'rl-mailwarmer')]); + } + + $conversation_ids = isset($_POST['conversation_ids']) ? array_map('intval', $_POST['conversation_ids']) : []; + + if (empty($conversation_ids)) { + wp_send_json_error(['message' => __('No conversations selected.', 'rl-mailwarmer')]); + } + + $table_name = $wpdb->prefix . 'rl_mailwarmer_conversation'; + + // Delete selected conversations + $placeholders = implode(',', array_fill(0, count($conversation_ids), '%d')); + $query = "DELETE FROM $table_name WHERE id IN ($placeholders)"; + $result = $wpdb->query($wpdb->prepare($query, $conversation_ids)); + + if ($result === false) { + wp_send_json_error(['message' => __('Failed to delete conversations.', 'rl-mailwarmer')]); + } + + wp_send_json_success(['message' => __('Conversations deleted successfully.', 'rl-mailwarmer')]); +}); + + +// Add the metabox +add_action('add_meta_boxes', 'rl_mailwarmer_add_process_conversations_metabox'); +function rl_mailwarmer_add_process_conversations_metabox() { + add_meta_box( + 'rl-process-conversations', + __('Process Upcoming Conversations', 'rl-mailwarmer'), + 'rl_mailwarmer_process_conversations_metabox_callback', + 'campaign', + 'side', + 'default' + ); +} + +// Metabox callback function +function rl_mailwarmer_process_conversations_metabox_callback($post) { + // Nonce field for security + wp_nonce_field('rl_process_conversations_nonce', 'rl_process_conversations_nonce_field'); + + ?> + +
+ admin_url('admin-ajax.php'), + 'nonce' => wp_create_nonce('rl_process_conversations_nonce'), + ]); + } +} + +// AJAX handler +add_action('wp_ajax_rl_process_upcoming_conversations', 'rl_mailwarmer_process_upcoming_conversations_handler'); +function rl_mailwarmer_process_upcoming_conversations_handler() { + check_ajax_referer('rl_process_conversations_nonce', 'security'); + + $post_id = intval($_POST['post_id']); + + if (!$post_id) { + wp_send_json_error(__('Invalid campaign ID.', 'rl-mailwarmer')); + } + + try { + // Call the function to process upcoming conversations + RL_MailWarmer_Conversation_Handler::process_upcoming_conversations($post_id); + + wp_send_json_success(__('Conversations processed successfully.', 'rl-mailwarmer')); + } catch (Exception $e) { + wp_send_json_error(__('Error processing conversations: ', 'rl-mailwarmer') . $e->getMessage()); + } +} \ No newline at end of file diff --git a/includes/class-rl-mailwarmer-email-account-helper.php b/includes/class-rl-mailwarmer-email-account-helper.php new file mode 100644 index 0000000..e317acf --- /dev/null +++ b/includes/class-rl-mailwarmer-email-account-helper.php @@ -0,0 +1,627 @@ + 'create', + // 'email' => 'johndoe@example.com', + // 'metadata' => [ + // 'full_name' => 'John Doe', + // 'mail_password' => 'securepassword123', + // 'email_provider' => 123, // Post ID of the email provider + // 'smtp_server' => 'smtp.example.com', + // 'smtp_port' => 587, + // 'imap_server' => 'imap.example.com', + // 'imap_port' => 993, + // ], + // ]); + + // // Update an Email Account + // $result = RL_MailWarmer_Email_Helper::modify_email_account([ + // 'action' => 'update', + // 'post_id' => $post_id, + // 'metadata' => [ + // 'full_name' => 'Jane Doe', + // 'mail_password' => 'newsecurepassword123', + // 'smtp_status' => 'connected', + // ], + // ]); + + // // Delete an Email Account + // $result = RL_MailWarmer_Email_Helper::modify_email_account([ + // 'action' => 'delete', + // 'post_id' => $post_id, + // ]); + + + + + public static function modify_email_account(array $args) + { + // Validate required arguments + if (empty($args['action']) || !in_array($args['action'], ['create', 'update', 'delete'], true)) { + throw new InvalidArgumentException('Invalid or missing action.'); + } + + /* + * Add validation to only delete email-account posts + * + */ + + + $action = $args['action']; + $post_id = $args['post_id'] ?? null; + + // Handle delete action + if ($action === 'delete') { + if (!$post_id) { + throw new InvalidArgumentException('Post ID is required for deletion.'); + } + return wp_delete_post($post_id, true); + } + + // Validate fields for create/update + $post_data = [ + 'post_type' => 'email-account', + 'post_status' => 'publish', + ]; + + // For "create", ensure no existing post with the same title + if ($action === 'create') { + if (empty($args['email'])) { + throw new InvalidArgumentException('Email is required for creating a new account.'); + } + + $existing_post = get_page_by_title($args['email'], OBJECT, 'email-account'); + if ($existing_post) { + throw new RuntimeException('An email account with this title already exists.'); + } + + $post_data['post_title'] = $args['email']; + } elseif ($action === 'update') { + if (!$post_id) { + throw new InvalidArgumentException('Post ID is required for updates.'); + } + $post_data['ID'] = $post_id; + } + + // Assemble metadata + $meta_args = $args['metadata'] ?? []; + // Generate a random password if mail_password is not provided + if (empty($meta_args['mail_password'])) { + $meta_args['mail_password'] = bin2hex(random_bytes(8)); // 16-character password + } + $post_data['meta_input'] = array_map('sanitize_text_field', $meta_args); + + // Save or update the post + $post_id = wp_insert_post($post_data); + if (is_wp_error($post_id)) { + throw new RuntimeException('Failed to save email account post: ' . $post_id->get_error_message()); + } + + return $post_id; + } + + + /** + * Check mail login credentials for an email account. + * + * Validates IMAP/SMTP connection settings for the given email account. + * + * @param mixed $email_account The email-account post object or ID. + * @param string|null $protocol Optional. The protocol to validate ('IMAP' or 'SMTP'). Defaults to both. + * @return array|WP_Error Validation results for IMAP and/or SMTP or WP_Error on failure. + */ + public static function check_mail_login($email_account, $protocol = null) + { + log_to_file("check_mail_login - Email account id: {$email_account}"); + // Get the post object + $post = is_numeric($email_account) ? get_post($email_account) : $email_account; + if (!$post || $post->post_type !== 'email-account') { + return new WP_Error('invalid_post', __('Invalid email account post.', 'rl-mailwarmer')); + } + + // Fetch email provider and override defaults with saved values + $email_provider_id = get_post_meta($post->ID, 'email_provider', true); + // log_to_file("check_mail_login - "); + + // log_to_file("check_mail_login - Email Provider ID $email_provider_id"); + $defaults = $email_provider_id ? self::get_provider_defaults($email_provider_id) : []; + // log_to_file("check_mail_login - Email Provider Defaults: ", $defaults); + + // Fetch saved settings + $saved_settings = [ + 'full_name' => get_post_meta($post->ID, 'full_name', true), + 'email_signature' => get_post_meta($post->ID, 'email_signature', true), + 'mail_password' => get_post_meta($post->ID, 'mail_password', true), + 'imap_password' => get_post_meta($post->ID, 'imap_password', true), + 'imap_server' => get_post_meta($post->ID, 'imap_server', true), + 'imap_port' => get_post_meta($post->ID, 'imap_port', true), + 'smtp_password' => get_post_meta($post->ID, 'smtp_password', true), + 'smtp_server' => get_post_meta($post->ID, 'smtp_server', true), + 'smtp_port' => get_post_meta($post->ID, 'smtp_port', true), + ]; + + // Merge saved settings with defaults + $settings = array_merge($defaults, array_filter($saved_settings)); + + // log_to_file("check_mail_login - Using settings: ", $settings); + + $results = []; + + // Validate IMAP connection if required + if ($protocol === null || strtoupper($protocol) === 'IMAP') { + $imap_result = self::validate_imap_connection($post->post_title, $settings); + // log_to_file("check_mail_login - IMAP Result for " . $post->post_title . ": ", $imap_result); + $results['IMAP'] = $imap_result ? __('SUCCESS', 'rl-mailwarmer') : $imap_result->get_error_message(); + update_post_meta($post->ID, 'imap_status', $results['IMAP']); + } + + // Validate SMTP connection if required + if ($protocol === null || strtoupper($protocol) === 'SMTP') { + $smtp_result = self::validate_smtp_connection($post->post_title, $settings); + // log_to_file("check_mail_login - SMTP Result for " . $post->post_title . ": ", $imap_result); + $results['SMTP'] = $smtp_result ? __('SUCCESS', 'rl-mailwarmer') : $smtp_result->get_error_message(); + update_post_meta($post->ID, 'smtp_status', $results['SMTP']); + } + // log_to_file("check_mail_login - Full Results for " . $post->post_title . ": ", $results); + + return $results; + } + + /** + * Fetch default settings for an email provider. + * + * @param int $email_provider_id The post ID of the email provider. + * @return array The default server settings. + */ + public static function get_provider_defaults($email_provider_id) + { + return [ + 'imap_server' => get_post_meta($email_provider_id, 'default_imap_server', true), + 'imap_port' => get_post_meta($email_provider_id, 'default_imap_port', true), + 'smtp_server' => get_post_meta($email_provider_id, 'default_smtp_server', true), + 'smtp_port' => get_post_meta($email_provider_id, 'default_smtp_port', true), + ]; + } + + /** + * Validate an IMAP connection for an email account. + * + * @param string $email The email address. + * @param array $settings The server settings (imap_server, imap_port, imap_password). + * @return bool True if the connection is successful, false otherwise. + */ + private static function validate_imap_connection($email, $settings) + { + if ( empty($settings['imap_server']) || empty($settings['imap_port']) ) { + return false; // Missing required settings + } + + if (!empty($settings['imap_password'])) { + $password = $settings['imap_password']; + } else { + $password = $settings['mail_password']; + } + + // Try connecting to the IMAP server + $imap_stream = @imap_open( + '{' . $settings['imap_server'] . ':' . $settings['imap_port'] . '/imap/ssl}', + $email, + $password + ); + + if ($imap_stream) { + imap_close($imap_stream); // Close connection if successful + return true; + } + + return false; // Connection failed + } + + + /** + * Validate an SMTP connection for an email account using Symfony Mailer. + * + * @param string $email The email address. + * @param array $settings The server settings (smtp_server, smtp_port, smtp_password). + * @return bool True if the connection is successful, false otherwise. + */ + private static function validate_smtp_connection($email, $settings) + { + if (empty($settings['smtp_server']) || empty($settings['smtp_port']) ) { + return false; // Missing required settings + } + + if (!empty($settings['smtp_password'])) { + $password = $settings['smtp_password']; + } else { + $password = $settings['mail_password']; + } + + $signature = str_replace('\n', PHP_EOL, $settings['email_signature']); + $test_to_email = "ruben@redlotusaustin.com"; + $email_body = "This is a test email to verify SMTP connection for {$email}\n\n{$signature}"; + + try { + // Create the SMTP transport + $transport = new Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport( + $settings['smtp_server'], + $settings['smtp_port'] + ); + + // Set authentication details + $transport->setUsername($email); + $transport->setPassword($password); + + // Create the mailer + $mailer = new Symfony\Component\Mailer\Mailer($transport); + + // Send a test email + $test_email = (new Symfony\Component\Mime\Email()) + ->from("{$settings['full_name']} <{$email}>") + ->to($test_to_email) + ->subject('SMTP Connection Test for ' . $email) + ->html($email_body); + + $mailer->send($test_email); + + return true; + } catch (Exception $e) { + error_log('SMTP validation failed: ' . $e->getMessage()); + return false; + } + } + + + /** + * Generate random email accounts for the specified domain. + * + * @param string $domain The domain name to use for the email accounts. + * @param int $qty The number of email accounts to generate. Defaults to 1. + * @return array List of generated names and email addresses. + * @throws Exception If name pools are empty or post creation fails. + */ + public static function generate_random_accounts($domain, $qty = 1) + { + $domain_post = get_post($domain); + // Fetch name pools from ACF options + $first_name_pool = get_field('valid_first_name_pool', 'option'); + $last_name_pool = get_field('valid_last_name_pool', 'option'); + + if (empty($first_name_pool) || empty($last_name_pool)) { + throw new Exception(__('Name pools are empty. Please configure them in the ACF options.', 'rl-mailwarmer')); + } + + $first_names = explode(',', $first_name_pool); // Assume comma-separated list + $last_names = explode(',', $last_name_pool); + + $generated_accounts = []; + + for ($i = 0; $i < $qty; $i++) { + // Generate a random name + $first_name = trim($first_names[array_rand($first_names)]); + $last_name = trim($last_names[array_rand($last_names)]); + $full_name = "{$first_name} {$last_name}"; + + // Generate a semi-random email address + $email_formats = [ + "{$first_name}{$last_name}", + "{$first_name}.{$last_name}", + substr($first_name, 0, 1) . ".{$last_name}", + "{$first_name}.l", + substr($first_name, 0, 1) . $last_name, + ]; + $email_local_part = strtolower($email_formats[array_rand($email_formats)]); + $email_address = "{$email_local_part}@{$domain_post->post_title}"; + + // Generate a random password + $random_password = wp_generate_password(16, false, false); + $signature = self::generate_random_email_signature($first_name, $last_name, $email_address); + + $servers[] = 108; + $mailferno_default_email_provider = 92; + + // Create the email-account post + $post_id = wp_insert_post([ + 'post_type' => 'email-account', + 'post_status' => 'publish', + 'post_title' => $email_address, + 'meta_input' => [ + 'full_name' => "{$first_name} {$last_name}", + 'mail_password' => $random_password, + 'email_signature' => $signature, + 'domain' => $domain_post->ID, + 'include_in_reply_pool' => true, + 'include_in_cc_pool' => true, + 'include_in_warmup_pool' => true, + 'servers' => $servers, + 'email_provider' => $mailferno_default_email_provider, + ], + ]); + log_to_file("generate_random_accounts - Added email account to local server: $post_id"); + + if ($post_id && !is_wp_error($post_id)) { + $generated_accounts[] = [ + 'name' => "{$first_name} {$last_name}", + 'email' => $email_address, + 'password' => $random_password, + 'post_id' => $post_id, + ]; + log_to_file("generate_random_accounts - {$first_name} {$last_name}\t{$email_address}\t{$random_password}"); + $add_account_result = self::modify_email_account_on_server($post_id, 'create'); + log_to_file("generate_random_accounts - Result of attempting to add account to remote server: ", $add_account_result); + + if ( isset($add_account_result['errors']) ) { + log_to_file("generate_random_accounts - Error modifying account on remote server: ", $add_account_result['errors']); + } else { + log_to_file("generate_random_accounts - Added $email_address to remote server: ", $add_account_result); + $login_test_results = self::check_mail_login($post_id); + log_to_file("generate_random_accounts - Login test results: ", $login_test_results); + } + } else { + error_log('Failed to create email-account post: ' . print_r($post_id, true)); + } + } + + return $generated_accounts; + } + + /** + * Generate a randomized email signature. + * + * @param string $first_name The first name of the person. + * @param string $last_name The last name of the person. + * @param string $email_address The email address of the person. + * @return string The generated email signature. + */ + private static function generate_random_email_signature($first_name, $last_name, $email_address) { + // First line variations + $first_line_variations = [ + 'Best regards', + 'Regards', + 'Yours truly', + 'Sincerely', + 'Warm regards', + 'Kind regards', + 'Best wishes', + 'Respectfully', + 'With gratitude', + 'All the best', + 'Cheers', + 'Thank you', + 'Warm wishes', + 'Yours sincerely', + 'Cordially', + 'With appreciation', + 'Many thanks', + 'Take care', + 'Faithfully', + 'Always', + ]; + + // Randomized job titles + $job_titles = [ + 'Software Engineer', + 'Marketing Specialist', + 'Sales Manager', + 'Project Coordinator', + 'Product Designer', + 'Customer Success Lead', + ]; + + // Randomized phone formats + // $phone_formats = [ + // '(555) %03d-%04d', + // '555-%03d-%04d', + // '+1 555 %03d %04d', + // ]; + + // // Social media handles (optional) + // $social_links = [ + // 'LinkedIn' => 'https://linkedin.com/in/' . strtolower($first_name . $last_name), + // 'Twitter' => 'https://twitter.com/' . strtolower($first_name . $last_name), + // 'GitHub' => 'https://github.com/' . strtolower($first_name . $last_name), + // ]; + + // Generate random elements + $first_line = $first_line_variations[array_rand($first_line_variations)]; + $job_title = $job_titles[array_rand($job_titles)]; + // $phone_number = sprintf($phone_formats[array_rand($phone_formats)], rand(100, 999), rand(1000, 9999)); + // $selected_social = array_rand($social_links); + + // Build the email signature + $signature = "

{$first_line},

"; + $signature .= "

{$first_name} {$last_name}
"; + $signature .= "{$job_title}
"; + $signature .= "Email: {$email_address}
"; + // $signature .= "Phone: {$phone_number}
"; + // $signature .= "{$selected_social}

"; + + return $signature; + } + + + /** + * Modify an email account on a VirtualMin server. + * + * @param int $account_id The email-account post ID. + * @param string $action The action to perform: 'create', 'update', or 'delete'. + * @return bool|WP_Error True on success, WP_Error on failure. + */ + public static function modify_email_account_on_server($account_id, $action) + { + // Validate email-account post + $email_account = get_post($account_id); + if (!$email_account || $email_account->post_type !== 'email-account') { + return new WP_Error('invalid_account', __('Invalid email account.', 'rl-mailwarmer')); + } + + // Fetch associated server posts + $domain_id = get_post_meta($account_id, 'domain', true); + if (!$domain_id) { + return new WP_Error('missing_domain', __('No associated domain found.', 'rl-mailwarmer')); + } + + $server_ids = get_post_meta($domain_id, 'servers', true); // Assuming this field holds server post IDs + if (empty($server_ids) || !is_array($server_ids)) { + return new WP_Error('missing_servers', __('No associated servers found.', 'rl-mailwarmer')); + } + + // Fetch email account details + $email_address = $email_account->post_title; + $password = get_post_meta($account_id, 'mail_password', true); + $full_name = get_post_meta($account_id, 'full_name', true); + [$username, $domain] = explode('@', $email_address); + + // Iterate over servers and perform the action + foreach ($server_ids as $server_id) { + $server = get_post($server_id); + if (!$server || $server->post_type !== 'server') { + continue; // Skip invalid server posts + } + + $server_ip = get_post_meta($server_id, 'ip_address', true); + $server_port = get_post_meta($server_id, 'ssh_port', true); + $server_user = get_post_meta($server_id, 'username', true); + $server_password = get_post_meta($server_id, 'ssh_private_key', true); + // $server_password = get_post_meta($server_id, 'password', true); + + if (!$server_ip || !$server_user || !$server_password) { + return new WP_Error('missing_server_details', __('Missing server credentials.', 'rl-mailwarmer')); + } + + // Build VirtualMin command + $command = "sudo virtualmin"; + if ($action === 'create') { + $command .= " create-user --domain $domain --user $username --pass '$password' --real '$full_name'"; + } elseif ($action === 'update') { + $command .= " modify-user --domain $domain --user $username --pass '$password' --real '$full_name'"; + } elseif ($action === 'delete') { + $command .= " delete-user --domain $domain --user $username"; + } else { + return new WP_Error('invalid_action', __('Invalid action specified.', 'rl-mailwarmer')); + } + log_to_file("modify_email_account_on_server - SSH Command: ", $command); + + // Execute the command via SSH + // $ssh = new phpseclib\Net\SSH2($server_ip); + // $key = new phpseclib\Crypt\PublicKeyLoader::loadPrivateKey($server_password); // Adjust for SSH key or plain password + $ssh = new SSH2($server_ip, $server_port); + + if (!empty($server_password)) { + // Load the private key from the postmeta field + $key = PublicKeyLoader::loadPrivateKey($server_password); + } else { + // Fallback to password-based authentication + // $key = $server_password; + log_to_file("modify_email_account_on_server - Server $$server_id ssh_private_key empty"); + return new WP_Error('ssh_login_failed', __('No private key found!', 'rl-mailwarmer')); + } + + if (!$ssh->login($server_user, $key)) { + return new WP_Error('ssh_login_failed', __('Failed to log into the server.', 'rl-mailwarmer')); + } + $output = $ssh->exec($command); + + if (strpos($output, 'failed') !== false) { + return new WP_Error('command_failed', __('Failed to execute VirtualMin command.', 'rl-mailwarmer')); + } + } + + return true; // Success + } + + +} + + +add_action('restrict_manage_posts', function ($post_type) { + if ($post_type === 'email-account') { + ?> + + + __('No email accounts selected.', 'rl-mailwarmer')]); + } + + foreach ($account_ids as $account_id) { + // Call the check_mail_login function for this email account + $result = RL_MailWarmer_Email_Helper::check_mail_login($account_id); + } + + wp_send_json_success(['message' => __('Connections tested successfully for selected accounts.', 'rl-mailwarmer')]); +}); + diff --git a/includes/class-rl-mailwarmer-message-handler.php b/includes/class-rl-mailwarmer-message-handler.php new file mode 100644 index 0000000..8b490dd --- /dev/null +++ b/includes/class-rl-mailwarmer-message-handler.php @@ -0,0 +1,513 @@ +prefix . 'rl_mailwarmer_messages'; + $messages = $wpdb->get_results( + $wpdb->prepare( + "SELECT * FROM $table_name WHERE status = %s AND scheduled_for_timestamp < %s ORDER BY scheduled_for_timestamp ASC LIMIT 1", + 'pending', + current_time('mysql') + ), + ARRAY_A + ); + + if (empty($messages)) { + // log_to_file("process_pending_messages - messages empty"); + return; + } + + foreach ($messages as $message) { + // log_to_file("=========================================================="); + try { + if (!empty($message['first_message']) && $message['first_message']) { + // log_to_file("process_pending_messages - trying send_message"); + $result = self::send_message($message); + } else { + // log_to_file("process_pending_messages - trying reply_message"); + $result = self::reply_message($message); + } + + // Update message status to 'completed' on success + if ($result) { + self::update_message_status($message['id'], 'completed'); + } else { + self::update_message_status($message['id'], 'failed'); + } + } catch (Exception $e) { + // Handle errors gracefully and log them + log_to_file('Error processing message ID ' . $message['id'] . ': ' . $e->getMessage()); + self::update_message_status($message['id'], 'failed'); + } + sleep(3); + } + } + + /** + * Prepare email data and fetch connection info. + * + * @param array $message The message data. + * @return array Prepared email data and connection info. + * @throws Exception If required data is missing. + */ + private static function prepare_email_data($message) { + // log_to_file("prepare_email_data - Running"); + // log_to_file("prepare_email_data - Message: ", $message); + + // Ensure 'from' is valid and fetch connection info + if (empty($message['from_email'])) { + throw new Exception(__('Missing "from" address in the message.', 'rl-mailwarmer')); + } + + // Find the email-account post matching the 'from' address + $from_post_id = self::find_email_account_by_address($message['from_email']); + if (!$from_post_id) { + throw new Exception(__('No matching email account found for "from" address.', 'rl-mailwarmer')); + } + + // Fetch connection details + // log_to_file("prepare_email_data - Getting connection info"); + $mail_password = get_post_meta($from_post_id, 'mail_password', true); + $email_provider_id = get_post_meta($from_post_id, 'email_provider', true); + $connection_info = RL_MailWarmer_Email_Helper::get_provider_defaults($email_provider_id); + $connection_info['username'] = $message['from_email']; + $connection_info['mail_password'] = $mail_password; + // log_to_file("prepare_email_data - Connection Info: ", $connection_info); + + // Override provider defaults with account-specific settings + foreach (['smtp_server', 'smtp_port', 'smtp_password', 'imap_server', 'imap_port', 'imap_password'] as $key) { + $meta_value = get_post_meta($from_post_id, $key, true); + if (!empty($meta_value)) { + $connection_info[$key] = $meta_value; + } + } + + // Handle recipients + // log_to_file("prepare_email_data - Handling recipients"); + $to_emails = is_array($message['to_email']) ? $message['to_email'] : explode(',', $message['to_email']); + $cc_emails = is_array($message['cc']) ? $message['cc'] : explode(',', $message['cc']); + + return [ + 'connection_info' => $connection_info, + 'to' => array_filter(array_map('trim', $to_emails)), + 'cc' => array_filter(array_map('trim', $cc_emails)), + 'subject' => $message['subject'], + 'body' => $message['body'], + 'from' => $message['from_email'], + ]; + } + + /** + * Find the email-account post ID by email address. + * + * @param string $email_address The email address to search for. + * @return int|null The post ID if found, or null. + */ + private static function find_email_account_by_address($email_address) { + // log_to_file("find_email_account_by_address - Searching for: $email_address"); + $query = new WP_Query([ + 'post_type' => 'email-account', + 'post_status' => 'publish', + 'title' => $email_address, + 'fields' => 'ids', + ]); + + if (!empty($query->posts)) { + return $query->posts[0]; + } else { + return new WP_Error('find_email_account_by_address - Unable to find a matching email address'); + } + } + + /** + * Update the status of a message. + * + * @param int $message_id The ID of the message to update. + * @param string $status The new status ('completed', 'failed', etc.). + * @return void + */ + private static function update_message_status($message_id, $status) { + global $wpdb; + + $table_name = $wpdb->prefix . 'rl_mailwarmer_messages'; + + $wpdb->update( + $table_name, + ['status' => $status], + ['id' => $message_id], + ['%s'], + ['%d'] + ); + } + + + + /** + * Send the first message in a conversation. + * + * @param array $message The message details. + * @return bool True if the message is sent successfully, false otherwise. + * @throws Exception If required fields are missing or an error occurs during sending. + */ + public static function send_message($message) { + // log_to_file("send_message - Running"); + // log_to_file("send_message - Message: ", $message); + + // Prepare email data and connection info + $email_data = self::prepare_email_data($message); + + // log_to_file("send_message - Email Data: ", $email_data); + + // Extract connection info + $connection_info = $email_data['connection_info']; + + // Check required fields + if (empty($email_data['to']) || empty($email_data['from']) || empty($email_data['subject']) || empty($email_data['body'])) { + throw new Exception(__('Missing required fields for sending the email.', 'rl-mailwarmer')); + } + + // Create the SMTP transport + // log_to_file("send_message - Creating Transport"); + $transport = new Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport( + $connection_info['smtp_server'], + $connection_info['smtp_port'] + ); + + // Set authentication details + $transport->setUsername($connection_info['username']); + $transport->setPassword($connection_info['mail_password']); + + // Create the mailer + $mailer = new Symfony\Component\Mailer\Mailer($transport); + + // Build the email + // log_to_file("send_message - Building Mail"); + $email = (new Email()) + ->from($email_data['from']) + ->to(...$email_data['to']) + ->subject($email_data['subject']) + ->html($email_data['body']); + + // Add CCs if present + if (!empty($email_data['cc'])) { + $email->cc(...$email_data['cc']); + } + + // Attempt to send the email + // log_to_file("send_message - Trying to send"); + try { + $mailer->send($email); + log_to_file("send_message - Successfully sent SMTP mail from ", $email_data['from']); + return true; // Email sent successfully + } catch (TransportExceptionInterface $e) { + error_log('Error sending email: ' . $e->getMessage()); + return false; // Sending failed + } + } + + + /** + * Reply to an email message. + * + * @param array $message The message details. + * @return bool True if the message is replied to successfully, false otherwise. + * @throws Exception If required fields are missing or an error occurs. + */ + public static function reply_message($message) { + // Prepare email data and connection info + $email_data = self::prepare_email_data($message); + // log_to_file("reply_message - Email Data: ", $email_data); + + // Extract connection info + $connection_info = $email_data['connection_info']; + + // Validate required fields + if (empty($email_data['to']) || empty($email_data['from']) || empty($email_data['subject']) || empty($email_data['body'])) { + throw new Exception(__('Missing required fields for replying to the email.', 'rl-mailwarmer')); + } + + // Attempt to find the original email via IMAP + + + +// // Attempt to find the original email and reply via IMAP +// try { +// $imap = new \PhpImap\Mailbox( +// sprintf('{%s:%d/imap/ssl}', $connection_info['imap_server'], $connection_info['imap_port']), +// $connection_info['imap_username'], +// $connection_info['imap_password'], +// null, +// 'UTF-8' +// ); + +// // Search for the email with the matching subject +// $emails = $imap->searchMailbox('SUBJECT "' . addslashes($email_data['subject']) . '"'); +// if (!empty($emails)) { +// // Fetch the email data +// $original_email = $imap->getMail($emails[0]); + +// // Prepare and send the reply via IMAP +// $imap->reply( +// $emails[0], +// $email_data['body'], +// ['from' => $email_data['from'], 'cc' => $email_data['cc']] +// ); + +// return true; // Reply sent successfully via IMAP +// } + + + + + try { + // log_to_file("reply_message - Trying to reply via IMAP"); + $imap = new \PhpImap\Mailbox( + sprintf('{%s:%d/imap/ssl}', $connection_info['imap_server'], $connection_info['imap_port']), + $connection_info['username'], + $connection_info['mail_password'], + null, + 'UTF-8' + ); + + // Search for the email with the matching subject + // log_to_file("reply_message - Searching for message"); + $emails = $imap->searchMailbox('SUBJECT "' . addslashes($email_data['subject']) . '"'); + if (!empty($emails)) { + + + // Fetch the email data + $original_email = $imap->getMail($emails[0]); + // log_to_file("reply_message - Message found!"); + // log_to_file("reply_message - IMAP Message: ", $original_email); + + // Step 2: Send the reply via SMTP + $transport = new Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport( + $connection_info['smtp_server'], + $connection_info['smtp_port'] + ); + + // Set authentication details + $transport->setUsername($connection_info['username']); + $transport->setPassword($connection_info['mail_password']); + + $mailer = new Mailer($transport); + + $reply_email = (new Email()) + ->from($email_data['from']) + ->to(...$email_data['to']) + ->subject('Re: ' . $original_email->subject) + ->html($email_data['body']); + + // Add headers for threading + $headers = $reply_email->getHeaders(); + $headers->addTextHeader('In-Reply-To', $original_email->messageId); + $headers->addTextHeader('References', trim($original_email->headers->references . ' ' . $original_email->messageId)); + + // ->addHeader('In-Reply-To', $original_email->messageId) + // ->addHeader('References', trim($original_email->headers->references . ' ' . $original_email->messageId)); + + if (!empty($email_data['cc'])) { + $reply_email->cc(...$email_data['cc']); + } + + $mailer->send($reply_email); + + log_to_file("reply_message - Successfully sent IMAP/SMTP reply from ", $email_data['from']); + + // Step 3: Upload the reply to the Sent folder + $imap_stream = imap_open( + sprintf('{%s:%d/imap/ssl}', $connection_info['imap_server'], $connection_info['imap_port']), + $connection_info['username'], + $connection_info['mail_password'] + ); + + $raw_message = $reply_email->toString(); // Convert the Email object to raw MIME format + imap_append($imap_stream, sprintf('{%s}/Sent', $connection_info['imap_server']), $raw_message); + + imap_close($imap_stream); + + // Create the reply headers + // $reply_headers = [ + // 'In-Reply-To' => $original_email->messageId, + // 'References' => trim($original_email->headers->references . ' ' . $original_email->messageId), + // ]; + + // // Construct the reply body + // $reply_body = $email_data['body'] . "\n\n" . + // 'On ' . $original_email->date . ', ' . $original_email->fromName . ' <' . $original_email->fromAddress . '> wrote:' . "\n" . + // $original_email->textPlain; + + // // Send the reply via IMAP + // log_to_file("reply_message - Sending message via IMAP"); + // $imap->addMessageToSentFolder( + // 'To: ' . implode(', ', $email_data['to']) . "\r\n" . + // 'Cc: ' . implode(', ', $email_data['cc']) . "\r\n" . + // 'Subject: Re: ' . $original_email->subject . "\r\n" . + // 'From: ' . $email_data['from'] . "\r\n" . + // 'In-Reply-To: ' . $reply_headers['In-Reply-To'] . "\r\n" . + // 'References: ' . $reply_headers['References'] . "\r\n" . + // "\r\n" . + // $reply_body + // ); + // log_to_file("reply_message - Done message via IMAP"); + + // $mailer = new Mailer($transport); + // $mailer->send($reply); + + return true; // Reply sent successfully + } + } catch (Exception $e) { + log_to_file('IMAP Error: ' . $e->getMessage()); + } + + // Fallback to SMTP if IMAP fails + try { + // log_to_file("reply_message - Falling back to SMTP"); + $smtp_reply = (new Email()) + ->from($email_data['from']) + ->to(...$email_data['to']) + ->subject($email_data['subject']) + ->html($email_data['body']); + + // Add CCs if present + if (!empty($email_data['cc'])) { + $smtp_reply->cc(...$email_data['cc']); + } + + // Create the SMTP transport + $transport = new Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport( + $connection_info['smtp_server'], + $connection_info['smtp_port'] + ); + + // Set authentication details + $transport->setUsername($connection_info['username']); + $transport->setPassword($connection_info['mail_password']); + + // Create the mailer + $mailer = new Symfony\Component\Mailer\Mailer($transport); + $mailer->send($smtp_reply); + log_to_file("reply_message - Sent reply via fallback SMTP from ", $email_data['from']); + // log_to_file('reply_message - SMTP Send Success (?)'); + + return true; // Fallback SMTP reply sent successfully + } catch (Exception $e) { + log_to_file('reply_message - SMTP Error: ' . $e->getMessage()); + return false; // Reply failed + } + } +} + +/** + * Add a metabox to the WP dashboard for monitoring and processing the message queue. + */ +add_action('wp_dashboard_setup', function () { + wp_add_dashboard_widget( + 'rl_mailwarmer_message_queue', + __('Message Queue', 'rl-mailwarmer'), + 'rl_mailwarmer_render_message_queue_widget' + ); +}); + +/** + * Render the Message Queue dashboard widget. + */ +function rl_mailwarmer_render_message_queue_widget() { + global $wpdb; + + // Count past-due messages + $table_name = $wpdb->prefix . 'rl_mailwarmer_messages'; + $past_due_count = $wpdb->get_var( + $wpdb->prepare( + "SELECT COUNT(*) FROM $table_name WHERE status = %s AND scheduled_for_timestamp < %s", + 'pending', + current_time('mysql') + ) + ); + + ?> +
+

+ +
+
+ + + __('Invalid nonce.', 'rl-mailwarmer')]); + } + + // Ensure the user has permission + if (!current_user_can('manage_options')) { + wp_send_json_error(['message' => __('Permission denied.', 'rl-mailwarmer')]); + } + + try { + // Process pending messages + // log_to_file("wp_ajax_rl_mailwarmer_process_message_queue - Trying process_pending_messages()"); + $processed_count = RL_MailWarmer_Message_Handler::process_pending_messages(); + + wp_send_json_success(['processed_count' => $processed_count]); + } catch (Exception $e) { + wp_send_json_error(['message' => $e->getMessage()]); + } +}); diff --git a/includes/class-rl-mailwarmer-message-helper.php b/includes/class-rl-mailwarmer-message-helper.php new file mode 100644 index 0000000..a040898 --- /dev/null +++ b/includes/class-rl-mailwarmer-message-helper.php @@ -0,0 +1,155 @@ +prefix . 'rl_mailwarmer_conversations'; + $message_table = $wpdb->prefix . 'rl_mailwarmer_messages'; + + // Fetch conversations with their messages + $conversations = $wpdb->get_results(" + SELECT c.id as conversation_id, c.campaign_id, c.created_at, m.id as message_id, m.scheduled_for_timestamp, m.from_email, m.to_email, m.subject + FROM $conversation_table c + LEFT JOIN $message_table m ON c.id = m.conversation_id + WHERE m.status != 'scheduled' + ORDER BY c.created_at DESC, m.scheduled_for_timestamp ASC + "); + + ?> +
+

+ + +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ message_id) : ?> + + + conversation_id); ?>campaign_id); ?>created_at); ?>message_id); ?>scheduled_for_timestamp); ?>from_email); ?>to_email); ?>subject); ?>
+

+ +

+
+ +
+ + + __('Permission denied.', 'rl-mailwarmer')]); + } + + $message_ids = isset($_POST['message_ids']) ? array_map('intval', $_POST['message_ids']) : []; + + if (empty($message_ids)) { + wp_send_json_error(['message' => __('No messages selected.', 'rl-mailwarmer')]); + } + + $table_name = $wpdb->prefix . 'rl_mailwarmer_messages'; + + // Delete selected messages + $placeholders = implode(',', array_fill(0, count($message_ids), '%d')); + $query = "DELETE FROM $table_name WHERE id IN ($placeholders)"; + $result = $wpdb->query($wpdb->prepare($query, $message_ids)); + + if ($result === false) { + wp_send_json_error(['message' => __('Failed to delete messages.', 'rl-mailwarmer')]); + } + + wp_send_json_success(['message' => __('Messages deleted successfully.', 'rl-mailwarmer')]); +}); diff --git a/includes/class-rl-mailwarmer-post-tables.php b/includes/class-rl-mailwarmer-post-tables.php new file mode 100644 index 0000000..8c688c9 --- /dev/null +++ b/includes/class-rl-mailwarmer-post-tables.php @@ -0,0 +1,143 @@ +post_type = $post_type; + $this->items_per_page = isset($_GET['per_page']) ? intval($_GET['per_page']) : 10; + $this->paged = (get_query_var('paged')) ? get_query_var('paged') : 1; + + // Base query args + $base_args = [ + 'orderby' => 'title', + 'order' => 'ASC', + 'post_type' => $this->post_type, + 'posts_per_page' => $this->items_per_page, + 'paged' => $this->paged, + ]; + + // Add owner_id filter only for non-administrators + if (!current_user_can('administrator')) { + $base_args['meta_query'] = array( + array( + 'key' => 'owner_id', + 'value' => get_current_user_id(), + 'compare' => '=' + ) + ); + } + + // Merge with any additional query args + $this->additional_query_args = array_merge($base_args, $additional_query_args); + + // Default labels that can be overridden + $this->labels = array_merge([ + 'title' => ucfirst($post_type) . 's', + 'no_items' => 'No ' . strtolower($post_type) . 's found.', + 'delete_confirm' => 'Are you sure you want to delete this ' . strtolower($post_type) . '?' + ], $labels); + } + + public function render(): void { + $query = new WP_Query($this->additional_query_args); + $edit_url_base = "/dashboard/" . $this->post_type . "s/edit-" . $this->post_type . "?edit="; + ?> +
+ + + + + +
+ have_posts()) : ?> + have_posts()) : $query->the_post(); + $post_id = get_the_ID(); + ?> +
+
+ + + +
+ + Edit | + + + get_delete_link($post_id); ?> + +
+
+
+ + +

labels['no_items']); ?>

+ +
+ + + +
+ + + + 'delete', + 'post' => $post_id, + 'redirect_to' => urlencode($current_url) + ), + $current_url // Use current page URL instead of admin URL + ), + 'delete-post_' . $post_id + ); + + return sprintf( + 'Delete', + esc_url($delete_url) + ); + } +} \ No newline at end of file diff --git a/includes/rl-mailwarmer-importer.php b/includes/rl-mailwarmer-importer.php new file mode 100644 index 0000000..04325c1 --- /dev/null +++ b/includes/rl-mailwarmer-importer.php @@ -0,0 +1,185 @@ + +
+

+
+ + +

+

+ + +

+

+ + +

+
+
+

' . __('Failed to open the domain CSV file.', 'rl-mailwarmer') . '

'; + return; + } + + // Read the headers + $headers = fgetcsv($handle); + + while (($row = fgetcsv($handle)) !== false) { + $data = array_combine($headers, $row); + + if (empty($data['name'])) { + continue; // Skip invalid rows + } + + // Create or update the domain post + $existing_domain = get_posts([ + 'post_type' => 'domain', + 'title' => $data['name'], + 'posts_per_page' => 1, + 'fields' => 'ids', + ]); + + $post_id = $existing_domain[0] ?? null; + + $post_data = [ + 'post_title' => $data['name'], + 'post_type' => 'domain', + 'post_status' => 'publish', + ]; + + if ($post_id) { + $post_data['ID'] = $post_id; + } + + $post_id = wp_insert_post($post_data); + + if (!is_wp_error($post_id)) { + update_post_meta($post_id, 'cloudflare_api_email', sanitize_text_field($data['cloudflare_api_email'])); + update_post_meta($post_id, 'cloudflare_api_key', sanitize_text_field($data['cloudflare_api_key'])); + log_to_file("rl_mailwarmer_process_domain_csv - Imported domain: " . $data['name']); + } else { + + log_to_file("rl_mailwarmer_process_domain_csv - ERROR importing domain: " . $data['name']); + } + } + + fclose($handle); + echo '

' . __('Domains imported successfully.', 'rl-mailwarmer') . '

'; +} + +function rl_mailwarmer_process_email_account_csv($file_path) { + $handle = fopen($file_path, 'r'); + if (!$handle) { + echo '

' . __('Failed to open the email account CSV file.', 'rl-mailwarmer') . '

'; + return; + } + + // Read the headers + $headers = fgetcsv($handle); + + while (($row = fgetcsv($handle)) !== false) { + $data = array_combine($headers, $row); + + if (empty($data['Email Address']) || empty($data['Password'])) { + continue; // Skip invalid rows + } + + if (!empty($data['Name'])) { + $name = sanitize_text_field($data['Name']); + } + + if (!empty($data['Signature'])) { + $signature = sanitize_textarea_field(wp_slash($data['Signature'])); + // log_to_file("rl_mailwarmer_process_email_account_csv - Signature: {$signature}"); + } + + // Match the email account to a domain + $domain_name = strtolower(substr(strrchr($data['Email Address'], "@"), 1)); + $domain = get_posts([ + 'post_type' => 'domain', + 'title' => $domain_name, + 'post_status' => 'publish', + 'posts_per_page' => 1, + 'fields' => 'ids', + ]); + + // Match the email provider by name + $email_provider = $data['Email Provider']; + + // Check if this is a scrubber account + if (!empty($data['Is Scrubber'])) { + $include_in_scrubber_pool = true; + } else { + $include_in_scrubber_pool = false; + } + + // Create or update the email account post + $post_data = [ + 'post_title' => strtolower($data['Email Address']), + 'post_type' => 'email-account', + 'post_status' => 'publish', + 'meta_input' => [ + 'full_name' => $name, + 'mail_password' => sanitize_text_field($data['Password']), + 'email_provider' => $email_provider, + 'email_signature' => $signature, + 'include_in_scrubber_pool' => $include_in_scrubber_pool, + ], + ]; + + if (!empty($domain)) { + $post_data['meta_input']['domain'] = $domain[0]; + } + + $post_id = wp_insert_post($post_data); + + if (!is_wp_error($post_id)) { + // $email_account_connection_status = RL_MailWarmer_Email_Helper::check_mail_login($post_id); + log_to_file("rl_mailwarmer_process_email_account_csv - Imported email {$data['Email Address']}: "); + } else { + log_to_file("rl_mailwarmer_process_email_account_csv - ERROR importing email: " . $data['Email Address']); + } + } + + fclose($handle); + echo '

' . __('Email accounts imported successfully.', 'rl-mailwarmer') . '

'; +} diff --git a/js/admin-update-mx.js b/js/admin-update-mx.js new file mode 100644 index 0000000..a7c41fd --- /dev/null +++ b/js/admin-update-mx.js @@ -0,0 +1,38 @@ +jQuery(document).ready(function ($) { + $('#update-mx-record-button').on('click', function (e) { + e.preventDefault(); + + // const postId = $('#post_ID').val(); + const postId = rlMailWarmerMx.post_id; + const action = $('#mx_action').val(); + const content = $('#mx_content').val(); + const priority = $('#mx_priority').val(); + const ttl = $('#mx_ttl').val(); + + $('#mx-update-result').html('

Updating MX record...

'); + + $.ajax({ + url: rlMailWarmerMx.ajax_url, + method: 'POST', + data: { + action: 'rl_mailwarmer_update_mx_record', + post_id: postId, + action_type: action, + content: content, + priority: priority, + ttl: ttl, + security: rlMailWarmerMx.nonce, + }, + success: function (response) { + if (response.success) { + $('#mx-update-result').html('

' + response.data + '

'); + } else { + $('#mx-update-result').html('

Error: ' + response.data + '

'); + } + }, + error: function (xhr, status, error) { + $('#mx-update-result').html('

AJAX Error: ' + error + '

'); + }, + }); + }); +}); diff --git a/js/generate-conversation.js b/js/generate-conversation.js new file mode 100644 index 0000000..78e60b2 --- /dev/null +++ b/js/generate-conversation.js @@ -0,0 +1,36 @@ +jQuery(document).ready(function ($) { + $('#generate-conversation').on('click', function () { + const initiatedBy = $('#initiated_by').val(); + const subject = $('#subject').val(); + const length = $('#length').val(); + + $.ajax({ + url: rlGenerateConversation.ajax_url, + type: 'POST', + data: { + action: 'rl_generate_conversation', + nonce: rlGenerateConversation.nonce, + post_id: rlGenerateConversation.post_id, + initiated_by: initiatedBy, + subject: subject, + length: length, + }, + beforeSend: function () { + $('#generate-conversation').text('Generating...').prop('disabled', true); + }, + success: function (response) { + $('#generate-conversation').text('Generate Conversation').prop('disabled', false); + + if (response.success) { + console.log('Conversation generated successfully! ID: ' + response.data.conversation_id); + } else { + alert('Error: ' + response.data.message); + } + }, + error: function () { + $('#generate-conversation').text('Generate Conversation').prop('disabled', false); + alert('Failed to generate conversation. Please try again.'); + }, + }); + }); +}); diff --git a/js/public-check-domain-health.js b/js/public-check-domain-health.js new file mode 100644 index 0000000..dc765d7 --- /dev/null +++ b/js/public-check-domain-health.js @@ -0,0 +1,35 @@ +jQuery(document).ready(function ($) { + // console.log("Loaded check-domain-health.js"); + $('#check-domain-health-button').on('click', function (e) { + e.preventDefault(); + + //var postId = $('#post_ID').val(); // Get the current post ID + var postData = { + action: 'rl_mailwarmer_check_domain_health', + post_id: rlMailWarmer_public.post_id, + security: rlMailWarmer_public.nonce, + }; + // console.log("AJAX URL: " + rlMailWarmer_public.ajax_url); + // console.log("Post Action: " + postData.action); + // console.log("Post postId: " + postData.post_id); + // console.log("Post security: " + postData.security); + + $('#domain-health-result').html('

Checking domain health...

'); + + $.ajax({ + url: rlMailWarmer_public.ajax_url, + type: 'POST', + data: postData, + success: function (response) { + if (response.success) { + $('#domain-health-result').html('

Report saved successfully. Post ID: ' + response.data + '

'); + } else { + $('#domain-health-result').html('

Error: ' + response.data + '

'); + } + }, + error: function (xhr, status, error) { + $('#domain-health-result').html('

AJAX Error: ' + error + '

'); + }, + }); + }); +}); diff --git a/js/rl-generate-accounts.js b/js/rl-generate-accounts.js new file mode 100644 index 0000000..8ae26c2 --- /dev/null +++ b/js/rl-generate-accounts.js @@ -0,0 +1,35 @@ +jQuery(document).ready(function ($) { + $('#rl_generate_accounts_button').on('click', function () { + const postId = $('#post_ID').val(); + const quantity = $('#rl_generate_account_qty').val(); + + $('#rl_generate_accounts_result').html('

Generating accounts...

'); + + $.ajax({ + url: rlGenerateAccounts.ajax_url, + method: 'POST', + data: { + action: 'rl_generate_random_accounts', + post_id: postId, + qty: quantity, + security: rlGenerateAccounts.nonce, + }, + success: function (response) { + if (response.success) { + const results = response.data; + let resultHtml = '

Generated Accounts:

'; + $('#rl_generate_accounts_result').html(resultHtml); + } else { + $('#rl_generate_accounts_result').html(`

Error: ${response.data}

`); + } + }, + error: function () { + $('#rl_generate_accounts_result').html('

Failed to generate accounts.

'); + }, + }); + }); +}); diff --git a/js/rl-process-conversations.js b/js/rl-process-conversations.js new file mode 100644 index 0000000..5ade6c9 --- /dev/null +++ b/js/rl-process-conversations.js @@ -0,0 +1,27 @@ +jQuery(document).ready(function ($) { + $('#rl_process_conversations_button').on('click', function () { + const postId = $('#post_ID').val(); + + $('#rl_process_conversations_result').html('

Processing conversations...

'); + + $.ajax({ + url: rlProcessConversations.ajax_url, + method: 'POST', + data: { + action: 'rl_process_upcoming_conversations', + post_id: postId, + security: rlProcessConversations.nonce, + }, + success: function (response) { + if (response.success) { + $('#rl_process_conversations_result').html(`

${response.data}

`); + } else { + $('#rl_process_conversations_result').html(`

Error: ${response.data}

`); + } + }, + error: function () { + $('#rl_process_conversations_result').html('

Failed to process conversations.

'); + }, + }); + }); +}); \ No newline at end of file diff --git a/templates/domain-list.php b/templates/domain-list.php new file mode 100644 index 0000000..ad19c8c --- /dev/null +++ b/templates/domain-list.php @@ -0,0 +1,14 @@ + 'My Domains', +// 'no_items' => 'You haven\'t added any domains yet.', +// 'delete_confirm' => 'Are you sure you want to delete this domain? This action cannot be undone.' +// ]); +// $list->render(); + +// get_footer(); \ No newline at end of file