(Grav GitSync) Automatic Commit from smokephil
This commit is contained in:
commit
4267db646d
2765 changed files with 462171 additions and 0 deletions
400
plugins/email/CHANGELOG.md
Normal file
400
plugins/email/CHANGELOG.md
Normal file
|
|
@ -0,0 +1,400 @@
|
|||
# v4.0.4
|
||||
## 07/10/2023
|
||||
|
||||
1. [](#bugfix)
|
||||
* Fix for email file attachments using stream
|
||||
|
||||
# v4.0.3
|
||||
## 06/29/2023
|
||||
|
||||
1. [](#improved)
|
||||
* Simplified the `Email::processRecipients()` logic for readability
|
||||
1. [](#bugfix)
|
||||
* Fix an issue with 2 email addresses provided with 'just' email and no name [#176](https://github.com/getgrav/grav-plugin-email/issues/176)
|
||||
* Fix for blank subjectlines when using `Message::setSubject()` in Twig templates [getgrav/grav-plugin-login#299](https://github.com/getgrav/grav-plugin-login/issues/299)
|
||||
|
||||
# v4.0.2
|
||||
## 06/27/2023
|
||||
|
||||
1. [](#bugfix)
|
||||
* some recipient handling improvements. e.g. missing `bcc_name` throwing error
|
||||
* Allow overriding of defaults with a form configuration. Use `null` to remove default email configuration
|
||||
|
||||
# v4.0.1
|
||||
## 05/20/2023
|
||||
|
||||
1. [](#bugfix)
|
||||
* Fixed a bug with Email 4.0 ignoring `template:` parameter when dealing with multiple body sections
|
||||
|
||||
# v4.0.0
|
||||
## 05/09/2023
|
||||
|
||||
1. [](#new)
|
||||
* Migrated from `Swiftmailer` (which has been deprecated) to `Symfony/Mailer`, a more modern and extensible mailing library.
|
||||
* Built-in support for `SMTP`, `SMTPS`, `Sendmail` and `Native` (uses best solution per-platform) engines
|
||||
* Added ability to have 3rd party plugins that provide new engines (e.g. `SendGrid`, `MailJet`, etc.) with `SMTP`, `API` or `HTTPS` transports for faster and more reliable email sending
|
||||
* More flexible options for email formatting including RFC822 `name-addr` format (`Joe Bloggs <user@example.com>`) and simple arrays (`[user@example.com, Joe Bloggs]`)
|
||||
* Support `tags` and `metadata` in `Email::buildMessage()`
|
||||
* Two new events `onEmailEngines` and `onEmailTransportDsn` to facilitate the integration of custom engines via plugins
|
||||
|
||||
# v4.0.0-rc.4
|
||||
## 02/27/2023
|
||||
|
||||
1. [](#bugfix)
|
||||
* Fixed for multiple recipients [#167](https://github.com/getgrav/grav-plugin-email/issues/167)
|
||||
* Fix for simple array format with names which wasn't working
|
||||
|
||||
# v4.0.0-rc.3
|
||||
## 10/27/2022
|
||||
|
||||
1. [](#bugfix)
|
||||
* Fixed an issue with array based email address
|
||||
|
||||
# v4.0.0-rc.2
|
||||
## 10/19/2022
|
||||
|
||||
1. [](#improved)
|
||||
* Set `sendmail` as default engine to provide better fallback for unsupported `mailer` option
|
||||
* Added info about available engine plugins in `README.md`
|
||||
|
||||
# v4.0.0-rc.1
|
||||
## 10/05/2022
|
||||
|
||||
1. [](#new)
|
||||
* Migrated from `Swiftmailer` (which has been deprecated) to `Symfony/Mailer`, a more modern and extensible mailing library.
|
||||
* Built-in support for `SMTP`, `SMTPS`, `Sendmail` and `Native` (uses best solution per-platform) engines
|
||||
* Added ability to have 3rd party plugins that provide new engines (e.g. `SendGrid`, `MailJet`, etc.) with `SMTP`, `API` or `HTTPS` transports for faster and more reliable email sending
|
||||
* More flexible options for email formatting including RFC822 `name-addr` format (`Joe Bloggs <user@example.com>`) and simple arrays (`[user@example.com, Joe Bloggs]`)
|
||||
* Support `tags` and `metadata` in `Email::buildMessage()`
|
||||
* Two new events `onEmailEngines` and `onEmailTransportDsn` to facilitate the integration of custom engines via plugins
|
||||
|
||||
# v3.2.0
|
||||
## 03/28/2022
|
||||
|
||||
1. [](#new)
|
||||
* Require **Grav 1.7.32** and **Form 6.0.0**
|
||||
1. [](#improved)
|
||||
* Added access email message object inside the twig template file
|
||||
2. [](#bugfix)
|
||||
* Fixed twig not being initialized when building an email message
|
||||
|
||||
# v3.1.5
|
||||
## 01/03/2022
|
||||
|
||||
1. [](#improved)
|
||||
* Updated to Swiftmailer `v6.3.0` with PHP 8.1 compatibility [#157](https://github.com/getgrav/grav-plugin-email/issues/157)
|
||||
|
||||
# v3.1.4
|
||||
## 11/16/2021
|
||||
|
||||
1. [](#improved)
|
||||
* Added second parameter to `Email::send()` to get failed recipients
|
||||
|
||||
# v3.1.3
|
||||
## 07/19/2021
|
||||
|
||||
1. [](#improved)
|
||||
* Pass page variable to processed forms [#141](https://github.com/getgrav/grav-plugin-email/pull/141)
|
||||
* Email configuration available to templates [#152](https://github.com/getgrav/grav-plugin-email/pull/152)
|
||||
* New Event after eMail was sent [#151](https://github.com/getgrav/grav-plugin-email/pull/151)
|
||||
|
||||
# v3.1.2
|
||||
## 04/06/2021
|
||||
|
||||
1. [](#new)
|
||||
* Added new `onEmailMessage` event to make object available for editing [#150](https://github.com/getgrav/grav-plugin-email/pull/150)
|
||||
|
||||
# v3.1.1
|
||||
## 01/31/2021
|
||||
|
||||
1. [](#improved)
|
||||
* Latest vendor updates including SwiftMailer `6.2.5`
|
||||
* Updated CLI commands
|
||||
* Minor code cleanup
|
||||
|
||||
# v3.1.0
|
||||
## 12/02/2020
|
||||
|
||||
1. [](#improved)
|
||||
* Added support for `auth_mode` in SMTP engine [#101](https://github.com/getgrav/grav-plugin-email/pull/101)
|
||||
* Obfuscate the password shown in the CLI `test-email` command [#140](https://github.com/getgrav/grav-plugin-email/pull/140)
|
||||
|
||||
# v3.0.10
|
||||
## 11/09/2020
|
||||
|
||||
1. [](#improved)
|
||||
* Tweaked default `base.html.twig` template to better support dark-mode clients
|
||||
* Latest vendor updates
|
||||
1. [](#bugfix)
|
||||
* Add missing support for `template:` in body array
|
||||
* Added check to process markdown with `text/html` content type only
|
||||
|
||||
# v3.0.9
|
||||
## 06/08/2020
|
||||
|
||||
1. [](#improved)
|
||||
* Disable password autocomplete in password field
|
||||
* Don't save empty string in password field [#134](https://github.com/getgrav/grav-plugin-email/issues/134)
|
||||
|
||||
# v3.0.8
|
||||
## 04/27/2020
|
||||
|
||||
1. [](#improved)
|
||||
* Updated vendor library files
|
||||
* Use Grav's Parsedown class
|
||||
|
||||
# v3.0.7
|
||||
## 03/05/2020
|
||||
|
||||
1. [](#improved)
|
||||
* Updated email validator library
|
||||
1. [](#bugfix)
|
||||
* Fixed `Invalid resource theme://` on CLI command `test-email` on Grav 1.6.21 and later versions [#128](https://github.com/getgrav/grav-plugin-email/issues/128)
|
||||
|
||||
# v3.0.6
|
||||
## 02/11/2020
|
||||
|
||||
1. [](#improved)
|
||||
* Updated email validator library
|
||||
|
||||
# v3.0.5
|
||||
## 02/03/2020
|
||||
|
||||
1. [](#bugfix)
|
||||
* Fixed a date in changelog (no other changes)
|
||||
|
||||
# v3.0.4
|
||||
## 01/17/2020
|
||||
|
||||
1. [](#improved)
|
||||
* Added ZOHO configuration example
|
||||
* Updated SwiftMailer library for PHP 7.4 support
|
||||
|
||||
# v3.0.3
|
||||
## 08/16/2019
|
||||
|
||||
1. [](#new)
|
||||
* Support an array of multiple emails in `email:` form process
|
||||
* Allow form values in email templates
|
||||
1. [](#improved)
|
||||
* Added Twig blocks for `content` and `footer` in `email/base.html.twig` template
|
||||
* Updated `README.md` to reflect working setup for GMail
|
||||
|
||||
# v3.0.2
|
||||
## 05/09/2019
|
||||
|
||||
1. [](#new)
|
||||
* Requires Form Plugin v3.0.3
|
||||
* Added Russian translation [#113](https://github.com/getgrav/grav-plugin-email/pull/113)
|
||||
1. [](#bugfix)
|
||||
* Better fix for missing attachments when sending an email using a form [form#333](https://github.com/getgrav/grav-plugin-form/issues/333)
|
||||
|
||||
# v3.0.1
|
||||
## 04/15/2019
|
||||
|
||||
1. [](#improved)
|
||||
* Put a `try/catch` around email attachments and log any errors rather than hard fail
|
||||
1. [](#bugfix)
|
||||
* Fixed missing attachments when sending an email using a form [form#333](https://github.com/getgrav/grav-plugin-form/issues/333)
|
||||
|
||||
# v3.0.0
|
||||
## 04/11/2019
|
||||
|
||||
1. [](#new)
|
||||
* Added new `template:` to choose twig template option for email form processing
|
||||
* Moved `buildMessage()` and `parseAddressValue()` to Email object and made public
|
||||
* Refactored the `EmailUtils::sendEmail()` to take an array of params or the old param list
|
||||
* Switched to SwiftMailer v.6.1.3 (requires PHP7/Grav 1.6)
|
||||
* SwiftMailer 6.x compatibility fixes
|
||||
* Updated various translations
|
||||
* Added support for Email Queue with Scheduler support
|
||||
* Code cleanup, composer update
|
||||
* Added a new `clear-queue-failures` CLI command to flush out failed sends
|
||||
1. [](#improved)
|
||||
* Added backlink for scheduler task
|
||||
* Added support for `environment` option to `flushqueue` CLI command
|
||||
* Fixed mailtrap hostname in README.md
|
||||
* Disable autocomplete on SMTP `user` and `password` fields
|
||||
|
||||
# v2.7.2
|
||||
## 01/25/2019
|
||||
|
||||
1. [](#improved)
|
||||
* Added default for `to` address
|
||||
* Updated EN language [#99](https://github.com/getgrav/grav-plugin-email/pull/99)
|
||||
* Updated UK language [#98](https://github.com/getgrav/grav-plugin-email/pull/98)
|
||||
* Updated RU language [#100](https://github.com/getgrav/grav-plugin-email/pull/100)
|
||||
* Updated to SwiftMailer v5.4.12
|
||||
1. [](#bugfix)
|
||||
* Fixed `mailtrap` hostname
|
||||
|
||||
# v2.7.1
|
||||
## 12/05/2017
|
||||
|
||||
1. [](#new)
|
||||
* Added new `onEmailSend()` event hook before sending [#70](https://github.com/getgrav/grav-plugin-email/pull/70)
|
||||
1. [](#improved)
|
||||
* Added examples of setting up Email plugin with various SMTP providers
|
||||
* Updated RU language [#60](https://github.com/getgrav/grav-plugin-email/pull/60)
|
||||
* Updated to SwiftMailer v5.4.8
|
||||
|
||||
# v2.7.0
|
||||
## 10/26/2017
|
||||
|
||||
1. [](#improved)
|
||||
* Now uses a dedicated `logs/email.log` file when `debug: true`
|
||||
* Improved the README.txt file with examples, and troubleshooting
|
||||
* Changed default engine to `sendmail` as `mail` is deprecated and not functioning [swiftmailer#866](https://github.com/swiftmailer/swiftmailer/issues/866}
|
||||
|
||||
# v2.6.2
|
||||
## 09/30/2017
|
||||
|
||||
1. [](#improved)
|
||||
* Removed extraneous files from vendor folder
|
||||
|
||||
# v2.6.1
|
||||
## 09/07/2017
|
||||
|
||||
1. [](#improved)
|
||||
* Improved the error message when missing `from` in the configuration
|
||||
* Silently catch malformed email exceptions
|
||||
|
||||
# v2.6.0
|
||||
## 05/22/2017
|
||||
|
||||
1. [](#improved)
|
||||
* Inherit options from plugin configuration [#39](https://github.com/getgrav/grav-plugin-email/pull/39)
|
||||
1. [](#bugfix)
|
||||
* Also process translation on the email subject [https://github.com/getgrav/grav-plugin-comments/issues/38](https://github.com/getgrav/grav-plugin-comments/issues/38)
|
||||
|
||||
# v2.5.3
|
||||
## 01/03/2017
|
||||
|
||||
1. [](#improved)
|
||||
* Updated to SwiftMailer 5.4.5 [#45](https://github.com/getgrav/grav-plugin-email/issues/45)
|
||||
|
||||
# v2.5.2
|
||||
## 12/13/2016
|
||||
|
||||
1. [](#new)
|
||||
* RC released as stable
|
||||
|
||||
# v2.5.2-rc.1
|
||||
## 11/26/2016
|
||||
|
||||
1. [](#new)
|
||||
* Added a new `process_markdown` option for emails in forms
|
||||
1. [](#improved)
|
||||
* Improved the `Utils::sendEmail()` method to take the email type as an option
|
||||
|
||||
# v2.5.1
|
||||
## 10/19/2016
|
||||
|
||||
1. [](#improved)
|
||||
* CLI command will fallback to use the `to` from email plugin config if not provided
|
||||
* Explicit Composer based class loader to fix issues with class case
|
||||
|
||||
# v2.5.0
|
||||
## 09/07/2016
|
||||
|
||||
1. [](#new)
|
||||
* Added a new `bin/plugin email test-email` CLI command
|
||||
1. [](#improved)
|
||||
* Moved Email `Utils` class from Login to Email plugin
|
||||
* Provide a sample base `email/base.html.twig` template for emails
|
||||
1. [](#bugfix)
|
||||
* Fix handling attachments with the updated file upload field
|
||||
|
||||
# v2.4.3
|
||||
## 08/16/2016
|
||||
|
||||
1. [](#improved)
|
||||
* Added Russian translation
|
||||
* Updated Swiftmailer to 5.4.3 [#37](https://github.com/getgrav/grav-plugin-email/issues/37)
|
||||
|
||||
# v2.4.2
|
||||
## 08/10/2016
|
||||
|
||||
1. [](#improved)
|
||||
* Added Croatian translation
|
||||
|
||||
# v2.4.1
|
||||
## 07/14/2016
|
||||
|
||||
1. [](#improved)
|
||||
* Allow multiple email recipients (comma separated) [#31](https://github.com/getgrav/grav-plugin-email/issues/31)
|
||||
* Added Danish and Spanish translations
|
||||
|
||||
# v2.4.0
|
||||
## 05/11/2016
|
||||
|
||||
1. [](#improved)
|
||||
* Now includes Swiftmailer v5.4.2 which introduces a number of bug fixes and improvements
|
||||
1. [](#bugfix)
|
||||
* Correct `starttls` implementation, bundled in TLS
|
||||
|
||||
# v2.3.0
|
||||
## 04/20/2016
|
||||
|
||||
1. [](#improved)
|
||||
* Added debug option to enable logging on SwiftMailer.
|
||||
* Updated SwiftMailer from v5.1.0 to v5.4.1.
|
||||
* Added an option in the Admin settings to enable `starttls`
|
||||
1. [](#bugfix)
|
||||
* Correctly name TLS in the Admin settings, the label was `TTS` (but the value was correctly named `tls`)
|
||||
|
||||
# v2.2.0
|
||||
## 02/05/2016
|
||||
|
||||
1. [](#new)
|
||||
* Allow to send attachments in forms
|
||||
* Added French translation
|
||||
1. [](#improved)
|
||||
* Throw an exception when trying to send emails without a `from` or `to` parameters setup, to intercept less meaningful errors and provide a better description on how to fix the problem
|
||||
* Changed SMTP password in admin to use a password field instead of plain text
|
||||
|
||||
# v2.1.0
|
||||
## 12/18/2015
|
||||
|
||||
1. [](#new)
|
||||
* Added missing `content_type` to email.yaml
|
||||
* Added default values for CC and BCC
|
||||
1. [](#improved)
|
||||
* Improved documentation of new email params in `README.md`
|
||||
* Moved config setting of `mailer.default` to `mailer.engine`
|
||||
|
||||
# v2.0.0
|
||||
## 12/11/2015
|
||||
|
||||
1. [](#new)
|
||||
* Added support for from/sender name (Thomas Keitel)
|
||||
* Added support for message content type (Thomas Keitel)
|
||||
* Added support for reply addresses (Thomas Keitel)
|
||||
* Added support for CC/BCC (Thomas Keitel)
|
||||
* Added support for multiple body parts (Thomas Keitel)
|
||||
1. [](#bugfix)
|
||||
* Fix email engine selection (z38)
|
||||
|
||||
# v1.0.0
|
||||
## 11/20/2015
|
||||
|
||||
1. [](#bugfix)
|
||||
* Fix for issue with no body parameter specified
|
||||
|
||||
# v0.2.1
|
||||
## 09/11/2015
|
||||
|
||||
1. [](#bugfix)
|
||||
* Fix onFormProcessed event
|
||||
|
||||
# v0.2.0
|
||||
## 08/11/2015
|
||||
|
||||
1. [](#improved)
|
||||
* Disable `enable` in admin
|
||||
|
||||
# v0.1.0
|
||||
## 08/04/2015
|
||||
|
||||
1. [](#new)
|
||||
* ChangeLog started...
|
||||
21
plugins/email/LICENSE
Normal file
21
plugins/email/LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Grav
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
502
plugins/email/README.md
Normal file
502
plugins/email/README.md
Normal file
|
|
@ -0,0 +1,502 @@
|
|||
# Grav Email Plugin
|
||||
|
||||
The **email plugin** for [Grav](http://github.com/getgrav/grav) adds the ability to send email utilizing the `symfony/mailer` package. This is particularly useful for the **admin** and **login** plugins.
|
||||
|
||||
> IMPORTANT: **Version 4.0** replaced the old deprecated **SwiftMailer** library with **Symfony/Mailer** package. This is a modern and well supported library that also has the capability to support 3rd party transport engines such as `SendGrid`, `MailJet`, `MailGun`, `MailChimp`, etc. This library should be backwards compatible with existing configurations, but please create an issue if you run into any problems.
|
||||
|
||||
# Installation
|
||||
|
||||
The email plugin is easy to install with GPM.
|
||||
|
||||
```
|
||||
$ bin/gpm install email
|
||||
```
|
||||
|
||||
# Configuration
|
||||
|
||||
The plugin uses `sendmail` binary as the default mail engine.
|
||||
|
||||
```
|
||||
enabled: true
|
||||
from:
|
||||
to:
|
||||
mailer:
|
||||
engine: sendmail
|
||||
smtp:
|
||||
server: localhost
|
||||
port: 25
|
||||
encryption: none
|
||||
user:
|
||||
password:
|
||||
sendmail:
|
||||
bin: '/usr/sbin/sendmail -bs'
|
||||
content_type: text/html
|
||||
debug: false
|
||||
```
|
||||
|
||||
You can configure the Email plugin by using the Admin plugin, navigating to the Plugins list and choosing `Email`.
|
||||
|
||||
That's the easiest route. Or you can also alter the Plugin configuration by copying the `user/plugins/email/email.yaml` file into `user/config/plugins/email.yaml` and make your modifications there.
|
||||
|
||||
The first setting you'd likely change is your `Email from` / `Email to` names and emails.
|
||||
|
||||
Also, you'd likely want to setup a SMTP server instead of using PHP Mail, as the latter is not 100% reliable and you might experience problems with emails.
|
||||
|
||||
## Built-in Engines
|
||||
|
||||
By default Email 4.0 supports 4 native engines:
|
||||
|
||||
* SMTP - Standard "Simple Mail Transport Protocol" - The default for most providers
|
||||
* SMTPS - "Simple Mail Transport Protocol Secure" - Not very commonly used
|
||||
* Sendmail - Uses the built-in `sendmail` binary file available on many Linux and Mac systems
|
||||
* Native - Uses `sendmail_path` of `php.ini` for Mac + Linux, and `smtp` and `smtp_port` on Windows
|
||||
|
||||
Due to the modular nature of Symfony/Mailer, 3rd party engines are supported via Grav plugins.
|
||||
|
||||
## 3rd-Party Engines Plugin Support
|
||||
|
||||
Along with the **Email** `v4.0` release, there has also been several custom provider plugins released to provide support for `SMTP`, `API`, and sometimes even `HTTPS` support for 3rd party providers such as **Sendgrid**, **MailJet**, **MailGun**, **Amazon SES**, **Mailchimp/Mandrill**, and others! `API` or `HTTPS` will provide a faster email sending experience compared to `SMTP` which is an older protocol and requires more back-and-forth negotiation and communication compared to the single-request of `API` or `HTTPS` solutions.
|
||||
|
||||
Examples of the currently available plugins include:
|
||||
|
||||
* https://github.com/getgrav/grav-plugin-email-sendgrid - Sengrid Mailer
|
||||
* https://github.com/getgrav/grav-plugin-email-amazon - Amazon SES
|
||||
* https://github.com/getgrav/grav-plugin-email-mandrill - Mailchimp Mandrill Mailer
|
||||
* https://github.com/getgrav/grav-plugin-email-mailersend - Mailersend Mailer
|
||||
|
||||
More plugins will be released soon to support `Gmail`, `Mailgun`, `Mailjet`, `OhMySMTP`, `Postmark`, and `SendInBlue`.
|
||||
|
||||
## SMTP Configurations for popular solutions:
|
||||
|
||||
### Google Email
|
||||
|
||||
A popular option for sending email is to simply use your Google Accounts SMTP server. To set this up you will need to do 2 things first:
|
||||
|
||||
As Gmail no longer supports the "allow less secure apps" option, you now need to have 2FA enabled on the account and setup an "App Password" to create a specific password rather than your general account password. Follow these instructions: [https://support.google.com/accounts/answer/185833](https://support.google.com/accounts/answer/185833)
|
||||
|
||||
Then configure the Email plugin:
|
||||
|
||||
```
|
||||
mailer:
|
||||
engine: smtp
|
||||
smtp:
|
||||
server: smtp.gmail.com
|
||||
port: 587
|
||||
user: 'YOUR_GOOGLE_EMAIL_ADDRESS'
|
||||
password: 'YOUR_GOOGLE_PASSWORD'
|
||||
```
|
||||
|
||||
> NOTE: Check your email sending limits: https://support.google.com/a/answer/166852?hl=en
|
||||
|
||||
### Mailtrap.io
|
||||
|
||||
A good way to test emails is to use a SMTP server service that's built for testing emails, for example [https://mailtrap.io](https://mailtrap.io)
|
||||
|
||||
Setup the Email plugin to use that SMTP server with the fake inbox data. For example enter this configuration in `user/config/plugins/email.yaml` or through the Admin panel:
|
||||
|
||||
```
|
||||
mailer:
|
||||
engine: smtp
|
||||
smtp:
|
||||
server: smtp.mailtrap.io
|
||||
port: 2525
|
||||
encryption: none
|
||||
user: YOUR_MAILTRAP_INBOX_USER
|
||||
password: YOUR_MAILTRAP_INBOX_PASSWORD
|
||||
```
|
||||
|
||||
That service will intercept emails and show them on their web-based interface instead of sending them for real.
|
||||
|
||||
You can try and fine tune the emails there while testing.
|
||||
|
||||
### Sparkpost
|
||||
|
||||
Generous email sending limits even in the free tier, and simple setup, make [Sparkpost](https://www.sparkpost.com) a great option for email sending. You just need to create an account, then setup a verified sending domain. Sparkpost does a nice job of making this process very easy and undertandable. Then just click on the SMTP Relay option to get your details for the configuration:
|
||||
|
||||
```
|
||||
mailer:
|
||||
engine: smtp
|
||||
smtp:
|
||||
server: smtp.sparkpostmail.com
|
||||
port: 587
|
||||
user: 'SMTP_Injection'
|
||||
password: 'SEND_EMAIL_API_KEY'
|
||||
```
|
||||
|
||||
Then try sending a test email...
|
||||
|
||||
### Sendgrid
|
||||
|
||||
[Sendgrid](https://sendgrid.com) offers a very easy-to-setup serivce with 100 emails/day for free. The next level allows you to send 40k/email a day for just $10/month. Configuration is pretty simple, just create an account, then click SMTP integration and click the button to create an API key. The configuration is as follows:
|
||||
|
||||
```
|
||||
mailer:
|
||||
engine: smtp
|
||||
smtp:
|
||||
server: smtp.sendgrid.net
|
||||
port: 587
|
||||
user: 'apikey'
|
||||
password: 'YOUR_SENDGRID_API_KEY'
|
||||
```
|
||||
|
||||
### Mailgun
|
||||
|
||||
[Mailgun is a great service](https://www.mailgun.com/) that offers 10k/emails per month for free. Setup does require SPIF domain verification so that means you need to add at least a TXT entry in your DNS. This is pretty standard for SMTP sending services and does provide verification for remote email servers and makes your email sending more reliable. The Mailgun site, walks you through this process however, and the verification process is simple and fast.
|
||||
|
||||
```
|
||||
mailer:
|
||||
engine: smtp
|
||||
smtp:
|
||||
server: smtp.mailgun.org
|
||||
port: 587
|
||||
user: 'MAILGUN_EMAIL_ADDRESS'
|
||||
password: 'MAILGUN_EMAIL_PASSWORD'
|
||||
```
|
||||
|
||||
Adjust these configurations for your account.
|
||||
|
||||
### MailJet
|
||||
|
||||
Mailjet is another great service that is easy to quickly setup and get started sending email. The free account gives you 200 emails/day or 600 emails/month. Just signup and setup your SPF and DKIM entries for your domain. Then click on the SMTP settings and use those to configure the email plugin:
|
||||
|
||||
```
|
||||
mailer:
|
||||
engine: smtp
|
||||
smtp:
|
||||
server: in-v3.mailjet.com
|
||||
port: 587
|
||||
user: 'MAILJUST_USERNAME_API_KEY'
|
||||
password: 'MAILJUST_PASSWORD_SECRET_KEY'
|
||||
```
|
||||
|
||||
### ZOHO
|
||||
|
||||
ZOHO is a popular solution for hosted email due to it's great 'FREE' tier. It's paid options are also very reasonable and combined with the latest UI updates and outstanding security features, it's a solid email option.
|
||||
|
||||
In order to get ZOHO working with Grav, you need to send email via a user account. You can either use your users' password or generate an **App Password** via your ZOHO account (clicking on your avatar once logged in), then navigating to `My Account -> Security -> App Passwords -> Generate`. Just enter a unique app name (i.e. `Grav Website`).
|
||||
|
||||
NOTE: The SMTP host required can be found in `Settings -> Mail - > Mail Accounts -> POP/IMAP -> SMTP`. This will provide the SMTP server for this account (it may not be `imap.zoho.com` depending on what region you are in)
|
||||
|
||||
```
|
||||
mailer:
|
||||
engine: smtp
|
||||
smtp:
|
||||
server: smtp.zoho.com
|
||||
port: 587
|
||||
user: 'ZOHO_EMAIL_ADDRESS'
|
||||
password: 'ZOHO_EMAIL_PASSWORD'
|
||||
```
|
||||
|
||||
### Sendmail
|
||||
|
||||
Although not as reliable as SMTP not providing as much debug information, sendmail is a simple option as long as your hosting provider is not blocking the default SMTP port `25`:
|
||||
|
||||
```
|
||||
mailer:
|
||||
engine: sendmail
|
||||
sendmail:
|
||||
bin: '/usr/sbin/sendmail -bs'
|
||||
```
|
||||
|
||||
Simply adjust your binary command line to suite your environment
|
||||
|
||||
## SMTP Email Services
|
||||
|
||||
Solid SMTP options that even provide a FREE tier for low email volumes include:
|
||||
|
||||
* SendGrid (100/day free) - https://sendgrid.com
|
||||
* Mailgun - (10k/month free) - https://www.mailgun.com
|
||||
* Mailjet - (6k/month free) - https://www.mailjet.com/
|
||||
* Sparkpost - (15k/month free) - https://www.sparkpost.com
|
||||
* Amazon SES (62k/month free) - https://aws.amazon.com/ses/
|
||||
|
||||
If you are still unsure why should be using one in the first place, check out this article: https://zapier.com/learn/email-marketing/best-transactional-email-sending-services/
|
||||
|
||||
## Testing with CLI Command
|
||||
|
||||
You can test your email configuration with the following CLI Command:
|
||||
|
||||
```
|
||||
$ bin/plugin email test-email -t test@email.com
|
||||
```
|
||||
|
||||
You can also pass in a configuration environment:
|
||||
|
||||
```
|
||||
$ bin/plugin email test-email -t test@email.com --env=mysite.com
|
||||
```
|
||||
|
||||
This will use the email configuration you have located in `user/mysite.com/config/plugins/email.yaml`. Read the docs to find out more about environment-based configuration: https://learn.getgrav.org/advanced/environment-config
|
||||
|
||||
# Programmatically send emails
|
||||
|
||||
Add this code in your plugins:
|
||||
|
||||
```php
|
||||
|
||||
$to = 'email@test.com';
|
||||
$from = 'email@test.com';
|
||||
|
||||
$subject = 'Test';
|
||||
$content = 'Test';
|
||||
|
||||
$message = $this->grav['Email']->message($subject, $content, 'text/html')
|
||||
->setFrom($from)
|
||||
->setTo($to);
|
||||
|
||||
$sent = $this->grav['Email']->send($message);
|
||||
```
|
||||
|
||||
# Emails sent with Forms
|
||||
|
||||
When executing email actions during form processing, action parameters are inherited from the global configuration but may also be overridden on a per-action basis.
|
||||
|
||||
```
|
||||
title: Custom form
|
||||
|
||||
form:
|
||||
name: custom_form
|
||||
fields:
|
||||
|
||||
# Any fields you'd like to add to the form:
|
||||
# Their values may be referenced in email actions via '{{ form.value.FIELDNAME|e }}'
|
||||
|
||||
process:
|
||||
email:
|
||||
subject: "[Custom form] {{ form.value.name|e }}"
|
||||
body: "{% include 'forms/data.txt.twig' %}"
|
||||
from: Custom Sender <sender@example.com>
|
||||
to: Custom Recipient <recipient@example.com>
|
||||
process_markdown: true
|
||||
```
|
||||
|
||||
## Multiple Emails
|
||||
|
||||
You can send multiple emails by creating an array of emails under the `process: email:` option in the form:
|
||||
|
||||
```
|
||||
title: Custom form
|
||||
|
||||
form:
|
||||
name: custom_form
|
||||
fields:
|
||||
|
||||
# Any fields you'd like to add to the form:
|
||||
# Their values may be referenced in email actions via '{{ form.value.FIELDNAME|e }}'
|
||||
|
||||
process:
|
||||
email:
|
||||
-
|
||||
subject: "[Custom Email 1] {{ form.value.name|e }}"
|
||||
body: "{% include 'forms/data.txt.twig' %}"
|
||||
from: Site Owner <owner@mysite.com>
|
||||
to: Recipient 1 <recepient_1@example.com>
|
||||
template: "email/base.html.twig"
|
||||
-
|
||||
subject: "[Custom Email 2] {{ form.value.name|e }}"
|
||||
body: "{% include 'forms/data.txt.twig' %}"
|
||||
from: Site Owner <owner@mysite.com>
|
||||
to: Recipient 2 <recepient_2@example.com>
|
||||
template: "email/base.html.twig"
|
||||
```
|
||||
|
||||
## Templating Emails
|
||||
|
||||
You can specify a Twig template for HTML rendering, else Grav will use the default one `email/base.html.twig` which is included in this plugin. You can also specify a custom template that extends the base, where you can customize the `{% block content %}` and `{% block footer %}`. For example:
|
||||
|
||||
```twig
|
||||
{% extends 'email/base.html.twig' %}
|
||||
|
||||
{% block content %}
|
||||
<p>
|
||||
Greetings {{ form.value.name|e }},
|
||||
</p>
|
||||
|
||||
<p>
|
||||
We have received your request for help. Our team will get in touch with you within 3 Business Days.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Regards,
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>My Company</b>
|
||||
<br /><br />
|
||||
E - <a href="mailto:help@mycompany.com">help@mycompany.com</a><br />
|
||||
M - +1 555-123-4567<br />
|
||||
W - <a href="https://mycompany.com">mycompany.com</a>
|
||||
</p>
|
||||
{% endblock %}
|
||||
|
||||
{% block footer %}
|
||||
<p style="text-align: center;">My Company - All Rights Reserved</p>
|
||||
{% endblock %}
|
||||
```
|
||||
|
||||
## Sending Attachments
|
||||
|
||||
You can add file inputs to your form, and send those files via Email.
|
||||
Just add an `attachments` field and list the file input fields names. You can have multiple file fields, and this will send all the files as attachments. Example:
|
||||
|
||||
```
|
||||
form:
|
||||
name: custom_form
|
||||
fields:
|
||||
|
||||
my-file:
|
||||
label: 'Add a file'
|
||||
type: file
|
||||
multiple: false
|
||||
destination: user/data/files
|
||||
accept:
|
||||
- application/pdf
|
||||
- application/x-pdf
|
||||
- image/png
|
||||
- text/plain
|
||||
|
||||
process:
|
||||
|
||||
email:
|
||||
body: '{% include "forms/data.html.twig" %}'
|
||||
attachments:
|
||||
- 'my-file'
|
||||
```
|
||||
|
||||
## Additional action parameters
|
||||
|
||||
To have more control over your generated email, you may also use the following additional parameters:
|
||||
|
||||
* `reply_to`: Set one or more addresses that should be used to reply to the message.
|
||||
* `cc` _(Carbon copy)_: Add one or more addresses to the delivery list. Many email clients will mark email in one's inbox differently depending on whether they are in the `To:` or `Cc:` list.
|
||||
* `bcc` _(Blind carbon copy)_: Add one or more addresses to the delivery list that should (usually) not be listed in the message data, remaining invisible to other recipients.
|
||||
|
||||
### Specifying email addresses
|
||||
|
||||
Email-related parameters (`from`, `to`, `reply_to`, `cc`and `bcc`) allow different notations for single / multiple values:
|
||||
|
||||
#### Single email address string
|
||||
|
||||
```
|
||||
to: mail@example.com
|
||||
```
|
||||
|
||||
#### `name-addr` RFC822 Formatted string
|
||||
|
||||
```
|
||||
to: Joe Bloggs <maiil@example.com>
|
||||
```
|
||||
|
||||
#### Multiple email address strings
|
||||
|
||||
```
|
||||
to:
|
||||
- mail@example.com
|
||||
- mail+1@example.com
|
||||
- mail+2@example.com
|
||||
```
|
||||
|
||||
or in `name-addr` format:
|
||||
|
||||
```
|
||||
to:
|
||||
- Joe Bloggs <mail@example.com>
|
||||
- Jane Doe <mail+1@example.com>
|
||||
- Jasper Jesperson <mail+2@example.com>
|
||||
```
|
||||
|
||||
#### Simple array format with names
|
||||
|
||||
```
|
||||
to: [mail@exmaple.com, Joe Bloggs]
|
||||
```
|
||||
|
||||
#### Formatted email address with names
|
||||
|
||||
```
|
||||
to:
|
||||
email: mail@example.com
|
||||
name: Joe Bloggs
|
||||
```
|
||||
|
||||
or inline:
|
||||
|
||||
```
|
||||
to: {email: 'mail@example.com', name: 'Joe Bloggs'}
|
||||
```
|
||||
|
||||
#### Multiple email addresses (with and without names)
|
||||
|
||||
```
|
||||
to:
|
||||
- [mail@example.com, Joe Bloggs]
|
||||
- [mail+2@example.com, Jane Doe]
|
||||
```
|
||||
|
||||
```
|
||||
to:
|
||||
-
|
||||
email: mail@example.com
|
||||
name: Joe Bloggs
|
||||
-
|
||||
email: mail+2@example.com
|
||||
name: Jane Doe
|
||||
```
|
||||
|
||||
or inline:
|
||||
|
||||
```
|
||||
to:
|
||||
- {email: 'mail@example.com', name: 'Joe Bloggs'}
|
||||
- {email: 'mail+2@example.com', name: 'Jane Doe'}
|
||||
```
|
||||
|
||||
## Multi-part MIME messages
|
||||
|
||||
Apart from a simple string, an email body may contain different MIME parts (e.g. HTML body with plain text fallback):
|
||||
|
||||
```
|
||||
body:
|
||||
-
|
||||
content_type: 'text/html'
|
||||
body: "{% include 'forms/default/data.html.twig' %}"
|
||||
-
|
||||
content_type: 'text/plain'
|
||||
body: "{% include 'forms/default/data.txt.twig' %}"
|
||||
|
||||
```
|
||||
|
||||
# Troubleshooting
|
||||
|
||||
## Emails are not sent
|
||||
|
||||
#### Debugging
|
||||
|
||||
The first step in determining why emails are not sent is to enable debugging. This can be done via the `user/config/email.yaml` file or via the plugin settings in the admin. Just enable this and then try sending an email again. Then inspect the `logs/email.log` file for potential problems.
|
||||
|
||||
#### ISP Port 25 blocking
|
||||
|
||||
By default, when sending via PHP or Sendmail the machine running the webserver will attempt to send mail using the SMTP protocol. This uses port `25` which is often blocked by ISPs to protected against spamming. You can determine if this port is blocked by running this command in your temrinal (mac/linux only):
|
||||
|
||||
```
|
||||
(echo >/dev/tcp/localhost/25) &>/dev/null && echo "TCP port 25 opened" || echo "TCP port 25 closed"
|
||||
```
|
||||
|
||||
If it's blocked there are ways to configure relays to different ports, but the simplest solution is to use SMTP for mail sending.
|
||||
|
||||
|
||||
#### Exceptions
|
||||
|
||||
If you get an exception when sending email but you cannot see what the error is, you need to enable more verbose exception messages. In the `user/config/system.yaml` file ensure your have the following configuration:
|
||||
|
||||
```
|
||||
errors:
|
||||
display: 1
|
||||
log: true
|
||||
```
|
||||
|
||||
## Configuration Issues
|
||||
|
||||
As explained above in the Configuration section, if you're using the default settings, set the Plugin configuration to use a SMTP server. It can be [Gmail](https://www.digitalocean.com/community/tutorials/how-to-use-google-s-smtp-server) or another SMTP server you have at your disposal.
|
||||
|
||||
This is the first thing to check. The reason is that PHP Mail, the default system used by the Plugin, is not 100% reliable and emails might not arrive.
|
||||
172
plugins/email/blueprints.yaml
Normal file
172
plugins/email/blueprints.yaml
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
name: Email
|
||||
slug: email
|
||||
type: plugin
|
||||
version: 4.0.4
|
||||
testing: false
|
||||
description: Enables the emailing system for Grav
|
||||
icon: envelope
|
||||
author:
|
||||
name: Team Grav
|
||||
email: devs@getgrav.org
|
||||
url: http://getgrav.org
|
||||
keywords: plugin, email, sender
|
||||
homepage: https://github.com/getgrav/grav-plugin-email
|
||||
bugs: https://github.com/getgrav/grav-plugin-email/issues
|
||||
license: MIT
|
||||
|
||||
dependencies:
|
||||
- { name: grav, version: '>=1.7.41' }
|
||||
- { name: form, version: '>=7.0.0' }
|
||||
|
||||
form:
|
||||
validation: loose
|
||||
|
||||
fields:
|
||||
enabled:
|
||||
type: hidden
|
||||
label: PLUGIN_ADMIN.PLUGIN_STATUS
|
||||
highlight: 1
|
||||
default: 1
|
||||
options:
|
||||
1: PLUGIN_ADMIN.ENABLED
|
||||
0: PLUGIN_ADMIN.DISABLED
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
mailer.engine:
|
||||
type: select
|
||||
label: PLUGIN_EMAIL.MAIL_ENGINE
|
||||
size: medium
|
||||
description: PLUGIN_EMAIL.MAIL_ENGINE_DESC
|
||||
data-options@: '\Grav\Plugin\EmailPlugin::getEngines'
|
||||
|
||||
smtp_config:
|
||||
type: section
|
||||
title: PLUGIN_EMAIL.SMTP_CONFIGURATION
|
||||
underline: true
|
||||
|
||||
mailer.smtp.server:
|
||||
type: text
|
||||
size: medium
|
||||
label: PLUGIN_EMAIL.SMTP_SERVER
|
||||
placeholder: PLUGIN_EMAIL.SMTP_SERVER_PLACEHOLDER
|
||||
|
||||
mailer.smtp.port:
|
||||
type: text
|
||||
size: small
|
||||
label: PLUGIN_EMAIL.SMTP_PORT
|
||||
placeholder: PLUGIN_EMAIL.SMTP_PORT_PLACEHOLDER
|
||||
validate:
|
||||
type: number
|
||||
min: 1
|
||||
max: 65535
|
||||
|
||||
mailer.smtp.encryption:
|
||||
type: select
|
||||
size: medium
|
||||
label: PLUGIN_EMAIL.SMTP_ENCRYPTION
|
||||
options:
|
||||
none: PLUGIN_EMAIL.SMTP_ENCRYPTION_NONE
|
||||
ssl: SSL
|
||||
tls: TLS
|
||||
|
||||
mailer.smtp.user:
|
||||
type: text
|
||||
size: medium
|
||||
autocomplete: 'off'
|
||||
label: PLUGIN_EMAIL.SMTP_LOGIN_NAME
|
||||
|
||||
mailer.smtp.password:
|
||||
type: password
|
||||
size: medium
|
||||
autocomplete: 'new-password'
|
||||
label: PLUGIN_EMAIL.SMTP_PASSWORD
|
||||
|
||||
sendmail_config:
|
||||
type: section
|
||||
title: PLUGIN_EMAIL.SENDMAIL_CONFIGURATION
|
||||
underline: true
|
||||
|
||||
mailer.sendmail.bin:
|
||||
type: text
|
||||
size: medium
|
||||
label: PLUGIN_EMAIL.PATH_TO_SENDMAIL
|
||||
placeholder: "/usr/sbin/sendmail"
|
||||
|
||||
|
||||
|
||||
email_Defaults:
|
||||
type: section
|
||||
title: PLUGIN_EMAIL.EMAIL_DEFAULTS
|
||||
underline: true
|
||||
|
||||
content_type:
|
||||
type: select
|
||||
label: PLUGIN_EMAIL.CONTENT_TYPE
|
||||
size: medium
|
||||
default: 'text/html'
|
||||
options:
|
||||
'text/plain': PLUGIN_EMAIL.CONTENT_TYPE_PLAIN_TEXT
|
||||
'text/html': HTML
|
||||
|
||||
from:
|
||||
type: text
|
||||
size: large
|
||||
label: PLUGIN_EMAIL.EMAIL_FORM
|
||||
placeholder: PLUGIN_EMAIL.EMAIL_FORM_PLACEHOLDER
|
||||
help: PLUGIN_EMAIL.EMAIL_FORMAT
|
||||
validate:
|
||||
required: true
|
||||
|
||||
to:
|
||||
type: text
|
||||
size: large
|
||||
label: PLUGIN_EMAIL.EMAIL_TO
|
||||
placeholder: PLUGIN_EMAIL.EMAIL_TO_PLACEHOLDER
|
||||
help: PLUGIN_EMAIL.EMAIL_FORMAT
|
||||
validate:
|
||||
required: true
|
||||
|
||||
cc:
|
||||
type: text
|
||||
size: large
|
||||
label: PLUGIN_EMAIL.EMAIL_CC
|
||||
placeholder: PLUGIN_EMAIL.EMAIL_CC_PLACEHOLDER
|
||||
help: PLUGIN_EMAIL.EMAIL_FORMAT
|
||||
|
||||
bcc:
|
||||
type: text
|
||||
size: large
|
||||
label: PLUGIN_EMAIL.EMAIL_BCC
|
||||
placeholder: PLUGIN_EMAIL.EMAIL_BCC_PLACEHOLDER
|
||||
help: PLUGIN_EMAIL.EMAIL_FORMAT
|
||||
|
||||
reply_to:
|
||||
type: text
|
||||
size: large
|
||||
label: PLUGIN_EMAIL.EMAIL_REPLY_TO
|
||||
placeholder: PLUGIN_EMAIL.EMAIL_REPLY_TO_PLACEHOLDER
|
||||
help: PLUGIN_EMAIL.EMAIL_FORMAT
|
||||
|
||||
body:
|
||||
type: textarea
|
||||
size: large
|
||||
rows: 10
|
||||
label: PLUGIN_EMAIL.EMAIL_BODY
|
||||
placeholder: PLUGIN_EMAIL.EMAIL_BODY_PLACEHOLDER
|
||||
|
||||
advanced_section:
|
||||
type: section
|
||||
title: PLUGIN_EMAIL.ADVANCED
|
||||
underline: true
|
||||
|
||||
debug:
|
||||
type: toggle
|
||||
label: PLUGIN_EMAIL.DEBUG
|
||||
highlight: 1
|
||||
default: 0
|
||||
options:
|
||||
1: PLUGIN_ADMIN.ENABLED
|
||||
0: PLUGIN_ADMIN.DISABLED
|
||||
validate:
|
||||
type: bool
|
||||
520
plugins/email/classes/Email.php
Normal file
520
plugins/email/classes/Email.php
Normal file
|
|
@ -0,0 +1,520 @@
|
|||
<?php
|
||||
namespace Grav\Plugin\Email;
|
||||
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Utils;
|
||||
use Grav\Common\Language\Language;
|
||||
use Grav\Common\Markdown\Parsedown;
|
||||
use Grav\Common\Twig\Twig;
|
||||
use Grav\Framework\Form\Interfaces\FormInterface;
|
||||
use \Monolog\Logger;
|
||||
use \Monolog\Handler\StreamHandler;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
use Symfony\Component\Mailer\Envelope;
|
||||
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
|
||||
use Symfony\Component\Mailer\Header\MetadataHeader;
|
||||
use Symfony\Component\Mailer\Header\TagHeader;
|
||||
use Symfony\Component\Mailer\Mailer;
|
||||
use Symfony\Component\Mailer\Transport;
|
||||
use Symfony\Component\Mailer\Transport\TransportInterface;
|
||||
use Symfony\Component\Mime\Address;
|
||||
|
||||
class Email
|
||||
{
|
||||
/** @var Mailer */
|
||||
protected $mailer;
|
||||
|
||||
/** @var TransportInterface */
|
||||
protected $transport;
|
||||
|
||||
protected $log;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->initMailer();
|
||||
$this->initLog();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if emails have been enabled in the system.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function enabled(): bool
|
||||
{
|
||||
return Grav::instance()['config']->get('plugins.email.mailer.engine') !== 'none';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if debugging on emails has been enabled.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function debug(): bool
|
||||
{
|
||||
return Grav::instance()['config']->get('plugins.email.debug') == 'true';
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an email message.
|
||||
*
|
||||
* @param string|null $subject
|
||||
* @param string|null $body
|
||||
* @param string|null $contentType
|
||||
* @param string|null $charset @deprecated
|
||||
* @return Message
|
||||
*/
|
||||
public function message(string $subject = null, string $body = null, string $contentType = null, string $charset = null): Message
|
||||
{
|
||||
$message = new Message();
|
||||
$message->subject($subject);
|
||||
if ($contentType === 'text/html') {
|
||||
$message->html($body);
|
||||
} else {
|
||||
$message->text($body);
|
||||
}
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send email.
|
||||
*
|
||||
* @param Message $message
|
||||
* @param Envelope|null $envelope
|
||||
* @return int
|
||||
*/
|
||||
public function send(Message $message, Envelope $envelope = null): int
|
||||
{
|
||||
$status = '🛑 ';
|
||||
$sent_msg = null;
|
||||
$debug = null;
|
||||
|
||||
try {
|
||||
$sent_msg = $this->transport->send($message->getEmail(), $envelope);
|
||||
$return = 1;
|
||||
$status = '✅';
|
||||
$debug = $sent_msg->getDebug();
|
||||
} catch (TransportExceptionInterface $e) {
|
||||
$return = 0;
|
||||
$status .= $e->getMessage();
|
||||
$debug = $e->getDebug();
|
||||
}
|
||||
|
||||
if ($this->debug()) {
|
||||
$log_msg = "Email sent to %s at %s -> %s\n%s";
|
||||
$to = $this->jsonifyRecipients($message->getEmail()->getTo());
|
||||
$msg = sprintf($log_msg, $to, date('Y-m-d H:i:s'), $status, $debug);
|
||||
$this->log->addInfo($msg);
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build e-mail message.
|
||||
*
|
||||
* @param array $params
|
||||
* @param array $vars
|
||||
* @return Message
|
||||
*/
|
||||
public function buildMessage(array $params, array $vars = []): Message
|
||||
{
|
||||
/** @var Twig $twig */
|
||||
$twig = Grav::instance()['twig'];
|
||||
$twig->init();
|
||||
|
||||
/** @var Config $config */
|
||||
$config = Grav::instance()['config'];
|
||||
|
||||
/** @var Language $language */
|
||||
$language = Grav::instance()['language'];
|
||||
|
||||
// Create message object.
|
||||
$message = new Message();
|
||||
$headers = $message->getEmail()->getHeaders();
|
||||
$email = $message->getEmail();
|
||||
|
||||
// Extend parameters with defaults.
|
||||
$defaults = [
|
||||
'bcc' => $config->get('plugins.email.bcc', []),
|
||||
'bcc_name' => $config->get('plugins.email.bcc_name'),
|
||||
'body' => $config->get('plugins.email.body', '{% include "forms/data.html.twig" %}'),
|
||||
'cc' => $config->get('plugins.email.cc', []),
|
||||
'cc_name' => $config->get('plugins.email.cc_name'),
|
||||
'charset' => $config->get('plugins.email.charset', 'utf-8'),
|
||||
'from' => $config->get('plugins.email.from'),
|
||||
'from_name' => $config->get('plugins.email.from_name'),
|
||||
'content_type' => $config->get('plugins.email.content_type', 'text/html'),
|
||||
'reply_to' => $config->get('plugins.email.reply_to', []),
|
||||
'reply_to_name' => $config->get('plugins.email.reply_to_name'),
|
||||
'subject' => !empty($vars['form']) && $vars['form'] instanceof FormInterface ? $vars['form']->page()->title() : null,
|
||||
'to' => $config->get('plugins.email.to'),
|
||||
'to_name' => $config->get('plugins.email.to_name'),
|
||||
'process_markdown' => false,
|
||||
'template' => false,
|
||||
'message' => $message
|
||||
];
|
||||
|
||||
foreach ($defaults as $key => $value) {
|
||||
if (!key_exists($key, $params)) {
|
||||
$params[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$params['to']) {
|
||||
throw new \RuntimeException($language->translate('PLUGIN_EMAIL.PLEASE_CONFIGURE_A_TO_ADDRESS'));
|
||||
}
|
||||
if (!$params['from']) {
|
||||
throw new \RuntimeException($language->translate('PLUGIN_EMAIL.PLEASE_CONFIGURE_A_FROM_ADDRESS'));
|
||||
}
|
||||
|
||||
|
||||
// make email configuration available to templates
|
||||
$vars += [
|
||||
'email' => $params,
|
||||
];
|
||||
|
||||
$params = $this->processParams($params, $vars);
|
||||
|
||||
// Process parameters.
|
||||
foreach ($params as $key => $value) {
|
||||
switch ($key) {
|
||||
case 'body':
|
||||
if (is_string($value)) {
|
||||
$this->processBody($message, $params, $vars, $twig, $value);
|
||||
} elseif (is_array($value)) {
|
||||
foreach ($value as $body_part) {
|
||||
$params_part = $params;
|
||||
if (isset($body_part['content_type'])) {
|
||||
$params_part['content_type'] = $body_part['content_type'];
|
||||
}
|
||||
if (isset($body_part['template'])) {
|
||||
$params_part['template'] = $body_part['template'];
|
||||
}
|
||||
if (isset($body_part['body'])) {
|
||||
$this->processBody($message, $params_part, $vars, $twig, $body_part['body']);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'subject':
|
||||
if ($value) {
|
||||
$message->subject($language->translate($value));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'to':
|
||||
case 'from':
|
||||
case 'cc':
|
||||
case 'bcc':
|
||||
case 'reply_to':
|
||||
if ($recipients = $this->processRecipients($key, $params)) {
|
||||
$key = $key === 'reply_to' ? 'replyTo' : $key;
|
||||
$email->$key(...$recipients);
|
||||
}
|
||||
break;
|
||||
case 'tags':
|
||||
foreach ((array) $value as $tag) {
|
||||
if (is_string($tag)) {
|
||||
$headers->add(new TagHeader($tag));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'metadata':
|
||||
foreach ((array) $value as $k => $v) {
|
||||
if (is_string($k) && is_string($v)) {
|
||||
$headers->add(new MetadataHeader($k, $v));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @param array $params
|
||||
* @return array
|
||||
*/
|
||||
protected function processRecipients(string $type, array $params): array
|
||||
{
|
||||
if (array_key_exists($type, $params) && $params[$type] === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$recipients = $params[$type] ?? Grav::instance()['config']->get('plugins.email.'.$type) ?? [];
|
||||
|
||||
$list = [];
|
||||
|
||||
if (!empty($recipients)) {
|
||||
if (is_array($recipients)) {
|
||||
if (Utils::isAssoc($recipients) || (count($recipients) ===2 && $this->isValidEmail($recipients[0]) && !$this->isValidEmail($recipients[1]))) {
|
||||
$list[] = $this->createAddress($recipients);
|
||||
} else {
|
||||
foreach ($recipients as $recipient) {
|
||||
$list[] = $this->createAddress($recipient);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (is_string($recipients) && Utils::contains($recipients, ',')) {
|
||||
$recipients = array_map('trim', explode(',', $recipients));
|
||||
foreach ($recipients as $recipient) {
|
||||
$list[] = $this->createAddress($recipient);
|
||||
}
|
||||
} else {
|
||||
if (!Utils::contains($recipients, ['<','>']) && (isset($params[$type."_name"]))) {
|
||||
$recipients = [$recipients, $params[$type."_name"]];
|
||||
}
|
||||
$list[] = $this->createAddress($recipients);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @return Address
|
||||
*/
|
||||
protected function createAddress($data): Address
|
||||
{
|
||||
if (is_string($data)) {
|
||||
preg_match('/^(.*)\<(.*)\>$/', $data, $matches);
|
||||
if (isset($matches[2])) {
|
||||
$email = trim($matches[2]);
|
||||
$name = trim($matches[1]);
|
||||
} else {
|
||||
$email = $data;
|
||||
$name = '';
|
||||
}
|
||||
} elseif (Utils::isAssoc($data)) {
|
||||
$first_key = array_key_first($data);
|
||||
if (filter_var($first_key, FILTER_VALIDATE_EMAIL)) {
|
||||
$email = $first_key;
|
||||
$name = $data[$first_key];
|
||||
} else {
|
||||
$email = $data['email'] ?? $data['mail'] ?? $data['address'] ?? '';
|
||||
$name = $data['name'] ?? $data['fullname'] ?? '';
|
||||
}
|
||||
} else {
|
||||
$email = $data[0] ?? '';
|
||||
$name = $data[1] ?? '';
|
||||
}
|
||||
return new Address($email, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|Mailer
|
||||
* @internal
|
||||
*/
|
||||
protected function initMailer(): ?Mailer
|
||||
{
|
||||
if (!$this->enabled()) {
|
||||
return null;
|
||||
}
|
||||
if (!$this->mailer) {
|
||||
$this->transport = $this->getTransport();
|
||||
// Create the Mailer using your created Transport
|
||||
$this->mailer = new Mailer($this->transport);
|
||||
}
|
||||
return $this->mailer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function initLog()
|
||||
{
|
||||
$log_file = Grav::instance()['locator']->findResource('log://email.log', true, true);
|
||||
$this->log = new Logger('email');
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$this->log->pushHandler(new StreamHandler($log_file, Logger::DEBUG));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $params
|
||||
* @param array $vars
|
||||
* @return array
|
||||
*/
|
||||
protected function processParams(array $params, array $vars = []): array
|
||||
{
|
||||
$twig = Grav::instance()['twig'];
|
||||
array_walk_recursive($params, function(&$value) use ($twig, $vars) {
|
||||
if (is_string($value)) {
|
||||
$value = $twig->processString($value, $vars);
|
||||
}
|
||||
});
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $message
|
||||
* @param $params
|
||||
* @param $vars
|
||||
* @param $twig
|
||||
* @param $body
|
||||
* @return void
|
||||
*/
|
||||
protected function processBody($message, $params, $vars, $twig, $body)
|
||||
{
|
||||
if ($params['process_markdown'] && $params['content_type'] === 'text/html') {
|
||||
$body = (new Parsedown())->text($body);
|
||||
}
|
||||
|
||||
if ($params['template']) {
|
||||
$body = $twig->processTemplate($params['template'], ['content' => $body] + $vars);
|
||||
}
|
||||
|
||||
$content_type = !empty($params['content_type']) ? $twig->processString($params['content_type'], $vars) : null;
|
||||
|
||||
if ($content_type === 'text/html') {
|
||||
$message->html($body);
|
||||
} else {
|
||||
$message->text($body);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TransportInterface
|
||||
*/
|
||||
protected static function getTransport(): Transport\TransportInterface
|
||||
{
|
||||
/** @var Config $config */
|
||||
$config = Grav::instance()['config'];
|
||||
$engine = $config->get('plugins.email.mailer.engine');
|
||||
$dsn = 'null://default';
|
||||
|
||||
|
||||
// Create the Transport and initialize it.
|
||||
switch ($engine) {
|
||||
case 'smtps':
|
||||
case 'smtp':
|
||||
$options = $config->get('plugins.email.mailer.smtp');
|
||||
$dsn = $engine . '://';
|
||||
$auth = '';
|
||||
|
||||
if (isset($options['encryption']) && $options['encryption'] === 'none') {
|
||||
$options['options']['verify_peer'] = 0;
|
||||
}
|
||||
if (isset($options['user'])) {
|
||||
$auth .= urlencode($options['user']);
|
||||
}
|
||||
if (isset($options['password'])) {
|
||||
$auth .= ':'. urlencode($options['password']);
|
||||
}
|
||||
if (!empty($auth)) {
|
||||
$dsn .= "$auth@";
|
||||
}
|
||||
if (isset($options['server'])) {
|
||||
$dsn .= urlencode($options['server']);
|
||||
}
|
||||
if (isset($options['port'])) {
|
||||
$dsn .= ":{$options['port']}";
|
||||
}
|
||||
if (isset($options['options'])) {
|
||||
$dsn .= '?' . http_build_query($options['options']);
|
||||
}
|
||||
break;
|
||||
case 'mail':
|
||||
case 'native':
|
||||
$dsn = 'native://default';
|
||||
break;
|
||||
case 'sendmail':
|
||||
$dsn = 'sendmail://default';
|
||||
$bin = $config->get('plugins.email.mailer.sendmail.bin');
|
||||
if (isset($bin)) {
|
||||
$dsn .= '?command=' . urlencode($bin);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$e = new Event(['engine' => $engine, ]);
|
||||
Grav::instance()->fireEvent('onEmailTransportDsn', $e);
|
||||
if (isset($e['dsn'])) {
|
||||
$dsn = $e['dsn'];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if ($dsn instanceof TransportInterface) {
|
||||
$transport = $dsn;
|
||||
} else {
|
||||
$transport = Transport::fromDsn($dsn) ;
|
||||
}
|
||||
|
||||
return $transport;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $recipients
|
||||
* @return string
|
||||
*/
|
||||
protected function jsonifyRecipients(array $recipients): string
|
||||
{
|
||||
$json = [];
|
||||
foreach ($recipients as $recipient) {
|
||||
$json[] = str_replace('"', "", $recipient->toString());
|
||||
}
|
||||
return json_encode($json);
|
||||
}
|
||||
|
||||
protected function isValidEmail($email): bool
|
||||
{
|
||||
return is_string($email) && filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @deprecated 4.0 Switched from Swiftmailer to Symfony/Mailer - No longer supported
|
||||
*/
|
||||
public static function flushQueue() {}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @deprecated 4.0 Switched from Swiftmailer to Symfony/Mailer - No longer supported
|
||||
*/
|
||||
public static function clearQueueFailures() {}
|
||||
|
||||
/**
|
||||
* Creates an attachment.
|
||||
*
|
||||
* @param string $data
|
||||
* @param string $filename
|
||||
* @param string $contentType
|
||||
* @deprecated 4.0 Switched from Swiftmailer to Symfony/Mailer - No longer supported
|
||||
* @return void
|
||||
*/
|
||||
public function attachment($data = null, $filename = null, $contentType = null) {}
|
||||
|
||||
/**
|
||||
* Creates an embedded attachment.
|
||||
*
|
||||
* @param string $data
|
||||
* @param string $filename
|
||||
* @param string $contentType
|
||||
* @deprecated 4.0 Switched from Swiftmailer to Symfony/Mailer - No longer supported
|
||||
* @return void
|
||||
*/
|
||||
public function embedded($data = null, $filename = null, $contentType = null) {}
|
||||
|
||||
|
||||
/**
|
||||
* Creates an image attachment.
|
||||
*
|
||||
* @param string $data
|
||||
* @param string $filename
|
||||
* @param string $contentType
|
||||
* @deprecated 4.0 Switched from Swiftmailer to Symfony/Mailer - No longer supported
|
||||
* @return void
|
||||
*/
|
||||
public function image($data = null, $filename = null, $contentType = null) {}
|
||||
|
||||
}
|
||||
103
plugins/email/classes/Message.php
Normal file
103
plugins/email/classes/Message.php
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
namespace Grav\Plugin\Email;
|
||||
|
||||
use Symfony\Component\Mime\Email as SymfonyEmail;
|
||||
|
||||
class Message
|
||||
{
|
||||
/** @var SymfonyEmail */
|
||||
protected $email;
|
||||
|
||||
public function __construct() {
|
||||
$this->email = new SymfonyEmail();
|
||||
}
|
||||
|
||||
public function subject($subject): self
|
||||
{
|
||||
$this->email->subject($subject);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setSubject($subject): self
|
||||
{
|
||||
$this->subject($subject);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function to($to): self
|
||||
{
|
||||
$this->email->to($to);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function from($from): self
|
||||
{
|
||||
$this->email->from($from);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function cc($cc): self
|
||||
{
|
||||
$this->email->cc($cc);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function bcc($bcc): self
|
||||
{
|
||||
$this->email->bcc($bcc);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function replyTo($reply_to): self
|
||||
{
|
||||
$this->email->replyTo($reply_to);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function text($text): self
|
||||
{
|
||||
$this->email->text($text);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function html($html): self
|
||||
{
|
||||
$this->email->html($html);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function attachFromPath($path): self
|
||||
{
|
||||
$this->email->attachFromPath($path);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function embedFromPath($path): self
|
||||
{
|
||||
$this->email->embedFromPath($path);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function reply_to($reply_to): self
|
||||
{
|
||||
$this->replyTo($reply_to);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setFrom($from): self
|
||||
{
|
||||
$this->from($from);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setTo($to): self
|
||||
{
|
||||
$this->to($to);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEmail(): SymfonyEmail
|
||||
{
|
||||
return $this->email;
|
||||
}
|
||||
}
|
||||
48
plugins/email/classes/Utils.php
Normal file
48
plugins/email/classes/Utils.php
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace Grav\Plugin\Email;
|
||||
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Twig\Twig;
|
||||
use Grav\Common\Utils as GravUtils;
|
||||
|
||||
/**
|
||||
* Class Utils
|
||||
* @package Grav\Plugin\Email
|
||||
*/
|
||||
class Utils
|
||||
{
|
||||
/**
|
||||
* Quick utility method to send an HTML email.
|
||||
*
|
||||
* @param array<int,mixed> $params
|
||||
*
|
||||
* @return bool True if the action was performed.
|
||||
*/
|
||||
public static function sendEmail(...$params)
|
||||
{
|
||||
if (is_array($params[0])) {
|
||||
$params = array_shift($params);
|
||||
} else {
|
||||
$keys = ['subject', 'body', 'to', 'from', 'content_type'];
|
||||
$params = GravUtils::arrayCombine($keys, $params);
|
||||
}
|
||||
|
||||
//Initialize twig if not yet initialized
|
||||
/** @var Twig $twig */
|
||||
$twig = Grav::instance()['twig']->init();
|
||||
|
||||
/** @var Email $email */
|
||||
$email = Grav::instance()['Email'];
|
||||
|
||||
if (empty($params['to']) || empty($params['subject']) || empty($params['body'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$params['body'] = $twig->processTemplate('email/base.html.twig', ['content' => $params['body']]);
|
||||
|
||||
$message = $email->buildMessage($params);
|
||||
|
||||
return $email->send($message);
|
||||
}
|
||||
}
|
||||
39
plugins/email/cli/ClearQueueFailuresCommand.php
Normal file
39
plugins/email/cli/ClearQueueFailuresCommand.php
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
namespace Grav\Plugin\Console;
|
||||
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Console\ConsoleCommand;
|
||||
use Grav\Plugin\Email\Email;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
/**
|
||||
* Class FlushQueueCommand
|
||||
* @package Grav\Console\Cli\
|
||||
* @deprecated 4.0 Switched from Swiftmailer to Symfony/Mailer - No longer supported
|
||||
*/
|
||||
class ClearQueueFailuresCommand extends ConsoleCommand
|
||||
{
|
||||
/** @var array */
|
||||
protected $options = [];
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('clear-queue-failures')
|
||||
->setAliases(['clearqueue'])
|
||||
->setDescription('DEPRECATED: Clears any queue failures that have accumulated')
|
||||
->setHelp('The <info>clear-queue-failures</info> command clears any queue failures that have accumulated');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
* @deprecated 4.0 Switched from Swiftmailer to Symfony/Mailer - No longer supported
|
||||
*/
|
||||
protected function serve()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
39
plugins/email/cli/FlushQueueCommand.php
Normal file
39
plugins/email/cli/FlushQueueCommand.php
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
namespace Grav\Plugin\Console;
|
||||
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Console\ConsoleCommand;
|
||||
use Grav\Plugin\Email\Email;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
/**
|
||||
* Class FlushQueueCommand
|
||||
* @package Grav\Console\Cli\
|
||||
* @deprecated 4.0 Switched from Swiftmailer to Symfony/Mailer - No longer supported
|
||||
*/
|
||||
class FlushQueueCommand extends ConsoleCommand
|
||||
{
|
||||
/** @var array */
|
||||
protected $options = [];
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('flush-queue')
|
||||
->setAliases(['flushqueue'])
|
||||
->setDescription('DEPRECATED: Flushes the email queue of any pending emails')
|
||||
->setHelp('The <info>flush-queue</info> command flushes the email queue of any pending emails');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
* @deprecated 4.0 Switched from Swiftmailer to Symfony/Mailer - No longer supported
|
||||
*/
|
||||
protected function serve()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
105
plugins/email/cli/TestEmailCommand.php
Normal file
105
plugins/email/cli/TestEmailCommand.php
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
<?php
|
||||
namespace Grav\Plugin\Console;
|
||||
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Data\Data;
|
||||
use Grav\Console\ConsoleCommand;
|
||||
use Grav\Plugin\Email\Email;
|
||||
use Grav\Plugin\Email\Utils as EmailUtils;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
/**
|
||||
* Class TestEmailCommand
|
||||
* @package Grav\Console\Cli\
|
||||
*/
|
||||
class TestEmailCommand extends ConsoleCommand
|
||||
{
|
||||
/** @var array */
|
||||
protected $options = [];
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('test-email')
|
||||
->setAliases(['testemail'])
|
||||
->addOption(
|
||||
'to',
|
||||
't',
|
||||
InputOption::VALUE_REQUIRED,
|
||||
'An email address to send the email to'
|
||||
)
|
||||
->addOption(
|
||||
'env',
|
||||
'e',
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
'The environment to trigger a specific configuration. For example: localhost, mysite.dev, www.mysite.com'
|
||||
)
|
||||
->addOption(
|
||||
'subject',
|
||||
's',
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
'A subject for the email'
|
||||
)
|
||||
->addOption(
|
||||
'body',
|
||||
'b',
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
'The body of the email'
|
||||
)
|
||||
->setDescription('Sends a test email using the plugin\'s configuration')
|
||||
->setHelp('The <info>test-email</info> command sends a test email using the plugin\'s configuration');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
protected function serve()
|
||||
{
|
||||
// TODO: remove when requiring Grav 1.7+
|
||||
if (method_exists($this, 'initializeGrav')) {
|
||||
$this->initializeThemes();
|
||||
}
|
||||
|
||||
$this->output->writeln('');
|
||||
$this->output->writeln('<yellow>Current Configuration:</yellow>');
|
||||
$this->output->writeln('');
|
||||
|
||||
$grav = Grav::instance();
|
||||
$email_config = new Data($grav['config']->get('plugins.email'));
|
||||
if ($email_config->get('mailer.smtp.password')) {
|
||||
$password = $email_config->get('mailer.smtp.password');
|
||||
$obfuscated_password = str_repeat('*', strlen($password) - 2) . substr($password, -2);
|
||||
$email_config->set('mailer.smtp.password', $obfuscated_password);
|
||||
}
|
||||
|
||||
dump($email_config);
|
||||
|
||||
$this->output->writeln('');
|
||||
|
||||
$to = $this->input->getOption('to') ?: $grav['config']->get('plugins.email.to');
|
||||
$subject = $this->input->getOption('subject');
|
||||
$body = $this->input->getOption('body');
|
||||
|
||||
if (!$subject) {
|
||||
$subject = 'Testing Grav Email Plugin';
|
||||
}
|
||||
|
||||
if (!$body) {
|
||||
$configuration = print_r($email_config, true);
|
||||
$body = $grav['language']->translate(['PLUGIN_EMAIL.TEST_EMAIL_BODY', $configuration]);
|
||||
}
|
||||
|
||||
$sent = EmailUtils::sendEmail(['subject'=>$subject, 'body'=>$body, 'to'=>$to]);
|
||||
|
||||
if ($sent) {
|
||||
$this->output->writeln("<green>Message sent successfully!</green>");
|
||||
} else {
|
||||
$this->output->writeln("<red>Problem sending email...</red>");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
44
plugins/email/composer.json
Normal file
44
plugins/email/composer.json
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
{
|
||||
"name": "getgrav/grav-plugin-email",
|
||||
"type": "grav-plugin",
|
||||
"description": "Email plugin for Grav CMS",
|
||||
"keywords": ["email", "plugin", "sender"],
|
||||
"homepage": "https://github.com/getgrav/grav-plugin-email",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Team Grav",
|
||||
"email": "devs@getgrav.org",
|
||||
"homepage": "https://getgrav.org",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/getgrav/grav-plugin-email/issues",
|
||||
"irc": "https://chat.getgrav.org",
|
||||
"forum": "https://getgrav.org/forum",
|
||||
"docs": "https://github.com/getgrav/grav-plugin-email/blob/master/README.md"
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.3.6",
|
||||
"symfony/mailer": "^5.4",
|
||||
"symfony/messenger": "^5.4"
|
||||
},
|
||||
"replace": {
|
||||
"symfony/polyfill-iconv": "*",
|
||||
"symfony/polyfill-mbstring": "*",
|
||||
"symfony/polyfill-php72": "*"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Grav\\Plugin\\Email\\": "classes/",
|
||||
"Grav\\Plugin\\Console\\": "cli/"
|
||||
},
|
||||
"classmap": ["email.php"]
|
||||
},
|
||||
"config": {
|
||||
"platform": {
|
||||
"php": "7.3.6"
|
||||
}
|
||||
}
|
||||
}
|
||||
1342
plugins/email/composer.lock
generated
Normal file
1342
plugins/email/composer.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
190
plugins/email/email.php
Normal file
190
plugins/email/email.php
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
<?php
|
||||
namespace Grav\Plugin;
|
||||
|
||||
use Composer\Autoload\ClassLoader;
|
||||
use Grav\Common\Data\Data;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Plugin;
|
||||
use Grav\Common\Utils;
|
||||
use Grav\Plugin\Email\Email;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
|
||||
class EmailPlugin extends Plugin
|
||||
{
|
||||
/**
|
||||
* @var Email
|
||||
*/
|
||||
protected $email;
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return [
|
||||
'onPluginsInitialized' => ['onPluginsInitialized', 0],
|
||||
'onFormProcessed' => ['onFormProcessed', 0],
|
||||
'onTwigTemplatePaths' => ['onTwigTemplatePaths', 0],
|
||||
'onSchedulerInitialized' => ['onSchedulerInitialized', 0],
|
||||
'onAdminSave' => ['onAdminSave', 0],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ClassLoader
|
||||
*/
|
||||
public function autoload(): ClassLoader
|
||||
{
|
||||
return require __DIR__ . '/vendor/autoload.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize emailing.
|
||||
*/
|
||||
public function onPluginsInitialized()
|
||||
{
|
||||
$this->email = new Email();
|
||||
|
||||
if ($this->email::enabled()) {
|
||||
$this->grav['Email'] = $this->email;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add twig paths to plugin templates.
|
||||
*/
|
||||
public function onTwigTemplatePaths()
|
||||
{
|
||||
$twig = $this->grav['twig'];
|
||||
$twig->twig_paths[] = __DIR__ . '/templates';
|
||||
}
|
||||
|
||||
/**
|
||||
* Force compile during save if admin plugin save
|
||||
*
|
||||
* @param Event $event
|
||||
*/
|
||||
public function onAdminSave(Event $event)
|
||||
{
|
||||
/** @var Data $obj */
|
||||
$obj = $event['object'];
|
||||
|
||||
if ($obj instanceof Data && $obj->blueprints()->getFilename() === 'email/blueprints') {
|
||||
$current_pw = $this->grav['config']->get('plugins.email.mailer.smtp.password');
|
||||
$new_pw = $obj->get('mailer.smtp.password');
|
||||
if (!empty($current_pw) && empty($new_pw)) {
|
||||
$obj->set('mailer.smtp.password', $current_pw);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send email when processing the form data.
|
||||
*
|
||||
* @param Event $event
|
||||
*/
|
||||
public function onFormProcessed(Event $event)
|
||||
{
|
||||
$form = $event['form'];
|
||||
$action = $event['action'];
|
||||
$params = $event['params'];
|
||||
|
||||
if (!$this->email->enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch ($action) {
|
||||
case 'email':
|
||||
// Prepare Twig variables
|
||||
$vars = array(
|
||||
'form' => $form,
|
||||
'page' => $this->grav['page']
|
||||
);
|
||||
|
||||
// Copy files now, we need those.
|
||||
// TODO: needs an update
|
||||
$form->legacyUploads();
|
||||
$form->copyFiles();
|
||||
|
||||
$this->grav->fireEvent('onEmailSend', new Event(['params' => &$params, 'vars' => &$vars]));
|
||||
|
||||
if (Utils::isAssoc($params)) {
|
||||
$this->sendFormEmail($form, $params, $vars);
|
||||
} else {
|
||||
foreach ($params as $email) {
|
||||
$this->sendFormEmail($form, $email, $vars);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected function sendFormEmail($form, $params, $vars)
|
||||
{
|
||||
// Build message
|
||||
$message = $this->email->buildMessage($params, $vars);
|
||||
$locator = $this->grav['locator'];
|
||||
|
||||
if (isset($params['attachments'])) {
|
||||
$filesToAttach = (array)$params['attachments'];
|
||||
if ($filesToAttach) foreach ($filesToAttach as $fileToAttach) {
|
||||
$filesValues = $form->value($fileToAttach);
|
||||
|
||||
if ($filesValues) foreach($filesValues as $fileValues) {
|
||||
if (isset($fileValues['file'])) {
|
||||
$filename = $fileValues['file'];
|
||||
} else {
|
||||
$filename = $fileValues['path'];
|
||||
}
|
||||
|
||||
$filename = $locator->findResource($filename, true, true);
|
||||
|
||||
try {
|
||||
$message->attachFromPath($filename);
|
||||
} catch (\Exception $e) {
|
||||
// Log any issues
|
||||
$this->grav['log']->error($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//fire event to apply optional signers
|
||||
$this->grav->fireEvent('onEmailMessage', new Event(['message' => $message, 'params' => $params, 'form' => $form]));
|
||||
|
||||
// Send e-mail
|
||||
$this->email->send($message);
|
||||
|
||||
//fire event after eMail was sent
|
||||
$this->grav->fireEvent('onEmailSent', new Event(['message' => $message, 'params' => $params, 'form' => $form]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for dynamic blueprint field
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getEngines(): array
|
||||
{
|
||||
$engines = (object) [
|
||||
'sendmail' => 'Sendmail',
|
||||
'smtp' => 'SMTP',
|
||||
'smtps' => 'SMTPS',
|
||||
'native' => 'Native',
|
||||
'none' => 'PLUGIN_ADMIN.DISABLED',
|
||||
];
|
||||
Grav::instance()->fireEvent('onEmailEngines', new Event(['engines' => $engines]));
|
||||
return (array) $engines;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 4.0 Switched from Swiftmailer to Symfony/Mailer - No longer supported
|
||||
*/
|
||||
public function onSchedulerInitialized(Event $e)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
15
plugins/email/email.yaml
Normal file
15
plugins/email/email.yaml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
enabled: true
|
||||
from:
|
||||
to:
|
||||
mailer:
|
||||
engine: sendmail
|
||||
smtp:
|
||||
server: localhost
|
||||
port: 25
|
||||
encryption: none
|
||||
user:
|
||||
password:
|
||||
sendmail:
|
||||
bin: '/usr/sbin/sendmail -bs'
|
||||
content_type: text/html
|
||||
debug: false
|
||||
218
plugins/email/languages.yaml
Normal file
218
plugins/email/languages.yaml
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
en:
|
||||
PLUGIN_EMAIL:
|
||||
MAIL_ENGINE: "Mail Engine"
|
||||
MAIL_ENGINE_DISABLED: "Disabled"
|
||||
MAIL_ENGINE_DESC: "NOTE: If you select an engine provided by another plugin, you should configure options in that plugin."
|
||||
CONTENT_TYPE: "Content type"
|
||||
CONTENT_TYPE_PLAIN_TEXT: "Plain text"
|
||||
CHARSET: "Charset"
|
||||
CHARSET_PLACEHOLDER: "Defaults to UTF-8"
|
||||
EMAIL_FORM: "From address"
|
||||
EMAIL_FORM_PLACEHOLDER: "Default email from address"
|
||||
EMAIL_FROM_NAME: "From name"
|
||||
EMAIL_FROM_NAME_PLACEHOLDER: "Default email from name"
|
||||
EMAIL_TO: "To address"
|
||||
EMAIL_TO_PLACEHOLDER: "Default email to address"
|
||||
EMAIL_TO_NAME: "To name"
|
||||
EMAIL_TO_NAME_PLACEHOLDER: "Default email to name"
|
||||
EMAIL_CC: "CC address"
|
||||
EMAIL_CC_PLACEHOLDER: "Default email CC address"
|
||||
EMAIL_CC_NAME: "CC name"
|
||||
EMAIL_CC_NAME_PLACEHOLDER: "Default email CC name"
|
||||
EMAIL_BCC: "BCC address"
|
||||
EMAIL_BCC_PLACEHOLDER: "Default email BCC address"
|
||||
EMAIL_REPLY_TO: "Reply-to address"
|
||||
EMAIL_REPLY_TO_PLACEHOLDER: "Default email reply-to address"
|
||||
EMAIL_REPLY_TO_NAME: "Reply-to name"
|
||||
EMAIL_REPLY_TO_NAME_PLACEHOLDER: "Default email reply-to name"
|
||||
EMAIL_BODY: "Body"
|
||||
EMAIL_BODY_PLACEHOLDER: "Defaults to a table of all form fields"
|
||||
SMTP_SERVER: "SMTP server"
|
||||
SMTP_SERVER_PLACEHOLDER: "e.g. smtp.google.com"
|
||||
SMTP_PORT: "SMTP port"
|
||||
SMTP_PORT_PLACEHOLDER: "Defaults to 25 (plaintext) / 587 (encrypted)"
|
||||
SMTP_ENCRYPTION: "SMTP encryption"
|
||||
SMTP_ENCRYPTION_NONE: "None"
|
||||
SMTP_LOGIN_NAME: "SMTP login name"
|
||||
SMTP_PASSWORD: "SMTP password"
|
||||
SMTP_AUTH_MODE: "SMTP auth mode"
|
||||
PATH_TO_SENDMAIL: "Path to sendmail binary"
|
||||
DEBUG: "Debug"
|
||||
EMAIL_NOT_CONFIGURED: "Email not configured"
|
||||
PLEASE_CONFIGURE_A_TO_ADDRESS: "Please configure a 'to' address in the Email Plugin settings, or in the form"
|
||||
PLEASE_CONFIGURE_A_FROM_ADDRESS: "Please configure a 'from' address in the Email Plugin settings, or in the form"
|
||||
TEST_EMAIL_BODY: "<h1>Testing Email</h1><p>This test email has been sent based on the following configuration:</p> <p><pre>%1$s</pre></p>"
|
||||
EMAIL_DEFAULTS: "Email Defaults"
|
||||
SMTP_CONFIGURATION: "SMTP/S Configuration"
|
||||
SENDMAIL_CONFIGURATION: "Sendmail Configuration"
|
||||
ADVANCED: "Advanced"
|
||||
EMAIL_FOOTER: "GetGrav.org"
|
||||
QUEUE_TITLE: "Email Queue"
|
||||
QUEUE_DESC: "When you enable the email queue, email is not <i>sent immediately</i>, rather the email is sent to the queue, and then the Grav <strong>scheduler will flush the queue</strong> and actually send the email based on the configured frequency. This ensures Grav is not waiting around for email connections to complete."
|
||||
QUEUE_ENABLED: "Enabled"
|
||||
QUEUE_FLUSH_FREQUENCY: "Flush Frequency"
|
||||
QUEUE_FLUSH_FREQUENCY_HELP: "Use 'cron' format"
|
||||
QUEUE_FLUSH_MSG_LIMIT: "Messages per flush"
|
||||
QUEUE_FLUSH_MSG_LIMIT_APPEND: "Messages"
|
||||
QUEUE_FLUSH_TIME_LIMIT: "Flush time limit"
|
||||
QUEUE_FLUSH_TIME_LIMIT_APPEND: "Seconds"
|
||||
EMAIL_FORMAT: "Use the `addr` format: `email@address.org` or `name-addr` format: `Your Name <email@address.org>`. Comma separated for multiple addresses"
|
||||
|
||||
da:
|
||||
PLUGIN_EMAIL:
|
||||
PLEASE_CONFIGURE_A_TO_ADDRESS: "Konfigurere venligst en 'til' email adresse i Email Plugin indstillingerne eller her i formularen"
|
||||
PLEASE_CONFIGURE_A_FROM_ADDRESS: "Konfigurere venligst en 'fra' email adresse i Email Plugin indstillingerne eller her i formularen"
|
||||
|
||||
de:
|
||||
PLUGIN_EMAIL:
|
||||
EMAIL_NOT_CONFIGURED: "E-Mail ist nicht konfiguriert"
|
||||
PLEASE_CONFIGURE_A_TO_ADDRESS: "Bitte konfigurieren sie eine 'An' ('to') Adresse in den Email-Plugin-Einstellungen oder im Formular."
|
||||
PLEASE_CONFIGURE_A_FROM_ADDRESS: "Bitte konfigurieren sie eine 'Von' ('from') Adresse in den Email-Plugin-Einstellungen oder im Formular."
|
||||
|
||||
es:
|
||||
PLUGIN_EMAIL:
|
||||
PLEASE_CONFIGURE_A_TO_ADDRESS: "Por favor configura una dirección de 'remitente' en la configuración del Plugin de Email o en el formulario"
|
||||
PLEASE_CONFIGURE_A_FROM_ADDRESS: "Por favor configura una dirección de 'destinatario' en la configuración del Plugin de Email o en el formulario"
|
||||
|
||||
fr:
|
||||
PLUGIN_EMAIL:
|
||||
MAIL_ENGINE: "Moteur de messagerie"
|
||||
MAIL_ENGINE_DISABLED: "Désactivé"
|
||||
MAIL_ENGINE_DESC: "NOTE : Si vous sélectionnez un moteur fourni par un autre plugin, vous devez configurer les options dans ce même plugin."
|
||||
CONTENT_TYPE: "Type de contenu"
|
||||
CONTENT_TYPE_PLAIN_TEXT: "Texte brut"
|
||||
CHARSET: "Jeu de caractères"
|
||||
CHARSET_PLACEHOLDER: "Defaults to UTF-8"
|
||||
EMAIL_FORM: "Adresse de l’expéditeur"
|
||||
EMAIL_FORM_PLACEHOLDER: "Adresse e-mail par défaut de l’expéditeur"
|
||||
EMAIL_FROM_NAME: "Nom de l’expéditeur"
|
||||
EMAIL_FROM_NAME_PLACEHOLDER: "Nom par défaut de l’expéditeur"
|
||||
EMAIL_TO: "Adresse du destinataire"
|
||||
EMAIL_TO_PLACEHOLDER: "Adresse e-mail par défaut du destinataire"
|
||||
EMAIL_TO_NAME: "Nom du destinataire"
|
||||
EMAIL_TO_NAME_PLACEHOLDER: "Nom par défaut du destinataire"
|
||||
EMAIL_CC: "Adresse en CC"
|
||||
EMAIL_CC_PLACEHOLDER: "Adresse e-mail par défaut en CC"
|
||||
EMAIL_CC_NAME: "Nom en CC"
|
||||
EMAIL_CC_NAME_PLACEHOLDER: "Nom par défaut en CC"
|
||||
EMAIL_BCC: "Adresse en BCC"
|
||||
EMAIL_BCC_PLACEHOLDER: "Adresse e-mail par défaut en BCC"
|
||||
EMAIL_REPLY_TO: "Adresse de réponse"
|
||||
EMAIL_REPLY_TO_PLACEHOLDER: "Adresse e-mail par défaut pour la réponse"
|
||||
EMAIL_REPLY_TO_NAME: "Nom pour la réponse"
|
||||
EMAIL_REPLY_TO_NAME_PLACEHOLDER: "Nom par défaut pour la réponse"
|
||||
EMAIL_BODY: "Corps"
|
||||
EMAIL_BODY_PLACEHOLDER: "Table de tous les champs de formulaire par défaut"
|
||||
SMTP_SERVER: "Serveur SMTP"
|
||||
SMTP_SERVER_PLACEHOLDER: "ex. smtp.google.com"
|
||||
SMTP_PORT: "Port SMTP"
|
||||
SMTP_PORT_PLACEHOLDER: "25 (texte brut) / 587 (encrypté) par défaut"
|
||||
SMTP_ENCRYPTION: "cryptage SMTP"
|
||||
SMTP_ENCRYPTION_NONE: "Aucun"
|
||||
SMTP_LOGIN_NAME: "Nom d'utilisateur SMTP"
|
||||
SMTP_PASSWORD: "Mot de passe SMTP"
|
||||
SMTP_AUTH_MODE: "Mode d'authentification SMTP"
|
||||
PATH_TO_SENDMAIL: "Chemin vers sendmail"
|
||||
DEBUG: "Débogage"
|
||||
EMAIL_NOT_CONFIGURED: "L’e-mail n’est pas configuré"
|
||||
PLEASE_CONFIGURE_A_TO_ADDRESS: "Veuillez configurer une adresse de 'destinataire' dans les paramètres du plugin d’e-mail ou dans le formulaire."
|
||||
PLEASE_CONFIGURE_A_FROM_ADDRESS: "Veuillez configurer une adresse 'd’expéditeur' dans les paramètres du plugin d’e-mail ou dans le formulaire."
|
||||
TEST_EMAIL_BODY: "<h1>E-mail de test</h1><p>Cet e-mail de test est basé sur la configuration suivante :</p> <p><pre>%1$s</pre></p>"
|
||||
EMAIL_DEFAULTS: "E-mail par défaut"
|
||||
SMTP_CONFIGURATION: "Configuration SMTP"
|
||||
SENDMAIL_CONFIGURATION: "Configuration Sendmail"
|
||||
ADVANCED: "Avancée"
|
||||
EMAIL_FOOTER: "GetGrav.org"
|
||||
QUEUE_TITLE: "Fil d’attente d’e-mails"
|
||||
QUEUE_DESC: "Lorsque vous activez la file d’attente des e-mails, ils ne sont pas <i>envoyés immédiatement</i>. Au contraire, ils sont envoyés dans la file d’attente, puis le planificateur Grav <strong>videra la file d’attente</strong> et enverra les e-mails en fonction de la fréquence configurée. Cela permet de s’assurer que Grav n’attend pas la fin des connexions pour les envoyer"
|
||||
QUEUE_ENABLED: "Activé"
|
||||
QUEUE_FLUSH_FREQUENCY: "Fréquence de Flush"
|
||||
QUEUE_FLUSH_FREQUENCY_HELP: "Utiliser le format 'cron'"
|
||||
QUEUE_FLUSH_MSG_LIMIT: "Messages par Flush"
|
||||
QUEUE_FLUSH_MSG_LIMIT_APPEND: "Messages"
|
||||
QUEUE_FLUSH_TIME_LIMIT: "Délai de Flush"
|
||||
QUEUE_FLUSH_TIME_LIMIT_APPEND: "Secondes"
|
||||
EMAIL_FORMAT: "Utilisez le format `addr` : `email@adresse.org` ou le format `name-addr` : `Votre nom <email@adresse.org>`. Séparer par des virgules pour plusieurs adresses."
|
||||
|
||||
|
||||
hr:
|
||||
PLUGIN_EMAIL:
|
||||
EMAIL_NOT_CONFIGURED: "Email nije konfiguriran"
|
||||
PLEASE_CONFIGURE_A_TO_ADDRESS: "Konfigurirajte 'za' ('to') adresu u postavkama Email dodatka ili u obrascu"
|
||||
PLEASE_CONFIGURE_A_FROM_ADDRESS: "Konfigurirajte 'od' ('from') adresu u postavkama Email dodatka ili u obrascu"
|
||||
|
||||
it:
|
||||
PLUGIN_EMAIL:
|
||||
PLEASE_CONFIGURE_A_TO_ADDRESS: "Per favore, configura l'indirizzo di destinazione ('to') nella configurazione del Plugin Email, oppure direttamente nella form."
|
||||
PLEASE_CONFIGURE_A_FROM_ADDRESS: "Per favore, configura l'indirizzo di provenienza ('from') nella configurazione del Plugin Email, oppure direttamente nella form"
|
||||
|
||||
ro:
|
||||
PLUGIN_EMAIL:
|
||||
EMAIL_NOT_CONFIGURED: "Adresa de email nu este configurată"
|
||||
PLEASE_CONFIGURE_A_TO_ADDRESS: "Vă rugam setați o adresă 'către' în setările modulului Email sau în formular"
|
||||
PLEASE_CONFIGURE_A_FROM_ADDRESS: "Vă rugam setați o adresă 'de la' în setările modulului Email sau în formular"
|
||||
|
||||
ru:
|
||||
PLUGIN_EMAIL:
|
||||
MAIL_ENGINE: "Почтовая система"
|
||||
MAIL_ENGINE_DISABLED: "Отключена"
|
||||
CONTENT_TYPE: "Тип контента"
|
||||
CONTENT_TYPE_PLAIN_TEXT: "Простой текст"
|
||||
CHARSET: "Кодировка"
|
||||
CHARSET_PLACEHOLDER: "По умолчанию UTF-8"
|
||||
EMAIL_DEFAULTS: "Email настройки по умолчанию"
|
||||
EMAIL_FORM: "Почта отправителя"
|
||||
EMAIL_FORM_PLACEHOLDER: "Email адрес отправителя по умолчанию"
|
||||
EMAIL_FROM_NAME: "Имя почты отправителя"
|
||||
EMAIL_FROM_NAME_PLACEHOLDER: "Email имя отправителя по умолчанию"
|
||||
EMAIL_TO: "Почта получателя"
|
||||
EMAIL_TO_PLACEHOLDER: "Email адрес получателя по умолчанию"
|
||||
EMAIL_TO_NAME: "Имя почты получателя"
|
||||
EMAIL_TO_NAME_PLACEHOLDER: "Email имя получателя по умолчанию"
|
||||
EMAIL_CC: "Почта CC"
|
||||
EMAIL_CC_PLACEHOLDER: "Email CC адрес по умолчанию"
|
||||
EMAIL_CC_NAME: "Имя почты CC"
|
||||
EMAIL_CC_NAME_PLACEHOLDER: "Email CC имя по умолчанию"
|
||||
EMAIL_BCC: "Почта BCC"
|
||||
EMAIL_BCC_PLACEHOLDER: "Email BCC адрес по умолчанию"
|
||||
EMAIL_REPLY_TO: "Почта для ответов"
|
||||
EMAIL_REPLY_TO_PLACEHOLDER: "Email для ответов адрес по умолчанию"
|
||||
EMAIL_REPLY_TO_NAME: "Имя почты для ответов"
|
||||
EMAIL_REPLY_TO_NAME_PLACEHOLDER: "Email для ответов имя по умолчанию"
|
||||
EMAIL_BODY: "Тело сообщения"
|
||||
EMAIL_BODY_PLACEHOLDER: "По умолчанию используется таблица всех полей формы"
|
||||
SMTP_CONFIGURATION: "Конфигурация SMTP"
|
||||
SMTP_SERVER: "SMTP сервер"
|
||||
SMTP_SERVER_PLACEHOLDER: "e.g. smtp.google.com"
|
||||
SMTP_PORT: "SMTP порт"
|
||||
SMTP_PORT_PLACEHOLDER: "По умолчанию 25 (plaintext) / 587 (encrypted)"
|
||||
SMTP_ENCRYPTION: "SMTP шифрование"
|
||||
SMTP_ENCRYPTION_NONE: "Нет"
|
||||
SMTP_LOGIN_NAME: "SMTP логин"
|
||||
SMTP_PASSWORD: "SMTP пароль"
|
||||
SENDMAIL_CONFIGURATION: "Конфигурация Sendmail"
|
||||
PATH_TO_SENDMAIL: "Путь к sendmail"
|
||||
QUEUE_TITLE: "Очередь Email"
|
||||
QUEUE_DESC: "Когда вы включаете очередь email, электронная почта не <i>отправляется немедленно</i>, а отправляется в очередь, затем планировщик Grav <strong>обрабатывает очередь</strong> и на основе настроенной частоты фактически отправляет электронную почту. Это гарантирует, что Grav не ждет завершения подключения к электронной почте."
|
||||
QUEUE_ENABLED: "Включено"
|
||||
QUEUE_FLUSH_FREQUENCY: "Частота обработки"
|
||||
QUEUE_FLUSH_FREQUENCY_HELP: "Использовать формат 'cron'"
|
||||
QUEUE_FLUSH_MSG_LIMIT: "Количество сообщений на задачу"
|
||||
QUEUE_FLUSH_MSG_LIMIT_APPEND: "Сообщений"
|
||||
QUEUE_FLUSH_TIME_LIMIT: "Ограничение времени обработки"
|
||||
QUEUE_FLUSH_TIME_LIMIT_APPEND: "Секунд"
|
||||
ADVANCED: "Расширенные"
|
||||
DEBUG: "Отладка"
|
||||
EMAIL_NOT_CONFIGURED: "Электронная почта не настроена"
|
||||
PLEASE_CONFIGURE_A_TO_ADDRESS: "Пожалуйста настройте адрес получателя ('to') в настройках плагина Email Plugin, или на форме"
|
||||
PLEASE_CONFIGURE_A_FROM_ADDRESS: "Пожалуйста настройте адрес отправителя ('from') в настройках плагина Email Plugin, или на форме"
|
||||
TEST_EMAIL_BODY: "<h1>Тестирование электронной почты</h1><p>Это тестовое письмо отправлено на основе следующей конфигурации:</p> <p><pre>%1$s</pre></p>"
|
||||
EMAIL_FOOTER: "GetGrav.org"
|
||||
|
||||
uk:
|
||||
PLUGIN_EMAIL:
|
||||
EMAIL_NOT_CONFIGURED: "Електронна пошта не налаштована"
|
||||
PLEASE_CONFIGURE_A_TO_ADDRESS: "Будь ласка налаштуйте адресу одержувача ('to') в налаштуваннях плагіна Email Plugin, або на формі"
|
||||
PLEASE_CONFIGURE_A_FROM_ADDRESS: "Будь ласка налаштуйте адресу відправника ('from') в налаштуваннях плагіна Email Plugin, або на формі"
|
||||
TEST_EMAIL_BODY: "<h1>Тестування електронної пошти</h1><p>Це тестовий лист відправлено на основі такої конфігурації:</p> <p><pre>%1$s</pre></p>"
|
||||
EMAIL_FOOTER: "GetGrav.org"
|
||||
214
plugins/email/templates/email/base.html.twig
Normal file
214
plugins/email/templates/email/base.html.twig
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title></title>
|
||||
<style>
|
||||
/* -------------------------------------
|
||||
GLOBAL
|
||||
------------------------------------- */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif;
|
||||
font-size: 100%;
|
||||
line-height: 1.6;
|
||||
}
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
body {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-webkit-text-size-adjust: none;
|
||||
width: 100%!important;
|
||||
height: 100%;
|
||||
}
|
||||
/* -------------------------------------
|
||||
ELEMENTS
|
||||
------------------------------------- */
|
||||
a {
|
||||
color: #348eda;
|
||||
}
|
||||
.btn-primary {
|
||||
text-decoration: none;
|
||||
color: #FFF;
|
||||
background-color: #348eda;
|
||||
border: solid #348eda;
|
||||
border-width: 10px 20px;
|
||||
line-height: 2;
|
||||
font-weight: bold;
|
||||
margin-right: 10px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
border-radius: 25px;
|
||||
}
|
||||
.btn-secondary {
|
||||
text-decoration: none;
|
||||
color: #FFF;
|
||||
background-color: #aaa;
|
||||
border: solid #aaa;
|
||||
border-width: 10px 20px;
|
||||
line-height: 2;
|
||||
font-weight: bold;
|
||||
margin-right: 10px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
border-radius: 25px;
|
||||
}
|
||||
.last {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.first {
|
||||
margin-top: 0;
|
||||
}
|
||||
.padding {
|
||||
padding: 10px 0;
|
||||
}
|
||||
/* -------------------------------------
|
||||
BODY
|
||||
------------------------------------- */
|
||||
table.body-wrap {
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
}
|
||||
table.body-wrap .container {
|
||||
border-radius: 4px;
|
||||
}
|
||||
/* -------------------------------------
|
||||
FOOTER
|
||||
------------------------------------- */
|
||||
table.footer-wrap {
|
||||
width: 100%;
|
||||
clear: both!important;
|
||||
}
|
||||
.footer-wrap .container {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
|
||||
}
|
||||
table.footer-wrap a {
|
||||
color: #666;
|
||||
}
|
||||
/* -------------------------------------
|
||||
TYPOGRAPHY
|
||||
------------------------------------- */
|
||||
h1, h2, h3 {
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
|
||||
color: #000;
|
||||
margin: 40px 0 10px;
|
||||
line-height: 1.2;
|
||||
font-weight: 200;
|
||||
}
|
||||
h1 {
|
||||
font-size: 36px;
|
||||
}
|
||||
h2 {
|
||||
font-size: 28px;
|
||||
}
|
||||
h3 {
|
||||
font-size: 22px;
|
||||
}
|
||||
p, ul, ol {
|
||||
margin-bottom: 10px;
|
||||
font-weight: normal;
|
||||
font-size: 14px;
|
||||
}
|
||||
ul li, ol li {
|
||||
margin-left: 5px;
|
||||
list-style-position: inside;
|
||||
}
|
||||
.word-break {
|
||||
overflow-wrap: break-word;
|
||||
word-wrap: break-word;
|
||||
|
||||
-ms-word-break: break-all;
|
||||
/* This is the dangerous one in WebKit, as it breaks things wherever */
|
||||
word-break: break-all;
|
||||
/* Instead use this non-standard one: */
|
||||
word-break: break-word;
|
||||
|
||||
/* Adds a hyphen where the word breaks, if supported (No Blink) */
|
||||
-ms-hyphens: auto;
|
||||
-moz-hyphens: auto;
|
||||
-webkit-hyphens: auto;
|
||||
hyphens: auto;
|
||||
}
|
||||
/* ---------------------------------------------------
|
||||
RESPONSIVENESS
|
||||
Nuke it from orbit. It's the only way to be sure.
|
||||
------------------------------------------------------ */
|
||||
/* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */
|
||||
.container {
|
||||
display: block!important;
|
||||
max-width: 600px!important;
|
||||
margin: 0 auto!important; /* makes it centered */
|
||||
clear: both!important;
|
||||
}
|
||||
/* Set the padding on the td rather than the div for Outlook compatibility */
|
||||
.body-wrap .container {
|
||||
padding: 20px;
|
||||
}
|
||||
/* This should also be a block element, so that it will fill 100% of the .container */
|
||||
.content {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
}
|
||||
/* Let's make sure tables in the content area are 100% wide */
|
||||
.content table {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body bgcolor="#f6f6f6">
|
||||
|
||||
<!-- body -->
|
||||
<table class="body-wrap" bgcolor="#f6f6f6">
|
||||
<tr>
|
||||
<td></td>
|
||||
<td class="container" bgcolor="#FFFFFF">
|
||||
<div class="content">
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
{% block content %}
|
||||
{{ content|raw }}
|
||||
{% endblock content %}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</table>
|
||||
<!-- /body -->
|
||||
|
||||
<!-- footer -->
|
||||
<table class="footer-wrap">
|
||||
<tr>
|
||||
<td></td>
|
||||
<td class="container">
|
||||
<div class="content">
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center">
|
||||
{% block footer %}
|
||||
{{ 'PLUGIN_EMAIL.EMAIL_FOOTER'|t }}
|
||||
{% endblock footer %}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</table>
|
||||
<!-- /footer -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
12
plugins/email/vendor/autoload.php
vendored
Normal file
12
plugins/email/vendor/autoload.php
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
// autoload.php @generated by Composer
|
||||
|
||||
if (PHP_VERSION_ID < 50600) {
|
||||
echo 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
|
||||
return ComposerAutoloaderInit73924571ea6ee98bb12d10ff20aff2ab::getLoader();
|
||||
572
plugins/email/vendor/composer/ClassLoader.php
vendored
Normal file
572
plugins/email/vendor/composer/ClassLoader.php
vendored
Normal file
|
|
@ -0,0 +1,572 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
/**
|
||||
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
||||
*
|
||||
* $loader = new \Composer\Autoload\ClassLoader();
|
||||
*
|
||||
* // register classes with namespaces
|
||||
* $loader->add('Symfony\Component', __DIR__.'/component');
|
||||
* $loader->add('Symfony', __DIR__.'/framework');
|
||||
*
|
||||
* // activate the autoloader
|
||||
* $loader->register();
|
||||
*
|
||||
* // to enable searching the include path (eg. for PEAR packages)
|
||||
* $loader->setUseIncludePath(true);
|
||||
*
|
||||
* In this example, if you try to use a class in the Symfony\Component
|
||||
* namespace or one of its children (Symfony\Component\Console for instance),
|
||||
* the autoloader will first look for the class under the component/
|
||||
* directory, and it will then fallback to the framework/ directory if not
|
||||
* found before giving up.
|
||||
*
|
||||
* This class is loosely based on the Symfony UniversalClassLoader.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
* @see https://www.php-fig.org/psr/psr-0/
|
||||
* @see https://www.php-fig.org/psr/psr-4/
|
||||
*/
|
||||
class ClassLoader
|
||||
{
|
||||
/** @var ?string */
|
||||
private $vendorDir;
|
||||
|
||||
// PSR-4
|
||||
/**
|
||||
* @var array[]
|
||||
* @psalm-var array<string, array<string, int>>
|
||||
*/
|
||||
private $prefixLengthsPsr4 = array();
|
||||
/**
|
||||
* @var array[]
|
||||
* @psalm-var array<string, array<int, string>>
|
||||
*/
|
||||
private $prefixDirsPsr4 = array();
|
||||
/**
|
||||
* @var array[]
|
||||
* @psalm-var array<string, string>
|
||||
*/
|
||||
private $fallbackDirsPsr4 = array();
|
||||
|
||||
// PSR-0
|
||||
/**
|
||||
* @var array[]
|
||||
* @psalm-var array<string, array<string, string[]>>
|
||||
*/
|
||||
private $prefixesPsr0 = array();
|
||||
/**
|
||||
* @var array[]
|
||||
* @psalm-var array<string, string>
|
||||
*/
|
||||
private $fallbackDirsPsr0 = array();
|
||||
|
||||
/** @var bool */
|
||||
private $useIncludePath = false;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
* @psalm-var array<string, string>
|
||||
*/
|
||||
private $classMap = array();
|
||||
|
||||
/** @var bool */
|
||||
private $classMapAuthoritative = false;
|
||||
|
||||
/**
|
||||
* @var bool[]
|
||||
* @psalm-var array<string, bool>
|
||||
*/
|
||||
private $missingClasses = array();
|
||||
|
||||
/** @var ?string */
|
||||
private $apcuPrefix;
|
||||
|
||||
/**
|
||||
* @var self[]
|
||||
*/
|
||||
private static $registeredLoaders = array();
|
||||
|
||||
/**
|
||||
* @param ?string $vendorDir
|
||||
*/
|
||||
public function __construct($vendorDir = null)
|
||||
{
|
||||
$this->vendorDir = $vendorDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getPrefixes()
|
||||
{
|
||||
if (!empty($this->prefixesPsr0)) {
|
||||
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array[]
|
||||
* @psalm-return array<string, array<int, string>>
|
||||
*/
|
||||
public function getPrefixesPsr4()
|
||||
{
|
||||
return $this->prefixDirsPsr4;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array[]
|
||||
* @psalm-return array<string, string>
|
||||
*/
|
||||
public function getFallbackDirs()
|
||||
{
|
||||
return $this->fallbackDirsPsr0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array[]
|
||||
* @psalm-return array<string, string>
|
||||
*/
|
||||
public function getFallbackDirsPsr4()
|
||||
{
|
||||
return $this->fallbackDirsPsr4;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[] Array of classname => path
|
||||
* @psalm-return array<string, string>
|
||||
*/
|
||||
public function getClassMap()
|
||||
{
|
||||
return $this->classMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $classMap Class to filename map
|
||||
* @psalm-param array<string, string> $classMap
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addClassMap(array $classMap)
|
||||
{
|
||||
if ($this->classMap) {
|
||||
$this->classMap = array_merge($this->classMap, $classMap);
|
||||
} else {
|
||||
$this->classMap = $classMap;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix, either
|
||||
* appending or prepending to the ones previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param string[]|string $paths The PSR-0 root directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
(array) $paths,
|
||||
$this->fallbackDirsPsr0
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
$this->fallbackDirsPsr0,
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$first = $prefix[0];
|
||||
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
||||
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
|
||||
|
||||
return;
|
||||
}
|
||||
if ($prepend) {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
(array) $paths,
|
||||
$this->prefixesPsr0[$first][$prefix]
|
||||
);
|
||||
} else {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
$this->prefixesPsr0[$first][$prefix],
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace, either
|
||||
* appending or prepending to the ones previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param string[]|string $paths The PSR-4 base directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addPsr4($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
// Register directories for the root namespace.
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
(array) $paths,
|
||||
$this->fallbackDirsPsr4
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
$this->fallbackDirsPsr4,
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
||||
// Register directories for a new namespace.
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
} elseif ($prepend) {
|
||||
// Prepend directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
(array) $paths,
|
||||
$this->prefixDirsPsr4[$prefix]
|
||||
);
|
||||
} else {
|
||||
// Append directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
$this->prefixDirsPsr4[$prefix],
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix,
|
||||
* replacing any others previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param string[]|string $paths The PSR-0 base directories
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr0 = (array) $paths;
|
||||
} else {
|
||||
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace,
|
||||
* replacing any others previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param string[]|string $paths The PSR-4 base directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setPsr4($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr4 = (array) $paths;
|
||||
} else {
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns on searching the include path for class files.
|
||||
*
|
||||
* @param bool $useIncludePath
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setUseIncludePath($useIncludePath)
|
||||
{
|
||||
$this->useIncludePath = $useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used to check if the autoloader uses the include path to check
|
||||
* for classes.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getUseIncludePath()
|
||||
{
|
||||
return $this->useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns off searching the prefix and fallback directories for classes
|
||||
* that have not been registered with the class map.
|
||||
*
|
||||
* @param bool $classMapAuthoritative
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setClassMapAuthoritative($classMapAuthoritative)
|
||||
{
|
||||
$this->classMapAuthoritative = $classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should class lookup fail if not found in the current class map?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isClassMapAuthoritative()
|
||||
{
|
||||
return $this->classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
||||
*
|
||||
* @param string|null $apcuPrefix
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setApcuPrefix($apcuPrefix)
|
||||
{
|
||||
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The APCu prefix in use, or null if APCu caching is not enabled.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getApcuPrefix()
|
||||
{
|
||||
return $this->apcuPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers this instance as an autoloader.
|
||||
*
|
||||
* @param bool $prepend Whether to prepend the autoloader or not
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register($prepend = false)
|
||||
{
|
||||
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
||||
|
||||
if (null === $this->vendorDir) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($prepend) {
|
||||
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
|
||||
} else {
|
||||
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||
self::$registeredLoaders[$this->vendorDir] = $this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters this instance as an autoloader.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function unregister()
|
||||
{
|
||||
spl_autoload_unregister(array($this, 'loadClass'));
|
||||
|
||||
if (null !== $this->vendorDir) {
|
||||
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given class or interface.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
* @return true|null True if loaded, null otherwise
|
||||
*/
|
||||
public function loadClass($class)
|
||||
{
|
||||
if ($file = $this->findFile($class)) {
|
||||
includeFile($file);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the path to the file where the class is defined.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
*
|
||||
* @return string|false The path if found, false otherwise
|
||||
*/
|
||||
public function findFile($class)
|
||||
{
|
||||
// class map lookup
|
||||
if (isset($this->classMap[$class])) {
|
||||
return $this->classMap[$class];
|
||||
}
|
||||
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
||||
return false;
|
||||
}
|
||||
if (null !== $this->apcuPrefix) {
|
||||
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
||||
if ($hit) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
$file = $this->findFileWithExtension($class, '.php');
|
||||
|
||||
// Search for Hack files if we are running on HHVM
|
||||
if (false === $file && defined('HHVM_VERSION')) {
|
||||
$file = $this->findFileWithExtension($class, '.hh');
|
||||
}
|
||||
|
||||
if (null !== $this->apcuPrefix) {
|
||||
apcu_add($this->apcuPrefix.$class, $file);
|
||||
}
|
||||
|
||||
if (false === $file) {
|
||||
// Remember that this class does not exist.
|
||||
$this->missingClasses[$class] = true;
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently registered loaders indexed by their corresponding vendor directories.
|
||||
*
|
||||
* @return self[]
|
||||
*/
|
||||
public static function getRegisteredLoaders()
|
||||
{
|
||||
return self::$registeredLoaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $class
|
||||
* @param string $ext
|
||||
* @return string|false
|
||||
*/
|
||||
private function findFileWithExtension($class, $ext)
|
||||
{
|
||||
// PSR-4 lookup
|
||||
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
||||
|
||||
$first = $class[0];
|
||||
if (isset($this->prefixLengthsPsr4[$first])) {
|
||||
$subPath = $class;
|
||||
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
||||
$subPath = substr($subPath, 0, $lastPos);
|
||||
$search = $subPath . '\\';
|
||||
if (isset($this->prefixDirsPsr4[$search])) {
|
||||
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
||||
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
||||
if (file_exists($file = $dir . $pathEnd)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-4 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr4 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 lookup
|
||||
if (false !== $pos = strrpos($class, '\\')) {
|
||||
// namespaced class name
|
||||
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
||||
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
||||
} else {
|
||||
// PEAR-like class name
|
||||
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
||||
}
|
||||
|
||||
if (isset($this->prefixesPsr0[$first])) {
|
||||
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
||||
if (0 === strpos($class, $prefix)) {
|
||||
foreach ($dirs as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr0 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 include paths.
|
||||
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope isolated include.
|
||||
*
|
||||
* Prevents access to $this/self from included files.
|
||||
*
|
||||
* @param string $file
|
||||
* @return void
|
||||
* @private
|
||||
*/
|
||||
function includeFile($file)
|
||||
{
|
||||
include $file;
|
||||
}
|
||||
352
plugins/email/vendor/composer/InstalledVersions.php
vendored
Normal file
352
plugins/email/vendor/composer/InstalledVersions.php
vendored
Normal file
|
|
@ -0,0 +1,352 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer;
|
||||
|
||||
use Composer\Autoload\ClassLoader;
|
||||
use Composer\Semver\VersionParser;
|
||||
|
||||
/**
|
||||
* This class is copied in every Composer installed project and available to all
|
||||
*
|
||||
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
|
||||
*
|
||||
* To require its presence, you can require `composer-runtime-api ^2.0`
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class InstalledVersions
|
||||
{
|
||||
/**
|
||||
* @var mixed[]|null
|
||||
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
|
||||
*/
|
||||
private static $installed;
|
||||
|
||||
/**
|
||||
* @var bool|null
|
||||
*/
|
||||
private static $canGetVendors;
|
||||
|
||||
/**
|
||||
* @var array[]
|
||||
* @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||
*/
|
||||
private static $installedByVendor = array();
|
||||
|
||||
/**
|
||||
* Returns a list of all package names which are present, either by being installed, replaced or provided
|
||||
*
|
||||
* @return string[]
|
||||
* @psalm-return list<string>
|
||||
*/
|
||||
public static function getInstalledPackages()
|
||||
{
|
||||
$packages = array();
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
$packages[] = array_keys($installed['versions']);
|
||||
}
|
||||
|
||||
if (1 === \count($packages)) {
|
||||
return $packages[0];
|
||||
}
|
||||
|
||||
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all package names with a specific type e.g. 'library'
|
||||
*
|
||||
* @param string $type
|
||||
* @return string[]
|
||||
* @psalm-return list<string>
|
||||
*/
|
||||
public static function getInstalledPackagesByType($type)
|
||||
{
|
||||
$packagesByType = array();
|
||||
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
foreach ($installed['versions'] as $name => $package) {
|
||||
if (isset($package['type']) && $package['type'] === $type) {
|
||||
$packagesByType[] = $name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $packagesByType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given package is installed
|
||||
*
|
||||
* This also returns true if the package name is provided or replaced by another package
|
||||
*
|
||||
* @param string $packageName
|
||||
* @param bool $includeDevRequirements
|
||||
* @return bool
|
||||
*/
|
||||
public static function isInstalled($packageName, $includeDevRequirements = true)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (isset($installed['versions'][$packageName])) {
|
||||
return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given package satisfies a version constraint
|
||||
*
|
||||
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
|
||||
*
|
||||
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
|
||||
*
|
||||
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
|
||||
* @param string $packageName
|
||||
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
|
||||
* @return bool
|
||||
*/
|
||||
public static function satisfies(VersionParser $parser, $packageName, $constraint)
|
||||
{
|
||||
$constraint = $parser->parseConstraints($constraint);
|
||||
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
|
||||
|
||||
return $provided->matches($constraint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a version constraint representing all the range(s) which are installed for a given package
|
||||
*
|
||||
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
|
||||
* whether a given version of a package is installed, and not just whether it exists
|
||||
*
|
||||
* @param string $packageName
|
||||
* @return string Version constraint usable with composer/semver
|
||||
*/
|
||||
public static function getVersionRanges($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$ranges = array();
|
||||
if (isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
|
||||
}
|
||||
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
|
||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
|
||||
}
|
||||
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
|
||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
|
||||
}
|
||||
if (array_key_exists('provided', $installed['versions'][$packageName])) {
|
||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
|
||||
}
|
||||
|
||||
return implode(' || ', $ranges);
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||
*/
|
||||
public static function getVersion($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($installed['versions'][$packageName]['version'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $installed['versions'][$packageName]['version'];
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||
*/
|
||||
public static function getPrettyVersion($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $installed['versions'][$packageName]['pretty_version'];
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
|
||||
*/
|
||||
public static function getReference($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($installed['versions'][$packageName]['reference'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $installed['versions'][$packageName]['reference'];
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
|
||||
*/
|
||||
public static function getInstallPath($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
|
||||
*/
|
||||
public static function getRootPackage()
|
||||
{
|
||||
$installed = self::getInstalled();
|
||||
|
||||
return $installed[0]['root'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw installed.php data for custom implementations
|
||||
*
|
||||
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
|
||||
* @return array[]
|
||||
* @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
|
||||
*/
|
||||
public static function getRawData()
|
||||
{
|
||||
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
|
||||
|
||||
if (null === self::$installed) {
|
||||
// only require the installed.php file if this file is loaded from its dumped location,
|
||||
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||
if (substr(__DIR__, -8, 1) !== 'C') {
|
||||
self::$installed = include __DIR__ . '/installed.php';
|
||||
} else {
|
||||
self::$installed = array();
|
||||
}
|
||||
}
|
||||
|
||||
return self::$installed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw data of all installed.php which are currently loaded for custom implementations
|
||||
*
|
||||
* @return array[]
|
||||
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||
*/
|
||||
public static function getAllRawData()
|
||||
{
|
||||
return self::getInstalled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Lets you reload the static array from another file
|
||||
*
|
||||
* This is only useful for complex integrations in which a project needs to use
|
||||
* this class but then also needs to execute another project's autoloader in process,
|
||||
* and wants to ensure both projects have access to their version of installed.php.
|
||||
*
|
||||
* A typical case would be PHPUnit, where it would need to make sure it reads all
|
||||
* the data it needs from this class, then call reload() with
|
||||
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
|
||||
* the project in which it runs can then also use this class safely, without
|
||||
* interference between PHPUnit's dependencies and the project's dependencies.
|
||||
*
|
||||
* @param array[] $data A vendor/composer/installed.php data set
|
||||
* @return void
|
||||
*
|
||||
* @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
|
||||
*/
|
||||
public static function reload($data)
|
||||
{
|
||||
self::$installed = $data;
|
||||
self::$installedByVendor = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array[]
|
||||
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||
*/
|
||||
private static function getInstalled()
|
||||
{
|
||||
if (null === self::$canGetVendors) {
|
||||
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
|
||||
}
|
||||
|
||||
$installed = array();
|
||||
|
||||
if (self::$canGetVendors) {
|
||||
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
|
||||
if (isset(self::$installedByVendor[$vendorDir])) {
|
||||
$installed[] = self::$installedByVendor[$vendorDir];
|
||||
} elseif (is_file($vendorDir.'/composer/installed.php')) {
|
||||
$installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
|
||||
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
|
||||
self::$installed = $installed[count($installed) - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (null === self::$installed) {
|
||||
// only require the installed.php file if this file is loaded from its dumped location,
|
||||
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||
if (substr(__DIR__, -8, 1) !== 'C') {
|
||||
self::$installed = require __DIR__ . '/installed.php';
|
||||
} else {
|
||||
self::$installed = array();
|
||||
}
|
||||
}
|
||||
$installed[] = self::$installed;
|
||||
|
||||
return $installed;
|
||||
}
|
||||
}
|
||||
21
plugins/email/vendor/composer/LICENSE
vendored
Normal file
21
plugins/email/vendor/composer/LICENSE
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
Copyright (c) Nils Adermann, Jordi Boggiano
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
17
plugins/email/vendor/composer/autoload_classmap.php
vendored
Normal file
17
plugins/email/vendor/composer/autoload_classmap.php
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
// autoload_classmap.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'Attribute' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
|
||||
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
||||
'Grav\\Plugin\\EmailPlugin' => $baseDir . '/email.php',
|
||||
'Normalizer' => $vendorDir . '/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php',
|
||||
'PhpToken' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
|
||||
'Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
|
||||
'UnhandledMatchError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
|
||||
'ValueError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
|
||||
);
|
||||
13
plugins/email/vendor/composer/autoload_files.php
vendored
Normal file
13
plugins/email/vendor/composer/autoload_files.php
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
// autoload_files.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
|
||||
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
|
||||
'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
|
||||
'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php',
|
||||
);
|
||||
9
plugins/email/vendor/composer/autoload_namespaces.php
vendored
Normal file
9
plugins/email/vendor/composer/autoload_namespaces.php
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
// autoload_namespaces.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
);
|
||||
28
plugins/email/vendor/composer/autoload_psr4.php
vendored
Normal file
28
plugins/email/vendor/composer/autoload_psr4.php
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
// autoload_psr4.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'Symfony\\Polyfill\\Php80\\' => array($vendorDir . '/symfony/polyfill-php80'),
|
||||
'Symfony\\Polyfill\\Intl\\Normalizer\\' => array($vendorDir . '/symfony/polyfill-intl-normalizer'),
|
||||
'Symfony\\Polyfill\\Intl\\Idn\\' => array($vendorDir . '/symfony/polyfill-intl-idn'),
|
||||
'Symfony\\Contracts\\Service\\' => array($vendorDir . '/symfony/service-contracts'),
|
||||
'Symfony\\Contracts\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher-contracts'),
|
||||
'Symfony\\Component\\Mime\\' => array($vendorDir . '/symfony/mime'),
|
||||
'Symfony\\Component\\Messenger\\Bridge\\Redis\\' => array($vendorDir . '/symfony/redis-messenger'),
|
||||
'Symfony\\Component\\Messenger\\Bridge\\Doctrine\\' => array($vendorDir . '/symfony/doctrine-messenger'),
|
||||
'Symfony\\Component\\Messenger\\Bridge\\Amqp\\' => array($vendorDir . '/symfony/amqp-messenger'),
|
||||
'Symfony\\Component\\Messenger\\' => array($vendorDir . '/symfony/messenger'),
|
||||
'Symfony\\Component\\Mailer\\' => array($vendorDir . '/symfony/mailer'),
|
||||
'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'),
|
||||
'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
|
||||
'Psr\\EventDispatcher\\' => array($vendorDir . '/psr/event-dispatcher/src'),
|
||||
'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
|
||||
'Grav\\Plugin\\Email\\' => array($baseDir . '/classes'),
|
||||
'Grav\\Plugin\\Console\\' => array($baseDir . '/cli'),
|
||||
'Egulias\\EmailValidator\\' => array($vendorDir . '/egulias/email-validator/src'),
|
||||
'Doctrine\\Common\\Lexer\\' => array($vendorDir . '/doctrine/lexer/lib/Doctrine/Common/Lexer'),
|
||||
);
|
||||
57
plugins/email/vendor/composer/autoload_real.php
vendored
Normal file
57
plugins/email/vendor/composer/autoload_real.php
vendored
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
// autoload_real.php @generated by Composer
|
||||
|
||||
class ComposerAutoloaderInit73924571ea6ee98bb12d10ff20aff2ab
|
||||
{
|
||||
private static $loader;
|
||||
|
||||
public static function loadClassLoader($class)
|
||||
{
|
||||
if ('Composer\Autoload\ClassLoader' === $class) {
|
||||
require __DIR__ . '/ClassLoader.php';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Composer\Autoload\ClassLoader
|
||||
*/
|
||||
public static function getLoader()
|
||||
{
|
||||
if (null !== self::$loader) {
|
||||
return self::$loader;
|
||||
}
|
||||
|
||||
require __DIR__ . '/platform_check.php';
|
||||
|
||||
spl_autoload_register(array('ComposerAutoloaderInit73924571ea6ee98bb12d10ff20aff2ab', 'loadClassLoader'), true, true);
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInit73924571ea6ee98bb12d10ff20aff2ab', 'loadClassLoader'));
|
||||
|
||||
require __DIR__ . '/autoload_static.php';
|
||||
call_user_func(\Composer\Autoload\ComposerStaticInit73924571ea6ee98bb12d10ff20aff2ab::getInitializer($loader));
|
||||
|
||||
$loader->register(true);
|
||||
|
||||
$includeFiles = \Composer\Autoload\ComposerStaticInit73924571ea6ee98bb12d10ff20aff2ab::$files;
|
||||
foreach ($includeFiles as $fileIdentifier => $file) {
|
||||
composerRequire73924571ea6ee98bb12d10ff20aff2ab($fileIdentifier, $file);
|
||||
}
|
||||
|
||||
return $loader;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fileIdentifier
|
||||
* @param string $file
|
||||
* @return void
|
||||
*/
|
||||
function composerRequire73924571ea6ee98bb12d10ff20aff2ab($fileIdentifier, $file)
|
||||
{
|
||||
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
||||
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
|
||||
|
||||
require $file;
|
||||
}
|
||||
}
|
||||
152
plugins/email/vendor/composer/autoload_static.php
vendored
Normal file
152
plugins/email/vendor/composer/autoload_static.php
vendored
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
<?php
|
||||
|
||||
// autoload_static.php @generated by Composer
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
class ComposerStaticInit73924571ea6ee98bb12d10ff20aff2ab
|
||||
{
|
||||
public static $files = array (
|
||||
'6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
|
||||
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
|
||||
'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
|
||||
'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php',
|
||||
);
|
||||
|
||||
public static $prefixLengthsPsr4 = array (
|
||||
'S' =>
|
||||
array (
|
||||
'Symfony\\Polyfill\\Php80\\' => 23,
|
||||
'Symfony\\Polyfill\\Intl\\Normalizer\\' => 33,
|
||||
'Symfony\\Polyfill\\Intl\\Idn\\' => 26,
|
||||
'Symfony\\Contracts\\Service\\' => 26,
|
||||
'Symfony\\Contracts\\EventDispatcher\\' => 34,
|
||||
'Symfony\\Component\\Mime\\' => 23,
|
||||
'Symfony\\Component\\Messenger\\Bridge\\Redis\\' => 41,
|
||||
'Symfony\\Component\\Messenger\\Bridge\\Doctrine\\' => 44,
|
||||
'Symfony\\Component\\Messenger\\Bridge\\Amqp\\' => 40,
|
||||
'Symfony\\Component\\Messenger\\' => 28,
|
||||
'Symfony\\Component\\Mailer\\' => 25,
|
||||
'Symfony\\Component\\EventDispatcher\\' => 34,
|
||||
),
|
||||
'P' =>
|
||||
array (
|
||||
'Psr\\Log\\' => 8,
|
||||
'Psr\\EventDispatcher\\' => 20,
|
||||
'Psr\\Container\\' => 14,
|
||||
),
|
||||
'G' =>
|
||||
array (
|
||||
'Grav\\Plugin\\Email\\' => 18,
|
||||
'Grav\\Plugin\\Console\\' => 20,
|
||||
),
|
||||
'E' =>
|
||||
array (
|
||||
'Egulias\\EmailValidator\\' => 23,
|
||||
),
|
||||
'D' =>
|
||||
array (
|
||||
'Doctrine\\Common\\Lexer\\' => 22,
|
||||
),
|
||||
);
|
||||
|
||||
public static $prefixDirsPsr4 = array (
|
||||
'Symfony\\Polyfill\\Php80\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-php80',
|
||||
),
|
||||
'Symfony\\Polyfill\\Intl\\Normalizer\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer',
|
||||
),
|
||||
'Symfony\\Polyfill\\Intl\\Idn\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-intl-idn',
|
||||
),
|
||||
'Symfony\\Contracts\\Service\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/service-contracts',
|
||||
),
|
||||
'Symfony\\Contracts\\EventDispatcher\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/event-dispatcher-contracts',
|
||||
),
|
||||
'Symfony\\Component\\Mime\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/mime',
|
||||
),
|
||||
'Symfony\\Component\\Messenger\\Bridge\\Redis\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/redis-messenger',
|
||||
),
|
||||
'Symfony\\Component\\Messenger\\Bridge\\Doctrine\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/doctrine-messenger',
|
||||
),
|
||||
'Symfony\\Component\\Messenger\\Bridge\\Amqp\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/amqp-messenger',
|
||||
),
|
||||
'Symfony\\Component\\Messenger\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/messenger',
|
||||
),
|
||||
'Symfony\\Component\\Mailer\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/mailer',
|
||||
),
|
||||
'Symfony\\Component\\EventDispatcher\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/event-dispatcher',
|
||||
),
|
||||
'Psr\\Log\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/psr/log/Psr/Log',
|
||||
),
|
||||
'Psr\\EventDispatcher\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/psr/event-dispatcher/src',
|
||||
),
|
||||
'Psr\\Container\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/psr/container/src',
|
||||
),
|
||||
'Grav\\Plugin\\Email\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/../..' . '/classes',
|
||||
),
|
||||
'Grav\\Plugin\\Console\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/../..' . '/cli',
|
||||
),
|
||||
'Egulias\\EmailValidator\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/egulias/email-validator/src',
|
||||
),
|
||||
'Doctrine\\Common\\Lexer\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/doctrine/lexer/lib/Doctrine/Common/Lexer',
|
||||
),
|
||||
);
|
||||
|
||||
public static $classMap = array (
|
||||
'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
|
||||
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
|
||||
'Grav\\Plugin\\EmailPlugin' => __DIR__ . '/../..' . '/email.php',
|
||||
'Normalizer' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php',
|
||||
'PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
|
||||
'Stringable' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
|
||||
'UnhandledMatchError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
|
||||
'ValueError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
|
||||
);
|
||||
|
||||
public static function getInitializer(ClassLoader $loader)
|
||||
{
|
||||
return \Closure::bind(function () use ($loader) {
|
||||
$loader->prefixLengthsPsr4 = ComposerStaticInit73924571ea6ee98bb12d10ff20aff2ab::$prefixLengthsPsr4;
|
||||
$loader->prefixDirsPsr4 = ComposerStaticInit73924571ea6ee98bb12d10ff20aff2ab::$prefixDirsPsr4;
|
||||
$loader->classMap = ComposerStaticInit73924571ea6ee98bb12d10ff20aff2ab::$classMap;
|
||||
|
||||
}, null, ClassLoader::class);
|
||||
}
|
||||
}
|
||||
1378
plugins/email/vendor/composer/installed.json
vendored
Normal file
1378
plugins/email/vendor/composer/installed.json
vendored
Normal file
File diff suppressed because it is too large
Load diff
215
plugins/email/vendor/composer/installed.php
vendored
Normal file
215
plugins/email/vendor/composer/installed.php
vendored
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
<?php return array(
|
||||
'root' => array(
|
||||
'name' => 'getgrav/grav-plugin-email',
|
||||
'pretty_version' => 'dev-develop',
|
||||
'version' => 'dev-develop',
|
||||
'reference' => 'f6eb15486887c489363e6bf79fa49ea4a3d833c6',
|
||||
'type' => 'grav-plugin',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
'dev' => true,
|
||||
),
|
||||
'versions' => array(
|
||||
'doctrine/lexer' => array(
|
||||
'pretty_version' => '1.2.3',
|
||||
'version' => '1.2.3.0',
|
||||
'reference' => 'c268e882d4dbdd85e36e4ad69e02dc284f89d229',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../doctrine/lexer',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'egulias/email-validator' => array(
|
||||
'pretty_version' => '3.2.1',
|
||||
'version' => '3.2.1.0',
|
||||
'reference' => 'f88dcf4b14af14a98ad96b14b2b317969eab6715',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../egulias/email-validator',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'getgrav/grav-plugin-email' => array(
|
||||
'pretty_version' => 'dev-develop',
|
||||
'version' => 'dev-develop',
|
||||
'reference' => 'f6eb15486887c489363e6bf79fa49ea4a3d833c6',
|
||||
'type' => 'grav-plugin',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'psr/container' => array(
|
||||
'pretty_version' => '1.1.1',
|
||||
'version' => '1.1.1.0',
|
||||
'reference' => '8622567409010282b7aeebe4bb841fe98b58dcaf',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../psr/container',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'psr/event-dispatcher' => array(
|
||||
'pretty_version' => '1.0.0',
|
||||
'version' => '1.0.0.0',
|
||||
'reference' => 'dbefd12671e8a14ec7f180cab83036ed26714bb0',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../psr/event-dispatcher',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'psr/event-dispatcher-implementation' => array(
|
||||
'dev_requirement' => false,
|
||||
'provided' => array(
|
||||
0 => '1.0',
|
||||
),
|
||||
),
|
||||
'psr/log' => array(
|
||||
'pretty_version' => '1.1.4',
|
||||
'version' => '1.1.4.0',
|
||||
'reference' => 'd49695b909c3b7628b6289db5479a1c204601f11',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../psr/log',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/amqp-messenger' => array(
|
||||
'pretty_version' => 'v5.4.13',
|
||||
'version' => '5.4.13.0',
|
||||
'reference' => 'def93f2a7841cfa1a4a1fa487b84054d0d53e521',
|
||||
'type' => 'symfony-messenger-bridge',
|
||||
'install_path' => __DIR__ . '/../symfony/amqp-messenger',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/deprecation-contracts' => array(
|
||||
'pretty_version' => 'v2.5.2',
|
||||
'version' => '2.5.2.0',
|
||||
'reference' => 'e8b495ea28c1d97b5e0c121748d6f9b53d075c66',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/deprecation-contracts',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/doctrine-messenger' => array(
|
||||
'pretty_version' => 'v5.4.12',
|
||||
'version' => '5.4.12.0',
|
||||
'reference' => '7649a80e917b47c5072480a2d763c2422da239d2',
|
||||
'type' => 'symfony-messenger-bridge',
|
||||
'install_path' => __DIR__ . '/../symfony/doctrine-messenger',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/event-dispatcher' => array(
|
||||
'pretty_version' => 'v5.4.9',
|
||||
'version' => '5.4.9.0',
|
||||
'reference' => '8e6ce1cc0279e3ff3c8ff0f43813bc88d21ca1bc',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/event-dispatcher',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/event-dispatcher-contracts' => array(
|
||||
'pretty_version' => 'v2.5.2',
|
||||
'version' => '2.5.2.0',
|
||||
'reference' => 'f98b54df6ad059855739db6fcbc2d36995283fe1',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/event-dispatcher-contracts',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/event-dispatcher-implementation' => array(
|
||||
'dev_requirement' => false,
|
||||
'provided' => array(
|
||||
0 => '2.0',
|
||||
),
|
||||
),
|
||||
'symfony/mailer' => array(
|
||||
'pretty_version' => 'v5.4.13',
|
||||
'version' => '5.4.13.0',
|
||||
'reference' => '63bf36a5150ac0bfed1c4d0a4e0b114a57b77e11',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/mailer',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/messenger' => array(
|
||||
'pretty_version' => 'v5.4.13',
|
||||
'version' => '5.4.13.0',
|
||||
'reference' => '8f8d3425991e627902f8288088609a8d8f6c6ea4',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/messenger',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/mime' => array(
|
||||
'pretty_version' => 'v5.4.13',
|
||||
'version' => '5.4.13.0',
|
||||
'reference' => 'bb2ccf759e2b967dcd11bdee5bdf30dddd2290bd',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/mime',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/polyfill-iconv' => array(
|
||||
'dev_requirement' => false,
|
||||
'replaced' => array(
|
||||
0 => '*',
|
||||
),
|
||||
),
|
||||
'symfony/polyfill-intl-idn' => array(
|
||||
'pretty_version' => 'v1.26.0',
|
||||
'version' => '1.26.0.0',
|
||||
'reference' => '59a8d271f00dd0e4c2e518104cc7963f655a1aa8',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-intl-idn',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/polyfill-intl-normalizer' => array(
|
||||
'pretty_version' => 'v1.26.0',
|
||||
'version' => '1.26.0.0',
|
||||
'reference' => '219aa369ceff116e673852dce47c3a41794c14bd',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-intl-normalizer',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/polyfill-mbstring' => array(
|
||||
'dev_requirement' => false,
|
||||
'replaced' => array(
|
||||
0 => '*',
|
||||
),
|
||||
),
|
||||
'symfony/polyfill-php72' => array(
|
||||
'dev_requirement' => false,
|
||||
'replaced' => array(
|
||||
0 => '*',
|
||||
),
|
||||
),
|
||||
'symfony/polyfill-php80' => array(
|
||||
'pretty_version' => 'v1.26.0',
|
||||
'version' => '1.26.0.0',
|
||||
'reference' => 'cfa0ae98841b9e461207c13ab093d76b0fa7bace',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-php80',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/redis-messenger' => array(
|
||||
'pretty_version' => 'v5.4.13',
|
||||
'version' => '5.4.13.0',
|
||||
'reference' => 'd3028b772de91e9aa0342c92ff71c77b130ac9c4',
|
||||
'type' => 'symfony-messenger-bridge',
|
||||
'install_path' => __DIR__ . '/../symfony/redis-messenger',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/service-contracts' => array(
|
||||
'pretty_version' => 'v2.5.2',
|
||||
'version' => '2.5.2.0',
|
||||
'reference' => '4b426aac47d6427cc1a1d0f7e2ac724627f5966c',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/service-contracts',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
),
|
||||
);
|
||||
26
plugins/email/vendor/composer/platform_check.php
vendored
Normal file
26
plugins/email/vendor/composer/platform_check.php
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
// platform_check.php @generated by Composer
|
||||
|
||||
$issues = array();
|
||||
|
||||
if (!(PHP_VERSION_ID >= 70306)) {
|
||||
$issues[] = 'Your Composer dependencies require a PHP version ">= 7.3.6". You are running ' . PHP_VERSION . '.';
|
||||
}
|
||||
|
||||
if ($issues) {
|
||||
if (!headers_sent()) {
|
||||
header('HTTP/1.1 500 Internal Server Error');
|
||||
}
|
||||
if (!ini_get('display_errors')) {
|
||||
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
||||
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
|
||||
} elseif (!headers_sent()) {
|
||||
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
|
||||
}
|
||||
}
|
||||
trigger_error(
|
||||
'Composer detected issues in your platform: ' . implode(' ', $issues),
|
||||
E_USER_ERROR
|
||||
);
|
||||
}
|
||||
19
plugins/email/vendor/doctrine/lexer/LICENSE
vendored
Normal file
19
plugins/email/vendor/doctrine/lexer/LICENSE
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2006-2018 Doctrine Project
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
9
plugins/email/vendor/doctrine/lexer/README.md
vendored
Normal file
9
plugins/email/vendor/doctrine/lexer/README.md
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
# Doctrine Lexer
|
||||
|
||||
[](https://github.com/doctrine/lexer/actions)
|
||||
|
||||
Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.
|
||||
|
||||
This lexer is used in Doctrine Annotations and in Doctrine ORM (DQL).
|
||||
|
||||
https://www.doctrine-project.org/projects/lexer.html
|
||||
41
plugins/email/vendor/doctrine/lexer/composer.json
vendored
Normal file
41
plugins/email/vendor/doctrine/lexer/composer.json
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"name": "doctrine/lexer",
|
||||
"type": "library",
|
||||
"description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.",
|
||||
"keywords": [
|
||||
"php",
|
||||
"parser",
|
||||
"lexer",
|
||||
"annotations",
|
||||
"docblock"
|
||||
],
|
||||
"homepage": "https://www.doctrine-project.org/projects/lexer.html",
|
||||
"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"}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.1 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/coding-standard": "^9.0",
|
||||
"phpstan/phpstan": "^1.3",
|
||||
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
|
||||
"vimeo/psalm": "^4.11"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer" }
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": { "Doctrine\\Tests\\": "tests/Doctrine" }
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"composer/package-versions-deprecated": true,
|
||||
"dealerdirect/phpcodesniffer-composer-installer": true
|
||||
},
|
||||
"sort-packages": true
|
||||
}
|
||||
}
|
||||
337
plugins/email/vendor/doctrine/lexer/lib/Doctrine/Common/Lexer/AbstractLexer.php
vendored
Normal file
337
plugins/email/vendor/doctrine/lexer/lib/Doctrine/Common/Lexer/AbstractLexer.php
vendored
Normal file
|
|
@ -0,0 +1,337 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Common\Lexer;
|
||||
|
||||
use ReflectionClass;
|
||||
|
||||
use function implode;
|
||||
use function in_array;
|
||||
use function preg_split;
|
||||
use function sprintf;
|
||||
use function substr;
|
||||
|
||||
use const PREG_SPLIT_DELIM_CAPTURE;
|
||||
use const PREG_SPLIT_NO_EMPTY;
|
||||
use const PREG_SPLIT_OFFSET_CAPTURE;
|
||||
|
||||
/**
|
||||
* Base class for writing simple lexers, i.e. for creating small DSLs.
|
||||
*
|
||||
* @psalm-type Token = array{value: int|string, type:string|int|null, position:int}
|
||||
*/
|
||||
abstract class AbstractLexer
|
||||
{
|
||||
/**
|
||||
* Lexer original input string.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $input;
|
||||
|
||||
/**
|
||||
* Array of scanned tokens.
|
||||
*
|
||||
* Each token is an associative array containing three items:
|
||||
* - 'value' : the string value of the token in the input string
|
||||
* - 'type' : the type of the token (identifier, numeric, string, input
|
||||
* parameter, none)
|
||||
* - 'position' : the position of the token in the input string
|
||||
*
|
||||
* @var mixed[][]
|
||||
* @psalm-var list<Token>
|
||||
*/
|
||||
private $tokens = [];
|
||||
|
||||
/**
|
||||
* Current lexer position in input string.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $position = 0;
|
||||
|
||||
/**
|
||||
* Current peek of current lexer position.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $peek = 0;
|
||||
|
||||
/**
|
||||
* The next token in the input.
|
||||
*
|
||||
* @var mixed[]|null
|
||||
* @psalm-var Token|null
|
||||
*/
|
||||
public $lookahead;
|
||||
|
||||
/**
|
||||
* The last matched/seen token.
|
||||
*
|
||||
* @var mixed[]|null
|
||||
* @psalm-var Token|null
|
||||
*/
|
||||
public $token;
|
||||
|
||||
/**
|
||||
* Composed regex for input parsing.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
private $regex;
|
||||
|
||||
/**
|
||||
* Sets the input data to be tokenized.
|
||||
*
|
||||
* The Lexer is immediately reset and the new input tokenized.
|
||||
* Any unprocessed tokens from any previous input are lost.
|
||||
*
|
||||
* @param string $input The input to be tokenized.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setInput($input)
|
||||
{
|
||||
$this->input = $input;
|
||||
$this->tokens = [];
|
||||
|
||||
$this->reset();
|
||||
$this->scan($input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the lexer.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->lookahead = null;
|
||||
$this->token = null;
|
||||
$this->peek = 0;
|
||||
$this->position = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the peek pointer to 0.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function resetPeek()
|
||||
{
|
||||
$this->peek = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the lexer position on the input to the given position.
|
||||
*
|
||||
* @param int $position Position to place the lexical scanner.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function resetPosition($position = 0)
|
||||
{
|
||||
$this->position = $position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the original lexer's input until a given position.
|
||||
*
|
||||
* @param int $position
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getInputUntilPosition($position)
|
||||
{
|
||||
return substr($this->input, 0, $position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a given token matches the current lookahead.
|
||||
*
|
||||
* @param int|string $type
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isNextToken($type)
|
||||
{
|
||||
return $this->lookahead !== null && $this->lookahead['type'] === $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether any of the given tokens matches the current lookahead.
|
||||
*
|
||||
* @param list<int|string> $types
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isNextTokenAny(array $types)
|
||||
{
|
||||
return $this->lookahead !== null && in_array($this->lookahead['type'], $types, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves to the next token in the input string.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function moveNext()
|
||||
{
|
||||
$this->peek = 0;
|
||||
$this->token = $this->lookahead;
|
||||
$this->lookahead = isset($this->tokens[$this->position])
|
||||
? $this->tokens[$this->position++] : null;
|
||||
|
||||
return $this->lookahead !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the lexer to skip input tokens until it sees a token with the given value.
|
||||
*
|
||||
* @param string $type The token type to skip until.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function skipUntil($type)
|
||||
{
|
||||
while ($this->lookahead !== null && $this->lookahead['type'] !== $type) {
|
||||
$this->moveNext();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if given value is identical to the given token.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param int|string $token
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isA($value, $token)
|
||||
{
|
||||
return $this->getType($value) === $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the lookahead token forward.
|
||||
*
|
||||
* @return mixed[]|null The next token or NULL if there are no more tokens ahead.
|
||||
* @psalm-return Token|null
|
||||
*/
|
||||
public function peek()
|
||||
{
|
||||
if (isset($this->tokens[$this->position + $this->peek])) {
|
||||
return $this->tokens[$this->position + $this->peek++];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Peeks at the next token, returns it and immediately resets the peek.
|
||||
*
|
||||
* @return mixed[]|null The next token or NULL if there are no more tokens ahead.
|
||||
* @psalm-return Token|null
|
||||
*/
|
||||
public function glimpse()
|
||||
{
|
||||
$peek = $this->peek();
|
||||
$this->peek = 0;
|
||||
|
||||
return $peek;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans the input string for tokens.
|
||||
*
|
||||
* @param string $input A query string.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function scan($input)
|
||||
{
|
||||
if (! isset($this->regex)) {
|
||||
$this->regex = sprintf(
|
||||
'/(%s)|%s/%s',
|
||||
implode(')|(', $this->getCatchablePatterns()),
|
||||
implode('|', $this->getNonCatchablePatterns()),
|
||||
$this->getModifiers()
|
||||
);
|
||||
}
|
||||
|
||||
$flags = PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_OFFSET_CAPTURE;
|
||||
$matches = preg_split($this->regex, $input, -1, $flags);
|
||||
|
||||
if ($matches === false) {
|
||||
// Work around https://bugs.php.net/78122
|
||||
$matches = [[$input, 0]];
|
||||
}
|
||||
|
||||
foreach ($matches as $match) {
|
||||
// Must remain before 'value' assignment since it can change content
|
||||
$type = $this->getType($match[0]);
|
||||
|
||||
$this->tokens[] = [
|
||||
'value' => $match[0],
|
||||
'type' => $type,
|
||||
'position' => $match[1],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the literal for a given token.
|
||||
*
|
||||
* @param int|string $token
|
||||
*
|
||||
* @return int|string
|
||||
*/
|
||||
public function getLiteral($token)
|
||||
{
|
||||
$className = static::class;
|
||||
$reflClass = new ReflectionClass($className);
|
||||
$constants = $reflClass->getConstants();
|
||||
|
||||
foreach ($constants as $name => $value) {
|
||||
if ($value === $token) {
|
||||
return $className . '::' . $name;
|
||||
}
|
||||
}
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Regex modifiers
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getModifiers()
|
||||
{
|
||||
return 'iu';
|
||||
}
|
||||
|
||||
/**
|
||||
* Lexical catchable patterns.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
abstract protected function getCatchablePatterns();
|
||||
|
||||
/**
|
||||
* Lexical non-catchable patterns.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
abstract protected function getNonCatchablePatterns();
|
||||
|
||||
/**
|
||||
* Retrieve token type. Also processes the token value if necessary.
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return int|string|null
|
||||
*/
|
||||
abstract protected function getType(&$value);
|
||||
}
|
||||
15
plugins/email/vendor/doctrine/lexer/psalm.xml
vendored
Normal file
15
plugins/email/vendor/doctrine/lexer/psalm.xml
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0"?>
|
||||
<psalm
|
||||
errorLevel="5"
|
||||
resolveFromConfigFile="true"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="https://getpsalm.org/schema/config"
|
||||
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
|
||||
>
|
||||
<projectFiles>
|
||||
<directory name="lib/Doctrine/Common/Lexer" />
|
||||
<ignoreFiles>
|
||||
<directory name="vendor" />
|
||||
</ignoreFiles>
|
||||
</projectFiles>
|
||||
</psalm>
|
||||
33
plugins/email/vendor/egulias/email-validator/CHANGELOG.md
vendored
Normal file
33
plugins/email/vendor/egulias/email-validator/CHANGELOG.md
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# EmailValidator v3 Changelog
|
||||
|
||||
## New Features
|
||||
|
||||
* Access to local part and domain part from EmailParser
|
||||
* Validations outside of the scope of the RFC will be considered "extra" validations, thus opening the door for adding new; will live in their own folder "extra" (as requested in #248, #195, #183).
|
||||
|
||||
## Breacking changes
|
||||
|
||||
* PHP version upgraded to match Symfony's (as of 12/2020).
|
||||
* DNSCheckValidation now fails for missing MX records. While the RFC argues that the existence of only A records to be valid, starting in v3 they will be considered invalid.
|
||||
* Emails domain part are now intenteded to be RFC 1035 compliant, rendering previous valid emails (e.g example@examp&) invalid.
|
||||
|
||||
## PHP versions upgrade policy
|
||||
PHP version upgrade requirement will happen via MINOR (3.x) version upgrades of the library, following the adoption level by major frameworks.
|
||||
|
||||
## Changes
|
||||
* #235
|
||||
* #215
|
||||
* #130
|
||||
* #258
|
||||
* #188
|
||||
* #181
|
||||
* #217
|
||||
* #214
|
||||
* #249
|
||||
* #236
|
||||
* #257
|
||||
* #210
|
||||
|
||||
## Thanks
|
||||
To contributors, be it with PRs, reporting issues or supporting otherwise.
|
||||
|
||||
153
plugins/email/vendor/egulias/email-validator/CONTRIBUTING.md
vendored
Normal file
153
plugins/email/vendor/egulias/email-validator/CONTRIBUTING.md
vendored
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
# Contributing
|
||||
|
||||
When contributing to this repository make sure to follow the Pull request process below.
|
||||
Reduce to the minimum 3rd party dependencies.
|
||||
|
||||
Please note we have a [code of conduct](#Code of Conduct), please follow it in all your interactions with the project.
|
||||
|
||||
## Pull Request Process
|
||||
|
||||
When doing a PR to v2 remember that you also have to do the PR port to v3, or tests confirming the bug is not reproducible.
|
||||
|
||||
1. Supported version is v3. If you are fixing a bug in v2, please port to v3
|
||||
2. Use the title as a brief description of the changes
|
||||
3. Describe the changes you are proposing
|
||||
1. If adding an extra validation state the benefits of adding it and the problem is solving
|
||||
2. Document in the readme, by adding it to the list
|
||||
4. Provide appropriate tests for the code you are submitting: aim to keep the existing coverage percentage.
|
||||
5. Add your Twitter handle (if you have) so we can thank you there.
|
||||
|
||||
## License
|
||||
By contributing, you agree that your contributions will be licensed under its MIT License.
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
### Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
### Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
### Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
### Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
### Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at <emailvalidatorrfc.ccreport@gmail.com>.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
#### Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
#### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
#### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
#### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
#### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
### Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
[https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0].
|
||||
|
||||
Community Impact Guidelines were inspired by
|
||||
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available
|
||||
at [https://www.contributor-covenant.org/translations][translations].
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
[v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html
|
||||
[Mozilla CoC]: https://github.com/mozilla/diversity
|
||||
[FAQ]: https://www.contributor-covenant.org/faq
|
||||
[translations]: https://www.contributor-covenant.org/translations
|
||||
19
plugins/email/vendor/egulias/email-validator/LICENSE
vendored
Normal file
19
plugins/email/vendor/egulias/email-validator/LICENSE
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2013-2021 Eduardo Gulias Davis
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
38
plugins/email/vendor/egulias/email-validator/composer.json
vendored
Normal file
38
plugins/email/vendor/egulias/email-validator/composer.json
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"name": "egulias/email-validator",
|
||||
"description": "A library for validating emails against several RFCs",
|
||||
"homepage": "https://github.com/egulias/EmailValidator",
|
||||
"keywords": ["email", "validation", "validator", "emailvalidation", "emailvalidator"],
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{"name": "Eduardo Gulias Davis"}
|
||||
],
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.0.x-dev"
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2",
|
||||
"doctrine/lexer": "^1.2",
|
||||
"symfony/polyfill-intl-idn": "^1.15"
|
||||
},
|
||||
"require-dev": {
|
||||
"php-coveralls/php-coveralls": "^2.2",
|
||||
"phpunit/phpunit": "^8.5.8|^9.3.3",
|
||||
"vimeo/psalm": "^4"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Egulias\\EmailValidator\\": "src"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Egulias\\EmailValidator\\Tests\\": "tests"
|
||||
}
|
||||
}
|
||||
}
|
||||
5028
plugins/email/vendor/egulias/email-validator/composer.lock
generated
vendored
Normal file
5028
plugins/email/vendor/egulias/email-validator/composer.lock
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
339
plugins/email/vendor/egulias/email-validator/src/EmailLexer.php
vendored
Normal file
339
plugins/email/vendor/egulias/email-validator/src/EmailLexer.php
vendored
Normal file
|
|
@ -0,0 +1,339 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator;
|
||||
|
||||
use Doctrine\Common\Lexer\AbstractLexer;
|
||||
|
||||
class EmailLexer extends AbstractLexer
|
||||
{
|
||||
//ASCII values
|
||||
const S_EMPTY = null;
|
||||
const C_NUL = 0;
|
||||
const S_HTAB = 9;
|
||||
const S_LF = 10;
|
||||
const S_CR = 13;
|
||||
const S_SP = 32;
|
||||
const EXCLAMATION = 33;
|
||||
const S_DQUOTE = 34;
|
||||
const NUMBER_SIGN = 35;
|
||||
const DOLLAR = 36;
|
||||
const PERCENTAGE = 37;
|
||||
const AMPERSAND = 38;
|
||||
const S_SQUOTE = 39;
|
||||
const S_OPENPARENTHESIS = 40;
|
||||
const S_CLOSEPARENTHESIS = 41;
|
||||
const ASTERISK = 42;
|
||||
const S_PLUS = 43;
|
||||
const S_COMMA = 44;
|
||||
const S_HYPHEN = 45;
|
||||
const S_DOT = 46;
|
||||
const S_SLASH = 47;
|
||||
const S_COLON = 58;
|
||||
const S_SEMICOLON = 59;
|
||||
const S_LOWERTHAN = 60;
|
||||
const S_EQUAL = 61;
|
||||
const S_GREATERTHAN = 62;
|
||||
const QUESTIONMARK = 63;
|
||||
const S_AT = 64;
|
||||
const S_OPENBRACKET = 91;
|
||||
const S_BACKSLASH = 92;
|
||||
const S_CLOSEBRACKET = 93;
|
||||
const CARET = 94;
|
||||
const S_UNDERSCORE = 95;
|
||||
const S_BACKTICK = 96;
|
||||
const S_OPENCURLYBRACES = 123;
|
||||
const S_PIPE = 124;
|
||||
const S_CLOSECURLYBRACES = 125;
|
||||
const S_TILDE = 126;
|
||||
const C_DEL = 127;
|
||||
const INVERT_QUESTIONMARK= 168;
|
||||
const INVERT_EXCLAMATION = 173;
|
||||
const GENERIC = 300;
|
||||
const S_IPV6TAG = 301;
|
||||
const INVALID = 302;
|
||||
const CRLF = 1310;
|
||||
const S_DOUBLECOLON = 5858;
|
||||
const ASCII_INVALID_FROM = 127;
|
||||
const ASCII_INVALID_TO = 199;
|
||||
|
||||
/**
|
||||
* US-ASCII visible characters not valid for atext (@link http://tools.ietf.org/html/rfc5322#section-3.2.3)
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $charValue = [
|
||||
'{' => self::S_OPENCURLYBRACES,
|
||||
'}' => self::S_CLOSECURLYBRACES,
|
||||
'(' => self::S_OPENPARENTHESIS,
|
||||
')' => self::S_CLOSEPARENTHESIS,
|
||||
'<' => self::S_LOWERTHAN,
|
||||
'>' => self::S_GREATERTHAN,
|
||||
'[' => self::S_OPENBRACKET,
|
||||
']' => self::S_CLOSEBRACKET,
|
||||
':' => self::S_COLON,
|
||||
';' => self::S_SEMICOLON,
|
||||
'@' => self::S_AT,
|
||||
'\\' => self::S_BACKSLASH,
|
||||
'/' => self::S_SLASH,
|
||||
',' => self::S_COMMA,
|
||||
'.' => self::S_DOT,
|
||||
"'" => self::S_SQUOTE,
|
||||
"`" => self::S_BACKTICK,
|
||||
'"' => self::S_DQUOTE,
|
||||
'-' => self::S_HYPHEN,
|
||||
'::' => self::S_DOUBLECOLON,
|
||||
' ' => self::S_SP,
|
||||
"\t" => self::S_HTAB,
|
||||
"\r" => self::S_CR,
|
||||
"\n" => self::S_LF,
|
||||
"\r\n" => self::CRLF,
|
||||
'IPv6' => self::S_IPV6TAG,
|
||||
'' => self::S_EMPTY,
|
||||
'\0' => self::C_NUL,
|
||||
'*' => self::ASTERISK,
|
||||
'!' => self::EXCLAMATION,
|
||||
'&' => self::AMPERSAND,
|
||||
'^' => self::CARET,
|
||||
'$' => self::DOLLAR,
|
||||
'%' => self::PERCENTAGE,
|
||||
'~' => self::S_TILDE,
|
||||
'|' => self::S_PIPE,
|
||||
'_' => self::S_UNDERSCORE,
|
||||
'=' => self::S_EQUAL,
|
||||
'+' => self::S_PLUS,
|
||||
'¿' => self::INVERT_QUESTIONMARK,
|
||||
'?' => self::QUESTIONMARK,
|
||||
'#' => self::NUMBER_SIGN,
|
||||
'¡' => self::INVERT_EXCLAMATION,
|
||||
];
|
||||
|
||||
const INVALID_CHARS_REGEX = "/[^\p{S}\p{C}\p{Cc}]+/iu";
|
||||
|
||||
const VALID_UTF8_REGEX = '/\p{Cc}+/u';
|
||||
|
||||
const CATCHABLE_PATTERNS = [
|
||||
'[a-zA-Z]+[46]?', //ASCII and domain literal
|
||||
'[^\x00-\x7F]', //UTF-8
|
||||
'[0-9]+',
|
||||
'\r\n',
|
||||
'::',
|
||||
'\s+?',
|
||||
'.',
|
||||
];
|
||||
|
||||
const NON_CATCHABLE_PATTERNS = [
|
||||
'[\xA0-\xff]+',
|
||||
];
|
||||
|
||||
const MODIFIERS = 'iu';
|
||||
|
||||
/** @var bool */
|
||||
protected $hasInvalidTokens = false;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*
|
||||
* @psalm-var array{value:string, type:null|int, position:int}|array<empty, empty>
|
||||
*/
|
||||
protected $previous = [];
|
||||
|
||||
/**
|
||||
* The last matched/seen token.
|
||||
*
|
||||
* @var array
|
||||
*
|
||||
* @psalm-suppress NonInvariantDocblockPropertyType
|
||||
* @psalm-var array{value:string, type:null|int, position:int}
|
||||
* @psalm-suppress NonInvariantDocblockPropertyType
|
||||
*/
|
||||
public $token;
|
||||
|
||||
/**
|
||||
* The next token in the input.
|
||||
*
|
||||
* @var array{position: int, type: int|null|string, value: int|string}|null
|
||||
*/
|
||||
public $lookahead;
|
||||
|
||||
/** @psalm-var array{value:'', type:null, position:0} */
|
||||
private static $nullToken = [
|
||||
'value' => '',
|
||||
'type' => null,
|
||||
'position' => 0,
|
||||
];
|
||||
|
||||
/** @var string */
|
||||
private $accumulator = '';
|
||||
|
||||
/** @var bool */
|
||||
private $hasToRecord = false;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->previous = $this->token = self::$nullToken;
|
||||
$this->lookahead = null;
|
||||
}
|
||||
|
||||
public function reset() : void
|
||||
{
|
||||
$this->hasInvalidTokens = false;
|
||||
parent::reset();
|
||||
$this->previous = $this->token = self::$nullToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $type
|
||||
* @throws \UnexpectedValueException
|
||||
* @return boolean
|
||||
*
|
||||
* @psalm-suppress InvalidScalarArgument
|
||||
*/
|
||||
public function find($type) : bool
|
||||
{
|
||||
$search = clone $this;
|
||||
$search->skipUntil($type);
|
||||
|
||||
if (!$search->lookahead) {
|
||||
throw new \UnexpectedValueException($type . ' not found');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* moveNext
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function moveNext() : bool
|
||||
{
|
||||
if ($this->hasToRecord && $this->previous === self::$nullToken) {
|
||||
$this->accumulator .= $this->token['value'];
|
||||
}
|
||||
|
||||
$this->previous = $this->token;
|
||||
|
||||
if($this->lookahead === null) {
|
||||
$this->lookahead = self::$nullToken;
|
||||
}
|
||||
|
||||
$hasNext = parent::moveNext();
|
||||
|
||||
if ($this->hasToRecord) {
|
||||
$this->accumulator .= $this->token['value'];
|
||||
}
|
||||
|
||||
return $hasNext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve token type. Also processes the token value if necessary.
|
||||
*
|
||||
* @param string $value
|
||||
* @throws \InvalidArgumentException
|
||||
* @return integer
|
||||
*/
|
||||
protected function getType(&$value)
|
||||
{
|
||||
$encoded = $value;
|
||||
|
||||
if (mb_detect_encoding($value, 'auto', true) !== 'UTF-8') {
|
||||
$encoded = mb_convert_encoding($value, 'UTF-8', 'Windows-1252');
|
||||
}
|
||||
|
||||
if ($this->isValid($encoded)) {
|
||||
return $this->charValue[$encoded];
|
||||
}
|
||||
|
||||
if ($this->isNullType($encoded)) {
|
||||
return self::C_NUL;
|
||||
}
|
||||
|
||||
if ($this->isInvalidChar($encoded)) {
|
||||
$this->hasInvalidTokens = true;
|
||||
return self::INVALID;
|
||||
}
|
||||
|
||||
|
||||
return self::GENERIC;
|
||||
}
|
||||
|
||||
protected function isValid(string $value) : bool
|
||||
{
|
||||
return isset($this->charValue[$value]);
|
||||
}
|
||||
|
||||
protected function isNullType(string $value) : bool
|
||||
{
|
||||
return $value === "\0";
|
||||
}
|
||||
|
||||
protected function isInvalidChar(string $value) : bool
|
||||
{
|
||||
return !preg_match(self::INVALID_CHARS_REGEX, $value);
|
||||
}
|
||||
|
||||
protected function isUTF8Invalid(string $value) : bool
|
||||
{
|
||||
return preg_match(self::VALID_UTF8_REGEX, $value) !== false;
|
||||
}
|
||||
|
||||
public function hasInvalidTokens() : bool
|
||||
{
|
||||
return $this->hasInvalidTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* getPrevious
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPrevious() : array
|
||||
{
|
||||
return $this->previous;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lexical catchable patterns.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected function getCatchablePatterns() : array
|
||||
{
|
||||
return self::CATCHABLE_PATTERNS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lexical non-catchable patterns.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected function getNonCatchablePatterns() : array
|
||||
{
|
||||
return self::NON_CATCHABLE_PATTERNS;
|
||||
}
|
||||
|
||||
protected function getModifiers() : string
|
||||
{
|
||||
return self::MODIFIERS;
|
||||
}
|
||||
|
||||
public function getAccumulatedValues() : string
|
||||
{
|
||||
return $this->accumulator;
|
||||
}
|
||||
|
||||
public function startRecording() : void
|
||||
{
|
||||
$this->hasToRecord = true;
|
||||
}
|
||||
|
||||
public function stopRecording() : void
|
||||
{
|
||||
$this->hasToRecord = false;
|
||||
}
|
||||
|
||||
public function clearRecorded() : void
|
||||
{
|
||||
$this->accumulator = '';
|
||||
}
|
||||
}
|
||||
91
plugins/email/vendor/egulias/email-validator/src/EmailParser.php
vendored
Normal file
91
plugins/email/vendor/egulias/email-validator/src/EmailParser.php
vendored
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Result\Result;
|
||||
use Egulias\EmailValidator\Parser\LocalPart;
|
||||
use Egulias\EmailValidator\Parser\DomainPart;
|
||||
use Egulias\EmailValidator\Result\ValidEmail;
|
||||
use Egulias\EmailValidator\Result\InvalidEmail;
|
||||
use Egulias\EmailValidator\Warning\EmailTooLong;
|
||||
use Egulias\EmailValidator\Result\Reason\NoLocalPart;
|
||||
|
||||
class EmailParser extends Parser
|
||||
{
|
||||
const EMAIL_MAX_LENGTH = 254;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $domainPart = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $localPart = '';
|
||||
|
||||
public function parse(string $str) : Result
|
||||
{
|
||||
$result = parent::parse($str);
|
||||
|
||||
$this->addLongEmailWarning($this->localPart, $this->domainPart);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function preLeftParsing(): Result
|
||||
{
|
||||
if (!$this->hasAtToken()) {
|
||||
return new InvalidEmail(new NoLocalPart(), $this->lexer->token["value"]);
|
||||
}
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
protected function parseLeftFromAt(): Result
|
||||
{
|
||||
return $this->processLocalPart();
|
||||
}
|
||||
|
||||
protected function parseRightFromAt(): Result
|
||||
{
|
||||
return $this->processDomainPart();
|
||||
}
|
||||
|
||||
private function processLocalPart() : Result
|
||||
{
|
||||
$localPartParser = new LocalPart($this->lexer);
|
||||
$localPartResult = $localPartParser->parse();
|
||||
$this->localPart = $localPartParser->localPart();
|
||||
$this->warnings = array_merge($localPartParser->getWarnings(), $this->warnings);
|
||||
|
||||
return $localPartResult;
|
||||
}
|
||||
|
||||
private function processDomainPart() : Result
|
||||
{
|
||||
$domainPartParser = new DomainPart($this->lexer);
|
||||
$domainPartResult = $domainPartParser->parse();
|
||||
$this->domainPart = $domainPartParser->domainPart();
|
||||
$this->warnings = array_merge($domainPartParser->getWarnings(), $this->warnings);
|
||||
|
||||
return $domainPartResult;
|
||||
}
|
||||
|
||||
public function getDomainPart() : string
|
||||
{
|
||||
return $this->domainPart;
|
||||
}
|
||||
|
||||
public function getLocalPart() : string
|
||||
{
|
||||
return $this->localPart;
|
||||
}
|
||||
|
||||
private function addLongEmailWarning(string $localPart, string $parsedDomainPart) : void
|
||||
{
|
||||
if (strlen($localPart . '@' . $parsedDomainPart) > self::EMAIL_MAX_LENGTH) {
|
||||
$this->warnings[EmailTooLong::CODE] = new EmailTooLong();
|
||||
}
|
||||
}
|
||||
}
|
||||
67
plugins/email/vendor/egulias/email-validator/src/EmailValidator.php
vendored
Normal file
67
plugins/email/vendor/egulias/email-validator/src/EmailValidator.php
vendored
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator;
|
||||
|
||||
use Egulias\EmailValidator\Result\InvalidEmail;
|
||||
use Egulias\EmailValidator\Validation\EmailValidation;
|
||||
|
||||
class EmailValidator
|
||||
{
|
||||
/**
|
||||
* @var EmailLexer
|
||||
*/
|
||||
private $lexer;
|
||||
|
||||
/**
|
||||
* @var Warning\Warning[]
|
||||
*/
|
||||
private $warnings = [];
|
||||
|
||||
/**
|
||||
* @var ?InvalidEmail
|
||||
*/
|
||||
private $error;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->lexer = new EmailLexer();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $email
|
||||
* @param EmailValidation $emailValidation
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid(string $email, EmailValidation $emailValidation)
|
||||
{
|
||||
$isValid = $emailValidation->isValid($email, $this->lexer);
|
||||
$this->warnings = $emailValidation->getWarnings();
|
||||
$this->error = $emailValidation->getError();
|
||||
|
||||
return $isValid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasWarnings()
|
||||
{
|
||||
return !empty($this->warnings);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getWarnings()
|
||||
{
|
||||
return $this->warnings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return InvalidEmail|null
|
||||
*/
|
||||
public function getError()
|
||||
{
|
||||
return $this->error;
|
||||
}
|
||||
}
|
||||
92
plugins/email/vendor/egulias/email-validator/src/MessageIDParser.php
vendored
Normal file
92
plugins/email/vendor/egulias/email-validator/src/MessageIDParser.php
vendored
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator;
|
||||
|
||||
use Egulias\EmailValidator\Parser;
|
||||
use Egulias\EmailValidator\Result\Result;
|
||||
use Egulias\EmailValidator\Parser\IDLeftPart;
|
||||
use Egulias\EmailValidator\Parser\IDRightPart;
|
||||
use Egulias\EmailValidator\Result\ValidEmail;
|
||||
use Egulias\EmailValidator\Result\InvalidEmail;
|
||||
use Egulias\EmailValidator\Warning\EmailTooLong;
|
||||
use Egulias\EmailValidator\Result\Reason\NoLocalPart;
|
||||
|
||||
class MessageIDParser extends Parser
|
||||
{
|
||||
|
||||
const EMAILID_MAX_LENGTH = 254;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $idLeft = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $idRight = '';
|
||||
|
||||
public function parse(string $str) : Result
|
||||
{
|
||||
$result = parent::parse($str);
|
||||
|
||||
$this->addLongEmailWarning($this->idLeft, $this->idRight);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function preLeftParsing(): Result
|
||||
{
|
||||
if (!$this->hasAtToken()) {
|
||||
return new InvalidEmail(new NoLocalPart(), $this->lexer->token["value"]);
|
||||
}
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
protected function parseLeftFromAt(): Result
|
||||
{
|
||||
return $this->processIDLeft();
|
||||
}
|
||||
|
||||
protected function parseRightFromAt(): Result
|
||||
{
|
||||
return $this->processIDRight();
|
||||
}
|
||||
|
||||
private function processIDLeft() : Result
|
||||
{
|
||||
$localPartParser = new IDLeftPart($this->lexer);
|
||||
$localPartResult = $localPartParser->parse();
|
||||
$this->idLeft = $localPartParser->localPart();
|
||||
$this->warnings = array_merge($localPartParser->getWarnings(), $this->warnings);
|
||||
|
||||
return $localPartResult;
|
||||
}
|
||||
|
||||
private function processIDRight() : Result
|
||||
{
|
||||
$domainPartParser = new IDRightPart($this->lexer);
|
||||
$domainPartResult = $domainPartParser->parse();
|
||||
$this->idRight = $domainPartParser->domainPart();
|
||||
$this->warnings = array_merge($domainPartParser->getWarnings(), $this->warnings);
|
||||
|
||||
return $domainPartResult;
|
||||
}
|
||||
|
||||
public function getLeftPart() : string
|
||||
{
|
||||
return $this->idLeft;
|
||||
}
|
||||
|
||||
public function getRightPart() : string
|
||||
{
|
||||
return $this->idRight;
|
||||
}
|
||||
|
||||
private function addLongEmailWarning(string $localPart, string $parsedDomainPart) : void
|
||||
{
|
||||
if (strlen($localPart . '@' . $parsedDomainPart) > self::EMAILID_MAX_LENGTH) {
|
||||
$this->warnings[EmailTooLong::CODE] = new EmailTooLong();
|
||||
}
|
||||
}
|
||||
}
|
||||
78
plugins/email/vendor/egulias/email-validator/src/Parser.php
vendored
Normal file
78
plugins/email/vendor/egulias/email-validator/src/Parser.php
vendored
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator;
|
||||
|
||||
use Egulias\EmailValidator\Result\Result;
|
||||
use Egulias\EmailValidator\Result\ValidEmail;
|
||||
use Egulias\EmailValidator\Result\InvalidEmail;
|
||||
use Egulias\EmailValidator\Result\Reason\ExpectingATEXT;
|
||||
|
||||
abstract class Parser
|
||||
{
|
||||
/**
|
||||
* @var Warning\Warning[]
|
||||
*/
|
||||
protected $warnings = [];
|
||||
|
||||
/**
|
||||
* @var EmailLexer
|
||||
*/
|
||||
protected $lexer;
|
||||
|
||||
/**
|
||||
* id-left "@" id-right
|
||||
*/
|
||||
abstract protected function parseRightFromAt() : Result;
|
||||
abstract protected function parseLeftFromAt() : Result;
|
||||
abstract protected function preLeftParsing() : Result;
|
||||
|
||||
|
||||
public function __construct(EmailLexer $lexer)
|
||||
{
|
||||
$this->lexer = $lexer;
|
||||
}
|
||||
|
||||
public function parse(string $str) : Result
|
||||
{
|
||||
$this->lexer->setInput($str);
|
||||
|
||||
if ($this->lexer->hasInvalidTokens()) {
|
||||
return new InvalidEmail(new ExpectingATEXT("Invalid tokens found"), $this->lexer->token["value"]);
|
||||
}
|
||||
|
||||
$preParsingResult = $this->preLeftParsing();
|
||||
if ($preParsingResult->isInvalid()) {
|
||||
return $preParsingResult;
|
||||
}
|
||||
|
||||
$localPartResult = $this->parseLeftFromAt();
|
||||
|
||||
if ($localPartResult->isInvalid()) {
|
||||
return $localPartResult;
|
||||
}
|
||||
|
||||
$domainPartResult = $this->parseRightFromAt();
|
||||
|
||||
if ($domainPartResult->isInvalid()) {
|
||||
return $domainPartResult;
|
||||
}
|
||||
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Warning\Warning[]
|
||||
*/
|
||||
public function getWarnings() : array
|
||||
{
|
||||
return $this->warnings;
|
||||
}
|
||||
|
||||
protected function hasAtToken() : bool
|
||||
{
|
||||
$this->lexer->moveNext();
|
||||
$this->lexer->moveNext();
|
||||
|
||||
return $this->lexer->token['type'] !== EmailLexer::S_AT;
|
||||
}
|
||||
}
|
||||
103
plugins/email/vendor/egulias/email-validator/src/Parser/Comment.php
vendored
Normal file
103
plugins/email/vendor/egulias/email-validator/src/Parser/Comment.php
vendored
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Parser;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Result\Result;
|
||||
use Egulias\EmailValidator\Warning\QuotedPart;
|
||||
use Egulias\EmailValidator\Result\InvalidEmail;
|
||||
use Egulias\EmailValidator\Parser\CommentStrategy\CommentStrategy;
|
||||
use Egulias\EmailValidator\Result\Reason\UnclosedComment;
|
||||
use Egulias\EmailValidator\Result\Reason\UnOpenedComment;
|
||||
use Egulias\EmailValidator\Warning\Comment as WarningComment;
|
||||
|
||||
class Comment extends PartParser
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $openedParenthesis = 0;
|
||||
|
||||
/**
|
||||
* @var CommentStrategy
|
||||
*/
|
||||
private $commentStrategy;
|
||||
|
||||
public function __construct(EmailLexer $lexer, CommentStrategy $commentStrategy)
|
||||
{
|
||||
$this->lexer = $lexer;
|
||||
$this->commentStrategy = $commentStrategy;
|
||||
}
|
||||
|
||||
public function parse() : Result
|
||||
{
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) {
|
||||
$this->openedParenthesis++;
|
||||
if($this->noClosingParenthesis()) {
|
||||
return new InvalidEmail(new UnclosedComment(), $this->lexer->token['value']);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_CLOSEPARENTHESIS) {
|
||||
return new InvalidEmail(new UnOpenedComment(), $this->lexer->token['value']);
|
||||
}
|
||||
|
||||
$this->warnings[WarningComment::CODE] = new WarningComment();
|
||||
|
||||
$moreTokens = true;
|
||||
while ($this->commentStrategy->exitCondition($this->lexer, $this->openedParenthesis) && $moreTokens){
|
||||
|
||||
if ($this->lexer->isNextToken(EmailLexer::S_OPENPARENTHESIS)) {
|
||||
$this->openedParenthesis++;
|
||||
}
|
||||
$this->warnEscaping();
|
||||
if($this->lexer->isNextToken(EmailLexer::S_CLOSEPARENTHESIS)) {
|
||||
$this->openedParenthesis--;
|
||||
}
|
||||
$moreTokens = $this->lexer->moveNext();
|
||||
}
|
||||
|
||||
if($this->openedParenthesis >= 1) {
|
||||
return new InvalidEmail(new UnclosedComment(), $this->lexer->token['value']);
|
||||
} else if ($this->openedParenthesis < 0) {
|
||||
return new InvalidEmail(new UnOpenedComment(), $this->lexer->token['value']);
|
||||
}
|
||||
|
||||
$finalValidations = $this->commentStrategy->endOfLoopValidations($this->lexer);
|
||||
|
||||
$this->warnings = array_merge($this->warnings, $this->commentStrategy->getWarnings());
|
||||
|
||||
return $finalValidations;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function warnEscaping() : bool
|
||||
{
|
||||
//Backslash found
|
||||
if ($this->lexer->token['type'] !== EmailLexer::S_BACKSLASH) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB, EmailLexer::C_DEL))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->warnings[QuotedPart::CODE] =
|
||||
new QuotedPart($this->lexer->getPrevious()['type'], $this->lexer->token['type']);
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
private function noClosingParenthesis() : bool
|
||||
{
|
||||
try {
|
||||
$this->lexer->find(EmailLexer::S_CLOSEPARENTHESIS);
|
||||
return false;
|
||||
} catch (\RuntimeException $e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
18
plugins/email/vendor/egulias/email-validator/src/Parser/CommentStrategy/CommentStrategy.php
vendored
Normal file
18
plugins/email/vendor/egulias/email-validator/src/Parser/CommentStrategy/CommentStrategy.php
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Parser\CommentStrategy;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Result\Result;
|
||||
|
||||
interface CommentStrategy
|
||||
{
|
||||
/**
|
||||
* Return "true" to continue, "false" to exit
|
||||
*/
|
||||
public function exitCondition(EmailLexer $lexer, int $openedParenthesis) : bool;
|
||||
|
||||
public function endOfLoopValidations(EmailLexer $lexer) : Result;
|
||||
|
||||
public function getWarnings() : array;
|
||||
}
|
||||
37
plugins/email/vendor/egulias/email-validator/src/Parser/CommentStrategy/DomainComment.php
vendored
Normal file
37
plugins/email/vendor/egulias/email-validator/src/Parser/CommentStrategy/DomainComment.php
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Parser\CommentStrategy;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Result\Result;
|
||||
use Egulias\EmailValidator\Result\ValidEmail;
|
||||
use Egulias\EmailValidator\Result\InvalidEmail;
|
||||
use Egulias\EmailValidator\Result\Reason\ExpectingATEXT;
|
||||
|
||||
class DomainComment implements CommentStrategy
|
||||
{
|
||||
public function exitCondition(EmailLexer $lexer, int $openedParenthesis) : bool
|
||||
{
|
||||
if (($openedParenthesis === 0 && $lexer->isNextToken(EmailLexer::S_DOT))){ // || !$internalLexer->moveNext()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function endOfLoopValidations(EmailLexer $lexer) : Result
|
||||
{
|
||||
//test for end of string
|
||||
if (!$lexer->isNextToken(EmailLexer::S_DOT)) {
|
||||
return new InvalidEmail(new ExpectingATEXT('DOT not found near CLOSEPARENTHESIS'), $lexer->token['value']);
|
||||
}
|
||||
//add warning
|
||||
//Address is valid within the message but cannot be used unmodified for the envelope
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
public function getWarnings(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
37
plugins/email/vendor/egulias/email-validator/src/Parser/CommentStrategy/LocalComment.php
vendored
Normal file
37
plugins/email/vendor/egulias/email-validator/src/Parser/CommentStrategy/LocalComment.php
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Parser\CommentStrategy;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Result\Result;
|
||||
use Egulias\EmailValidator\Result\ValidEmail;
|
||||
use Egulias\EmailValidator\Warning\CFWSNearAt;
|
||||
use Egulias\EmailValidator\Result\InvalidEmail;
|
||||
use Egulias\EmailValidator\Result\Reason\ExpectingATEXT;
|
||||
|
||||
class LocalComment implements CommentStrategy
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $warnings = [];
|
||||
|
||||
public function exitCondition(EmailLexer $lexer, int $openedParenthesis) : bool
|
||||
{
|
||||
return !$lexer->isNextToken(EmailLexer::S_AT);
|
||||
}
|
||||
|
||||
public function endOfLoopValidations(EmailLexer $lexer) : Result
|
||||
{
|
||||
if (!$lexer->isNextToken(EmailLexer::S_AT)) {
|
||||
return new InvalidEmail(new ExpectingATEXT('ATEX is not expected after closing comments'), $lexer->token['value']);
|
||||
}
|
||||
$this->warnings[CFWSNearAt::CODE] = new CFWSNearAt();
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
public function getWarnings(): array
|
||||
{
|
||||
return $this->warnings;
|
||||
}
|
||||
}
|
||||
211
plugins/email/vendor/egulias/email-validator/src/Parser/DomainLiteral.php
vendored
Normal file
211
plugins/email/vendor/egulias/email-validator/src/Parser/DomainLiteral.php
vendored
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
<?php
|
||||
namespace Egulias\EmailValidator\Parser;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Result\Result;
|
||||
use Egulias\EmailValidator\Result\ValidEmail;
|
||||
use Egulias\EmailValidator\Result\InvalidEmail;
|
||||
use Egulias\EmailValidator\Warning\CFWSWithFWS;
|
||||
use Egulias\EmailValidator\Warning\IPV6BadChar;
|
||||
use Egulias\EmailValidator\Result\Reason\CRNoLF;
|
||||
use Egulias\EmailValidator\Warning\IPV6ColonEnd;
|
||||
use Egulias\EmailValidator\Warning\IPV6MaxGroups;
|
||||
use Egulias\EmailValidator\Warning\ObsoleteDTEXT;
|
||||
use Egulias\EmailValidator\Warning\AddressLiteral;
|
||||
use Egulias\EmailValidator\Warning\IPV6ColonStart;
|
||||
use Egulias\EmailValidator\Warning\IPV6Deprecated;
|
||||
use Egulias\EmailValidator\Warning\IPV6GroupCount;
|
||||
use Egulias\EmailValidator\Warning\IPV6DoubleColon;
|
||||
use Egulias\EmailValidator\Result\Reason\ExpectingDTEXT;
|
||||
use Egulias\EmailValidator\Result\Reason\UnusualElements;
|
||||
use Egulias\EmailValidator\Warning\DomainLiteral as WarningDomainLiteral;
|
||||
|
||||
class DomainLiteral extends PartParser
|
||||
{
|
||||
const IPV4_REGEX = '/\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/';
|
||||
|
||||
const OBSOLETE_WARNINGS = [
|
||||
EmailLexer::INVALID,
|
||||
EmailLexer::C_DEL,
|
||||
EmailLexer::S_LF,
|
||||
EmailLexer::S_BACKSLASH
|
||||
];
|
||||
|
||||
public function parse() : Result
|
||||
{
|
||||
$this->addTagWarnings();
|
||||
|
||||
$IPv6TAG = false;
|
||||
$addressLiteral = '';
|
||||
|
||||
do {
|
||||
if ($this->lexer->token['type'] === EmailLexer::C_NUL) {
|
||||
return new InvalidEmail(new ExpectingDTEXT(), $this->lexer->token['value']);
|
||||
}
|
||||
|
||||
$this->addObsoleteWarnings();
|
||||
|
||||
if ($this->lexer->isNextTokenAny(array(EmailLexer::S_OPENBRACKET, EmailLexer::S_OPENBRACKET))) {
|
||||
return new InvalidEmail(new ExpectingDTEXT(), $this->lexer->token['value']);
|
||||
}
|
||||
|
||||
if ($this->lexer->isNextTokenAny(
|
||||
array(EmailLexer::S_HTAB, EmailLexer::S_SP, EmailLexer::CRLF)
|
||||
)) {
|
||||
$this->warnings[CFWSWithFWS::CODE] = new CFWSWithFWS();
|
||||
$this->parseFWS();
|
||||
}
|
||||
|
||||
if ($this->lexer->isNextToken(EmailLexer::S_CR)) {
|
||||
return new InvalidEmail(new CRNoLF(), $this->lexer->token['value']);
|
||||
}
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH) {
|
||||
return new InvalidEmail(new UnusualElements($this->lexer->token['value']), $this->lexer->token['value']);
|
||||
}
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_IPV6TAG) {
|
||||
$IPv6TAG = true;
|
||||
}
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_CLOSEBRACKET) {
|
||||
break;
|
||||
}
|
||||
|
||||
$addressLiteral .= $this->lexer->token['value'];
|
||||
|
||||
} while ($this->lexer->moveNext());
|
||||
|
||||
|
||||
//Encapsulate
|
||||
$addressLiteral = str_replace('[', '', $addressLiteral);
|
||||
$isAddressLiteralIPv4 = $this->checkIPV4Tag($addressLiteral);
|
||||
|
||||
if (!$isAddressLiteralIPv4) {
|
||||
return new ValidEmail();
|
||||
} else {
|
||||
$addressLiteral = $this->convertIPv4ToIPv6($addressLiteral);
|
||||
}
|
||||
|
||||
if (!$IPv6TAG) {
|
||||
$this->warnings[WarningDomainLiteral::CODE] = new WarningDomainLiteral();
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
$this->warnings[AddressLiteral::CODE] = new AddressLiteral();
|
||||
|
||||
$this->checkIPV6Tag($addressLiteral);
|
||||
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $addressLiteral
|
||||
* @param int $maxGroups
|
||||
*/
|
||||
public function checkIPV6Tag($addressLiteral, $maxGroups = 8) : void
|
||||
{
|
||||
$prev = $this->lexer->getPrevious();
|
||||
if ($prev['type'] === EmailLexer::S_COLON) {
|
||||
$this->warnings[IPV6ColonEnd::CODE] = new IPV6ColonEnd();
|
||||
}
|
||||
|
||||
$IPv6 = substr($addressLiteral, 5);
|
||||
//Daniel Marschall's new IPv6 testing strategy
|
||||
$matchesIP = explode(':', $IPv6);
|
||||
$groupCount = count($matchesIP);
|
||||
$colons = strpos($IPv6, '::');
|
||||
|
||||
if (count(preg_grep('/^[0-9A-Fa-f]{0,4}$/', $matchesIP, PREG_GREP_INVERT)) !== 0) {
|
||||
$this->warnings[IPV6BadChar::CODE] = new IPV6BadChar();
|
||||
}
|
||||
|
||||
if ($colons === false) {
|
||||
// We need exactly the right number of groups
|
||||
if ($groupCount !== $maxGroups) {
|
||||
$this->warnings[IPV6GroupCount::CODE] = new IPV6GroupCount();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ($colons !== strrpos($IPv6, '::')) {
|
||||
$this->warnings[IPV6DoubleColon::CODE] = new IPV6DoubleColon();
|
||||
return;
|
||||
}
|
||||
|
||||
if ($colons === 0 || $colons === (strlen($IPv6) - 2)) {
|
||||
// RFC 4291 allows :: at the start or end of an address
|
||||
//with 7 other groups in addition
|
||||
++$maxGroups;
|
||||
}
|
||||
|
||||
if ($groupCount > $maxGroups) {
|
||||
$this->warnings[IPV6MaxGroups::CODE] = new IPV6MaxGroups();
|
||||
} elseif ($groupCount === $maxGroups) {
|
||||
$this->warnings[IPV6Deprecated::CODE] = new IPV6Deprecated();
|
||||
}
|
||||
}
|
||||
|
||||
public function convertIPv4ToIPv6(string $addressLiteralIPv4) : string
|
||||
{
|
||||
$matchesIP = [];
|
||||
$IPv4Match = preg_match(self::IPV4_REGEX, $addressLiteralIPv4, $matchesIP);
|
||||
|
||||
// Extract IPv4 part from the end of the address-literal (if there is one)
|
||||
if ($IPv4Match > 0) {
|
||||
$index = (int) strrpos($addressLiteralIPv4, $matchesIP[0]);
|
||||
//There's a match but it is at the start
|
||||
if ($index > 0) {
|
||||
// Convert IPv4 part to IPv6 format for further testing
|
||||
return substr($addressLiteralIPv4, 0, $index) . '0:0';
|
||||
}
|
||||
}
|
||||
|
||||
return $addressLiteralIPv4;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $addressLiteral
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function checkIPV4Tag($addressLiteral) : bool
|
||||
{
|
||||
$matchesIP = [];
|
||||
$IPv4Match = preg_match(self::IPV4_REGEX, $addressLiteral, $matchesIP);
|
||||
|
||||
// Extract IPv4 part from the end of the address-literal (if there is one)
|
||||
|
||||
if ($IPv4Match > 0) {
|
||||
$index = strrpos($addressLiteral, $matchesIP[0]);
|
||||
//There's a match but it is at the start
|
||||
if ($index === 0) {
|
||||
$this->warnings[AddressLiteral::CODE] = new AddressLiteral();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function addObsoleteWarnings() : void
|
||||
{
|
||||
if(in_array($this->lexer->token['type'], self::OBSOLETE_WARNINGS)) {
|
||||
$this->warnings[ObsoleteDTEXT::CODE] = new ObsoleteDTEXT();
|
||||
}
|
||||
}
|
||||
|
||||
private function addTagWarnings() : void
|
||||
{
|
||||
if ($this->lexer->isNextToken(EmailLexer::S_COLON)) {
|
||||
$this->warnings[IPV6ColonStart::CODE] = new IPV6ColonStart();
|
||||
}
|
||||
if ($this->lexer->isNextToken(EmailLexer::S_IPV6TAG)) {
|
||||
$lexer = clone $this->lexer;
|
||||
$lexer->moveNext();
|
||||
if ($lexer->isNextToken(EmailLexer::S_DOUBLECOLON)) {
|
||||
$this->warnings[IPV6ColonStart::CODE] = new IPV6ColonStart();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
312
plugins/email/vendor/egulias/email-validator/src/Parser/DomainPart.php
vendored
Normal file
312
plugins/email/vendor/egulias/email-validator/src/Parser/DomainPart.php
vendored
Normal file
|
|
@ -0,0 +1,312 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Parser;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Warning\TLD;
|
||||
use Egulias\EmailValidator\Result\Result;
|
||||
use Egulias\EmailValidator\Result\ValidEmail;
|
||||
use Egulias\EmailValidator\Result\InvalidEmail;
|
||||
use Egulias\EmailValidator\Result\Reason\DotAtEnd;
|
||||
use Egulias\EmailValidator\Result\Reason\DotAtStart;
|
||||
use Egulias\EmailValidator\Warning\DeprecatedComment;
|
||||
use Egulias\EmailValidator\Result\Reason\CRLFAtTheEnd;
|
||||
use Egulias\EmailValidator\Result\Reason\LabelTooLong;
|
||||
use Egulias\EmailValidator\Result\Reason\NoDomainPart;
|
||||
use Egulias\EmailValidator\Result\Reason\ConsecutiveAt;
|
||||
use Egulias\EmailValidator\Result\Reason\DomainTooLong;
|
||||
use Egulias\EmailValidator\Result\Reason\CharNotAllowed;
|
||||
use Egulias\EmailValidator\Result\Reason\DomainHyphened;
|
||||
use Egulias\EmailValidator\Result\Reason\ExpectingATEXT;
|
||||
use Egulias\EmailValidator\Parser\CommentStrategy\DomainComment;
|
||||
use Egulias\EmailValidator\Result\Reason\ExpectingDomainLiteralClose;
|
||||
use Egulias\EmailValidator\Parser\DomainLiteral as DomainLiteralParser;
|
||||
|
||||
class DomainPart extends PartParser
|
||||
{
|
||||
const DOMAIN_MAX_LENGTH = 253;
|
||||
const LABEL_MAX_LENGTH = 63;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $domainPart = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $label = '';
|
||||
|
||||
public function parse() : Result
|
||||
{
|
||||
$this->lexer->clearRecorded();
|
||||
$this->lexer->startRecording();
|
||||
|
||||
$this->lexer->moveNext();
|
||||
|
||||
$domainChecks = $this->performDomainStartChecks();
|
||||
if ($domainChecks->isInvalid()) {
|
||||
return $domainChecks;
|
||||
}
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_AT) {
|
||||
return new InvalidEmail(new ConsecutiveAt(), $this->lexer->token['value']);
|
||||
}
|
||||
|
||||
$result = $this->doParseDomainPart();
|
||||
if ($result->isInvalid()) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$end = $this->checkEndOfDomain();
|
||||
if ($end->isInvalid()) {
|
||||
return $end;
|
||||
}
|
||||
|
||||
$this->lexer->stopRecording();
|
||||
$this->domainPart = $this->lexer->getAccumulatedValues();
|
||||
|
||||
$length = strlen($this->domainPart);
|
||||
if ($length > self::DOMAIN_MAX_LENGTH) {
|
||||
return new InvalidEmail(new DomainTooLong(), $this->lexer->token['value']);
|
||||
}
|
||||
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
private function checkEndOfDomain() : Result
|
||||
{
|
||||
$prev = $this->lexer->getPrevious();
|
||||
if ($prev['type'] === EmailLexer::S_DOT) {
|
||||
return new InvalidEmail(new DotAtEnd(), $this->lexer->token['value']);
|
||||
}
|
||||
if ($prev['type'] === EmailLexer::S_HYPHEN) {
|
||||
return new InvalidEmail(new DomainHyphened('Hypen found at the end of the domain'), $prev['value']);
|
||||
}
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_SP) {
|
||||
return new InvalidEmail(new CRLFAtTheEnd(), $prev['value']);
|
||||
}
|
||||
return new ValidEmail();
|
||||
|
||||
}
|
||||
|
||||
private function performDomainStartChecks() : Result
|
||||
{
|
||||
$invalidTokens = $this->checkInvalidTokensAfterAT();
|
||||
if ($invalidTokens->isInvalid()) {
|
||||
return $invalidTokens;
|
||||
}
|
||||
|
||||
$missingDomain = $this->checkEmptyDomain();
|
||||
if ($missingDomain->isInvalid()) {
|
||||
return $missingDomain;
|
||||
}
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) {
|
||||
$this->warnings[DeprecatedComment::CODE] = new DeprecatedComment();
|
||||
}
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
private function checkEmptyDomain() : Result
|
||||
{
|
||||
$thereIsNoDomain = $this->lexer->token['type'] === EmailLexer::S_EMPTY ||
|
||||
($this->lexer->token['type'] === EmailLexer::S_SP &&
|
||||
!$this->lexer->isNextToken(EmailLexer::GENERIC));
|
||||
|
||||
if ($thereIsNoDomain) {
|
||||
return new InvalidEmail(new NoDomainPart(), $this->lexer->token['value']);
|
||||
}
|
||||
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
private function checkInvalidTokensAfterAT() : Result
|
||||
{
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_DOT) {
|
||||
return new InvalidEmail(new DotAtStart(), $this->lexer->token['value']);
|
||||
}
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_HYPHEN) {
|
||||
return new InvalidEmail(new DomainHyphened('After AT'), $this->lexer->token['value']);
|
||||
}
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
protected function parseComments(): Result
|
||||
{
|
||||
$commentParser = new Comment($this->lexer, new DomainComment());
|
||||
$result = $commentParser->parse();
|
||||
$this->warnings = array_merge($this->warnings, $commentParser->getWarnings());
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function doParseDomainPart() : Result
|
||||
{
|
||||
$tldMissing = true;
|
||||
$hasComments = false;
|
||||
$domain = '';
|
||||
do {
|
||||
$prev = $this->lexer->getPrevious();
|
||||
|
||||
$notAllowedChars = $this->checkNotAllowedChars($this->lexer->token);
|
||||
if ($notAllowedChars->isInvalid()) {
|
||||
return $notAllowedChars;
|
||||
}
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS ||
|
||||
$this->lexer->token['type'] === EmailLexer::S_CLOSEPARENTHESIS ) {
|
||||
$hasComments = true;
|
||||
$commentsResult = $this->parseComments();
|
||||
|
||||
//Invalid comment parsing
|
||||
if($commentsResult->isInvalid()) {
|
||||
return $commentsResult;
|
||||
}
|
||||
}
|
||||
|
||||
$dotsResult = $this->checkConsecutiveDots();
|
||||
if ($dotsResult->isInvalid()) {
|
||||
return $dotsResult;
|
||||
}
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_OPENBRACKET) {
|
||||
$literalResult = $this->parseDomainLiteral();
|
||||
|
||||
$this->addTLDWarnings($tldMissing);
|
||||
return $literalResult;
|
||||
}
|
||||
|
||||
$labelCheck = $this->checkLabelLength();
|
||||
if ($labelCheck->isInvalid()) {
|
||||
return $labelCheck;
|
||||
}
|
||||
|
||||
$FwsResult = $this->parseFWS();
|
||||
if($FwsResult->isInvalid()) {
|
||||
return $FwsResult;
|
||||
}
|
||||
|
||||
$domain .= $this->lexer->token['value'];
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_DOT && $this->lexer->isNextToken(EmailLexer::GENERIC)) {
|
||||
$tldMissing = false;
|
||||
}
|
||||
|
||||
$exceptionsResult = $this->checkDomainPartExceptions($prev, $hasComments);
|
||||
if ($exceptionsResult->isInvalid()) {
|
||||
return $exceptionsResult;
|
||||
}
|
||||
$this->lexer->moveNext();
|
||||
|
||||
} while (null !== $this->lexer->token['type']);
|
||||
|
||||
$labelCheck = $this->checkLabelLength(true);
|
||||
if ($labelCheck->isInvalid()) {
|
||||
return $labelCheck;
|
||||
}
|
||||
$this->addTLDWarnings($tldMissing);
|
||||
|
||||
$this->domainPart = $domain;
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
private function checkNotAllowedChars(array $token) : Result
|
||||
{
|
||||
$notAllowed = [EmailLexer::S_BACKSLASH => true, EmailLexer::S_SLASH=> true];
|
||||
if (isset($notAllowed[$token['type']])) {
|
||||
return new InvalidEmail(new CharNotAllowed(), $token['value']);
|
||||
}
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Result
|
||||
*/
|
||||
protected function parseDomainLiteral() : Result
|
||||
{
|
||||
try {
|
||||
$this->lexer->find(EmailLexer::S_CLOSEBRACKET);
|
||||
} catch (\RuntimeException $e) {
|
||||
return new InvalidEmail(new ExpectingDomainLiteralClose(), $this->lexer->token['value']);
|
||||
}
|
||||
|
||||
$domainLiteralParser = new DomainLiteralParser($this->lexer);
|
||||
$result = $domainLiteralParser->parse();
|
||||
$this->warnings = array_merge($this->warnings, $domainLiteralParser->getWarnings());
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function checkDomainPartExceptions(array $prev, bool $hasComments) : Result
|
||||
{
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_OPENBRACKET && $prev['type'] !== EmailLexer::S_AT) {
|
||||
return new InvalidEmail(new ExpectingATEXT('OPENBRACKET not after AT'), $this->lexer->token['value']);
|
||||
}
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_HYPHEN && $this->lexer->isNextToken(EmailLexer::S_DOT)) {
|
||||
return new InvalidEmail(new DomainHyphened('Hypen found near DOT'), $this->lexer->token['value']);
|
||||
}
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH
|
||||
&& $this->lexer->isNextToken(EmailLexer::GENERIC)) {
|
||||
return new InvalidEmail(new ExpectingATEXT('Escaping following "ATOM"'), $this->lexer->token['value']);
|
||||
}
|
||||
|
||||
return $this->validateTokens($hasComments);
|
||||
}
|
||||
|
||||
protected function validateTokens(bool $hasComments) : Result
|
||||
{
|
||||
$validDomainTokens = array(
|
||||
EmailLexer::GENERIC => true,
|
||||
EmailLexer::S_HYPHEN => true,
|
||||
EmailLexer::S_DOT => true,
|
||||
);
|
||||
|
||||
if ($hasComments) {
|
||||
$validDomainTokens[EmailLexer::S_OPENPARENTHESIS] = true;
|
||||
$validDomainTokens[EmailLexer::S_CLOSEPARENTHESIS] = true;
|
||||
}
|
||||
|
||||
if (!isset($validDomainTokens[$this->lexer->token['type']])) {
|
||||
return new InvalidEmail(new ExpectingATEXT('Invalid token in domain: ' . $this->lexer->token['value']), $this->lexer->token['value']);
|
||||
}
|
||||
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
private function checkLabelLength(bool $isEndOfDomain = false) : Result
|
||||
{
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_DOT || $isEndOfDomain) {
|
||||
if ($this->isLabelTooLong($this->label)) {
|
||||
return new InvalidEmail(new LabelTooLong(), $this->lexer->token['value']);
|
||||
}
|
||||
$this->label = '';
|
||||
}
|
||||
$this->label .= $this->lexer->token['value'];
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
|
||||
private function isLabelTooLong(string $label) : bool
|
||||
{
|
||||
if (preg_match('/[^\x00-\x7F]/', $label)) {
|
||||
idn_to_ascii($label, IDNA_DEFAULT, INTL_IDNA_VARIANT_UTS46, $idnaInfo);
|
||||
return (bool) ($idnaInfo['errors'] & IDNA_ERROR_LABEL_TOO_LONG);
|
||||
}
|
||||
return strlen($label) > self::LABEL_MAX_LENGTH;
|
||||
}
|
||||
|
||||
private function addTLDWarnings(bool $isTLDMissing) : void
|
||||
{
|
||||
if ($isTLDMissing) {
|
||||
$this->warnings[TLD::CODE] = new TLD();
|
||||
}
|
||||
}
|
||||
|
||||
public function domainPart() : string
|
||||
{
|
||||
return $this->domainPart;
|
||||
}
|
||||
}
|
||||
88
plugins/email/vendor/egulias/email-validator/src/Parser/DoubleQuote.php
vendored
Normal file
88
plugins/email/vendor/egulias/email-validator/src/Parser/DoubleQuote.php
vendored
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
namespace Egulias\EmailValidator\Parser;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Parser\Parser;
|
||||
use Egulias\EmailValidator\Result\ValidEmail;
|
||||
use Egulias\EmailValidator\Result\InvalidEmail;
|
||||
use Egulias\EmailValidator\Warning\CFWSWithFWS;
|
||||
use Egulias\EmailValidator\Warning\QuotedString;
|
||||
use Egulias\EmailValidator\Result\Reason\ExpectingATEXT;
|
||||
use Egulias\EmailValidator\Result\Reason\UnclosedQuotedString;
|
||||
use Egulias\EmailValidator\Result\Result;
|
||||
|
||||
class DoubleQuote extends PartParser
|
||||
{
|
||||
public function parse() : Result
|
||||
{
|
||||
|
||||
$validQuotedString = $this->checkDQUOTE();
|
||||
if($validQuotedString->isInvalid()) return $validQuotedString;
|
||||
|
||||
$special = [
|
||||
EmailLexer::S_CR => true,
|
||||
EmailLexer::S_HTAB => true,
|
||||
EmailLexer::S_LF => true
|
||||
];
|
||||
|
||||
$invalid = [
|
||||
EmailLexer::C_NUL => true,
|
||||
EmailLexer::S_HTAB => true,
|
||||
EmailLexer::S_CR => true,
|
||||
EmailLexer::S_LF => true
|
||||
];
|
||||
|
||||
$setSpecialsWarning = true;
|
||||
|
||||
$this->lexer->moveNext();
|
||||
|
||||
while ($this->lexer->token['type'] !== EmailLexer::S_DQUOTE && null !== $this->lexer->token['type']) {
|
||||
if (isset($special[$this->lexer->token['type']]) && $setSpecialsWarning) {
|
||||
$this->warnings[CFWSWithFWS::CODE] = new CFWSWithFWS();
|
||||
$setSpecialsWarning = false;
|
||||
}
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH && $this->lexer->isNextToken(EmailLexer::S_DQUOTE)) {
|
||||
$this->lexer->moveNext();
|
||||
}
|
||||
|
||||
$this->lexer->moveNext();
|
||||
|
||||
if (!$this->escaped() && isset($invalid[$this->lexer->token['type']])) {
|
||||
return new InvalidEmail(new ExpectingATEXT("Expecting ATEXT between DQUOTE"), $this->lexer->token['value']);
|
||||
}
|
||||
}
|
||||
|
||||
$prev = $this->lexer->getPrevious();
|
||||
|
||||
if ($prev['type'] === EmailLexer::S_BACKSLASH) {
|
||||
$validQuotedString = $this->checkDQUOTE();
|
||||
if($validQuotedString->isInvalid()) return $validQuotedString;
|
||||
}
|
||||
|
||||
if (!$this->lexer->isNextToken(EmailLexer::S_AT) && $prev['type'] !== EmailLexer::S_BACKSLASH) {
|
||||
return new InvalidEmail(new ExpectingATEXT("Expecting ATEXT between DQUOTE"), $this->lexer->token['value']);
|
||||
}
|
||||
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
protected function checkDQUOTE() : Result
|
||||
{
|
||||
$previous = $this->lexer->getPrevious();
|
||||
|
||||
if ($this->lexer->isNextToken(EmailLexer::GENERIC) && $previous['type'] === EmailLexer::GENERIC) {
|
||||
$description = 'https://tools.ietf.org/html/rfc5322#section-3.2.4 - quoted string should be a unit';
|
||||
return new InvalidEmail(new ExpectingATEXT($description), $this->lexer->token['value']);
|
||||
}
|
||||
|
||||
try {
|
||||
$this->lexer->find(EmailLexer::S_DQUOTE);
|
||||
} catch (\Exception $e) {
|
||||
return new InvalidEmail(new UnclosedQuotedString(), $this->lexer->token['value']);
|
||||
}
|
||||
$this->warnings[QuotedString::CODE] = new QuotedString($previous['value'], $this->lexer->token['value']);
|
||||
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
}
|
||||
86
plugins/email/vendor/egulias/email-validator/src/Parser/FoldingWhiteSpace.php
vendored
Normal file
86
plugins/email/vendor/egulias/email-validator/src/Parser/FoldingWhiteSpace.php
vendored
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
namespace Egulias\EmailValidator\Parser;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Warning\CFWSNearAt;
|
||||
use Egulias\EmailValidator\Result\InvalidEmail;
|
||||
use Egulias\EmailValidator\Warning\CFWSWithFWS;
|
||||
use Egulias\EmailValidator\Result\Reason\CRNoLF;
|
||||
use Egulias\EmailValidator\Result\Reason\AtextAfterCFWS;
|
||||
use Egulias\EmailValidator\Result\Reason\CRLFAtTheEnd;
|
||||
use Egulias\EmailValidator\Result\Reason\CRLFX2;
|
||||
use Egulias\EmailValidator\Result\Reason\ExpectingCTEXT;
|
||||
use Egulias\EmailValidator\Result\Result;
|
||||
use Egulias\EmailValidator\Result\ValidEmail;
|
||||
|
||||
class FoldingWhiteSpace extends PartParser
|
||||
{
|
||||
const FWS_TYPES = [
|
||||
EmailLexer::S_SP,
|
||||
EmailLexer::S_HTAB,
|
||||
EmailLexer::S_CR,
|
||||
EmailLexer::S_LF,
|
||||
EmailLexer::CRLF
|
||||
];
|
||||
|
||||
public function parse() : Result
|
||||
{
|
||||
if (!$this->isFWS()) {
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
$previous = $this->lexer->getPrevious();
|
||||
|
||||
$resultCRLF = $this->checkCRLFInFWS();
|
||||
if ($resultCRLF->isInvalid()) {
|
||||
return $resultCRLF;
|
||||
}
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_CR) {
|
||||
return new InvalidEmail(new CRNoLF(), $this->lexer->token['value']);
|
||||
}
|
||||
|
||||
if ($this->lexer->isNextToken(EmailLexer::GENERIC) && $previous['type'] !== EmailLexer::S_AT) {
|
||||
return new InvalidEmail(new AtextAfterCFWS(), $this->lexer->token['value']);
|
||||
}
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_LF || $this->lexer->token['type'] === EmailLexer::C_NUL) {
|
||||
return new InvalidEmail(new ExpectingCTEXT(), $this->lexer->token['value']);
|
||||
}
|
||||
|
||||
if ($this->lexer->isNextToken(EmailLexer::S_AT) || $previous['type'] === EmailLexer::S_AT) {
|
||||
$this->warnings[CFWSNearAt::CODE] = new CFWSNearAt();
|
||||
} else {
|
||||
$this->warnings[CFWSWithFWS::CODE] = new CFWSWithFWS();
|
||||
}
|
||||
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
protected function checkCRLFInFWS() : Result
|
||||
{
|
||||
if ($this->lexer->token['type'] !== EmailLexer::CRLF) {
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB))) {
|
||||
return new InvalidEmail(new CRLFX2(), $this->lexer->token['value']);
|
||||
}
|
||||
|
||||
//this has no coverage. Condition is repeated from above one
|
||||
if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB))) {
|
||||
return new InvalidEmail(new CRLFAtTheEnd(), $this->lexer->token['value']);
|
||||
}
|
||||
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
protected function isFWS() : bool
|
||||
{
|
||||
if ($this->escaped()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return in_array($this->lexer->token['type'], self::FWS_TYPES);
|
||||
}
|
||||
}
|
||||
16
plugins/email/vendor/egulias/email-validator/src/Parser/IDLeftPart.php
vendored
Normal file
16
plugins/email/vendor/egulias/email-validator/src/Parser/IDLeftPart.php
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Parser;
|
||||
|
||||
use Egulias\EmailValidator\Result\Result;
|
||||
use Egulias\EmailValidator\Parser\LocalPart;
|
||||
use Egulias\EmailValidator\Result\InvalidEmail;
|
||||
use Egulias\EmailValidator\Result\Reason\CommentsInIDRight;
|
||||
|
||||
class IDLeftPart extends LocalPart
|
||||
{
|
||||
protected function parseComments(): Result
|
||||
{
|
||||
return new InvalidEmail(new CommentsInIDRight(), $this->lexer->token['value']);
|
||||
}
|
||||
}
|
||||
29
plugins/email/vendor/egulias/email-validator/src/Parser/IDRightPart.php
vendored
Normal file
29
plugins/email/vendor/egulias/email-validator/src/Parser/IDRightPart.php
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Parser;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Result\Result;
|
||||
use Egulias\EmailValidator\Result\ValidEmail;
|
||||
use Egulias\EmailValidator\Result\InvalidEmail;
|
||||
use Egulias\EmailValidator\Result\Reason\ExpectingATEXT;
|
||||
|
||||
class IDRightPart extends DomainPart
|
||||
{
|
||||
protected function validateTokens(bool $hasComments) : Result
|
||||
{
|
||||
$invalidDomainTokens = [
|
||||
EmailLexer::S_DQUOTE => true,
|
||||
EmailLexer::S_SQUOTE => true,
|
||||
EmailLexer::S_BACKTICK => true,
|
||||
EmailLexer::S_SEMICOLON => true,
|
||||
EmailLexer::S_GREATERTHAN => true,
|
||||
EmailLexer::S_LOWERTHAN => true,
|
||||
];
|
||||
|
||||
if (isset($invalidDomainTokens[$this->lexer->token['type']])) {
|
||||
return new InvalidEmail(new ExpectingATEXT('Invalid token in domain: ' . $this->lexer->token['value']), $this->lexer->token['value']);
|
||||
}
|
||||
return new ValidEmail();
|
||||
}
|
||||
}
|
||||
165
plugins/email/vendor/egulias/email-validator/src/Parser/LocalPart.php
vendored
Normal file
165
plugins/email/vendor/egulias/email-validator/src/Parser/LocalPart.php
vendored
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Parser;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Result\Result;
|
||||
use Egulias\EmailValidator\Result\ValidEmail;
|
||||
use Egulias\EmailValidator\Result\InvalidEmail;
|
||||
use Egulias\EmailValidator\Warning\LocalTooLong;
|
||||
use Egulias\EmailValidator\Result\Reason\DotAtEnd;
|
||||
use Egulias\EmailValidator\Result\Reason\DotAtStart;
|
||||
use Egulias\EmailValidator\Result\Reason\ConsecutiveDot;
|
||||
use Egulias\EmailValidator\Result\Reason\ExpectingATEXT;
|
||||
use Egulias\EmailValidator\Parser\CommentStrategy\LocalComment;
|
||||
|
||||
class LocalPart extends PartParser
|
||||
{
|
||||
const INVALID_TOKENS = [
|
||||
EmailLexer::S_COMMA => EmailLexer::S_COMMA,
|
||||
EmailLexer::S_CLOSEBRACKET => EmailLexer::S_CLOSEBRACKET,
|
||||
EmailLexer::S_OPENBRACKET => EmailLexer::S_OPENBRACKET,
|
||||
EmailLexer::S_GREATERTHAN => EmailLexer::S_GREATERTHAN,
|
||||
EmailLexer::S_LOWERTHAN => EmailLexer::S_LOWERTHAN,
|
||||
EmailLexer::S_COLON => EmailLexer::S_COLON,
|
||||
EmailLexer::S_SEMICOLON => EmailLexer::S_SEMICOLON,
|
||||
EmailLexer::INVALID => EmailLexer::INVALID
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $localPart = '';
|
||||
|
||||
|
||||
public function parse() : Result
|
||||
{
|
||||
$this->lexer->startRecording();
|
||||
|
||||
while ($this->lexer->token['type'] !== EmailLexer::S_AT && null !== $this->lexer->token['type']) {
|
||||
if ($this->hasDotAtStart()) {
|
||||
return new InvalidEmail(new DotAtStart(), $this->lexer->token['value']);
|
||||
}
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_DQUOTE) {
|
||||
$dquoteParsingResult = $this->parseDoubleQuote();
|
||||
|
||||
//Invalid double quote parsing
|
||||
if($dquoteParsingResult->isInvalid()) {
|
||||
return $dquoteParsingResult;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS ||
|
||||
$this->lexer->token['type'] === EmailLexer::S_CLOSEPARENTHESIS ) {
|
||||
$commentsResult = $this->parseComments();
|
||||
|
||||
//Invalid comment parsing
|
||||
if($commentsResult->isInvalid()) {
|
||||
return $commentsResult;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_DOT && $this->lexer->isNextToken(EmailLexer::S_DOT)) {
|
||||
return new InvalidEmail(new ConsecutiveDot(), $this->lexer->token['value']);
|
||||
}
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_DOT &&
|
||||
$this->lexer->isNextToken(EmailLexer::S_AT)
|
||||
) {
|
||||
return new InvalidEmail(new DotAtEnd(), $this->lexer->token['value']);
|
||||
}
|
||||
|
||||
$resultEscaping = $this->validateEscaping();
|
||||
if ($resultEscaping->isInvalid()) {
|
||||
return $resultEscaping;
|
||||
}
|
||||
|
||||
$resultToken = $this->validateTokens(false);
|
||||
if ($resultToken->isInvalid()) {
|
||||
return $resultToken;
|
||||
}
|
||||
|
||||
$resultFWS = $this->parseLocalFWS();
|
||||
if($resultFWS->isInvalid()) {
|
||||
return $resultFWS;
|
||||
}
|
||||
|
||||
$this->lexer->moveNext();
|
||||
}
|
||||
|
||||
$this->lexer->stopRecording();
|
||||
$this->localPart = rtrim($this->lexer->getAccumulatedValues(), '@');
|
||||
if (strlen($this->localPart) > LocalTooLong::LOCAL_PART_LENGTH) {
|
||||
$this->warnings[LocalTooLong::CODE] = new LocalTooLong();
|
||||
}
|
||||
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
protected function validateTokens(bool $hasComments) : Result
|
||||
{
|
||||
if (isset(self::INVALID_TOKENS[$this->lexer->token['type']])) {
|
||||
return new InvalidEmail(new ExpectingATEXT('Invalid token found'), $this->lexer->token['value']);
|
||||
}
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
public function localPart() : string
|
||||
{
|
||||
return $this->localPart;
|
||||
}
|
||||
|
||||
private function parseLocalFWS() : Result
|
||||
{
|
||||
$foldingWS = new FoldingWhiteSpace($this->lexer);
|
||||
$resultFWS = $foldingWS->parse();
|
||||
if ($resultFWS->isValid()) {
|
||||
$this->warnings = array_merge($this->warnings, $foldingWS->getWarnings());
|
||||
}
|
||||
return $resultFWS;
|
||||
}
|
||||
|
||||
private function hasDotAtStart() : bool
|
||||
{
|
||||
return $this->lexer->token['type'] === EmailLexer::S_DOT && null === $this->lexer->getPrevious()['type'];
|
||||
}
|
||||
|
||||
private function parseDoubleQuote() : Result
|
||||
{
|
||||
$dquoteParser = new DoubleQuote($this->lexer);
|
||||
$parseAgain = $dquoteParser->parse();
|
||||
$this->warnings = array_merge($this->warnings, $dquoteParser->getWarnings());
|
||||
|
||||
return $parseAgain;
|
||||
}
|
||||
|
||||
protected function parseComments(): Result
|
||||
{
|
||||
$commentParser = new Comment($this->lexer, new LocalComment());
|
||||
$result = $commentParser->parse();
|
||||
$this->warnings = array_merge($this->warnings, $commentParser->getWarnings());
|
||||
if($result->isInvalid()) {
|
||||
return $result;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function validateEscaping() : Result
|
||||
{
|
||||
//Backslash found
|
||||
if ($this->lexer->token['type'] !== EmailLexer::S_BACKSLASH) {
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
if ($this->lexer->isNextToken(EmailLexer::GENERIC)) {
|
||||
return new InvalidEmail(new ExpectingATEXT('Found ATOM after escaping'), $this->lexer->token['value']);
|
||||
}
|
||||
|
||||
if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB, EmailLexer::C_DEL))) {
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
return new ValidEmail();
|
||||
}
|
||||
}
|
||||
63
plugins/email/vendor/egulias/email-validator/src/Parser/PartParser.php
vendored
Normal file
63
plugins/email/vendor/egulias/email-validator/src/Parser/PartParser.php
vendored
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Parser;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Result\InvalidEmail;
|
||||
use Egulias\EmailValidator\Result\Reason\ConsecutiveDot;
|
||||
use Egulias\EmailValidator\Result\Result;
|
||||
use Egulias\EmailValidator\Result\ValidEmail;
|
||||
|
||||
abstract class PartParser
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $warnings = [];
|
||||
|
||||
/**
|
||||
* @var EmailLexer
|
||||
*/
|
||||
protected $lexer;
|
||||
|
||||
public function __construct(EmailLexer $lexer)
|
||||
{
|
||||
$this->lexer = $lexer;
|
||||
}
|
||||
|
||||
abstract public function parse() : Result;
|
||||
|
||||
/**
|
||||
* @return \Egulias\EmailValidator\Warning\Warning[]
|
||||
*/
|
||||
public function getWarnings()
|
||||
{
|
||||
return $this->warnings;
|
||||
}
|
||||
|
||||
protected function parseFWS() : Result
|
||||
{
|
||||
$foldingWS = new FoldingWhiteSpace($this->lexer);
|
||||
$resultFWS = $foldingWS->parse();
|
||||
$this->warnings = array_merge($this->warnings, $foldingWS->getWarnings());
|
||||
return $resultFWS;
|
||||
}
|
||||
|
||||
protected function checkConsecutiveDots() : Result
|
||||
{
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_DOT && $this->lexer->isNextToken(EmailLexer::S_DOT)) {
|
||||
return new InvalidEmail(new ConsecutiveDot(), $this->lexer->token['value']);
|
||||
}
|
||||
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
protected function escaped() : bool
|
||||
{
|
||||
$previous = $this->lexer->getPrevious();
|
||||
|
||||
return $previous && $previous['type'] === EmailLexer::S_BACKSLASH
|
||||
&&
|
||||
$this->lexer->token['type'] !== EmailLexer::GENERIC;
|
||||
}
|
||||
}
|
||||
46
plugins/email/vendor/egulias/email-validator/src/Result/InvalidEmail.php
vendored
Normal file
46
plugins/email/vendor/egulias/email-validator/src/Result/InvalidEmail.php
vendored
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Result;
|
||||
|
||||
use Egulias\EmailValidator\Result\Reason\Reason;
|
||||
|
||||
class InvalidEmail implements Result
|
||||
{
|
||||
private $token;
|
||||
/**
|
||||
* @var Reason
|
||||
*/
|
||||
protected $reason;
|
||||
|
||||
public function __construct(Reason $reason, string $token)
|
||||
{
|
||||
$this->token = $token;
|
||||
$this->reason = $reason;
|
||||
}
|
||||
|
||||
public function isValid(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isInvalid(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function description(): string
|
||||
{
|
||||
return $this->reason->description() . " in char " . $this->token;
|
||||
}
|
||||
|
||||
public function code(): int
|
||||
{
|
||||
return $this->reason->code();
|
||||
}
|
||||
|
||||
public function reason() : Reason
|
||||
{
|
||||
return $this->reason;
|
||||
}
|
||||
|
||||
}
|
||||
56
plugins/email/vendor/egulias/email-validator/src/Result/MultipleErrors.php
vendored
Normal file
56
plugins/email/vendor/egulias/email-validator/src/Result/MultipleErrors.php
vendored
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Result;
|
||||
|
||||
use Egulias\EmailValidator\Result\Reason\EmptyReason;
|
||||
use Egulias\EmailValidator\Result\Reason\Reason;
|
||||
|
||||
/**
|
||||
* @psalm-suppress PropertyNotSetInConstructor
|
||||
*/
|
||||
class MultipleErrors extends InvalidEmail
|
||||
{
|
||||
/**
|
||||
* @var Reason[]
|
||||
*/
|
||||
private $reasons = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public function addReason(Reason $reason) : void
|
||||
{
|
||||
$this->reasons[$reason->code()] = $reason;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Reason[]
|
||||
*/
|
||||
public function getReasons() : array
|
||||
{
|
||||
return $this->reasons;
|
||||
}
|
||||
|
||||
public function reason() : Reason
|
||||
{
|
||||
return 0 !== count($this->reasons)
|
||||
? current($this->reasons)
|
||||
: new EmptyReason();
|
||||
}
|
||||
|
||||
public function description() : string
|
||||
{
|
||||
$description = '';
|
||||
foreach($this->reasons as $reason) {
|
||||
$description .= $reason->description() . PHP_EOL;
|
||||
}
|
||||
|
||||
return $description;
|
||||
}
|
||||
|
||||
public function code() : int
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/AtextAfterCFWS.php
vendored
Normal file
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/AtextAfterCFWS.php
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Result\Reason;
|
||||
|
||||
class AtextAfterCFWS implements Reason
|
||||
{
|
||||
public function code() : int
|
||||
{
|
||||
return 133;
|
||||
}
|
||||
|
||||
public function description() : string
|
||||
{
|
||||
return 'ATEXT found after CFWS';
|
||||
}
|
||||
}
|
||||
19
plugins/email/vendor/egulias/email-validator/src/Result/Reason/CRLFAtTheEnd.php
vendored
Normal file
19
plugins/email/vendor/egulias/email-validator/src/Result/Reason/CRLFAtTheEnd.php
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Result\Reason;
|
||||
|
||||
class CRLFAtTheEnd implements Reason
|
||||
{
|
||||
const CODE = 149;
|
||||
const REASON = "CRLF at the end";
|
||||
|
||||
public function code() : int
|
||||
{
|
||||
return 149;
|
||||
}
|
||||
|
||||
public function description() : string
|
||||
{
|
||||
return 'CRLF at the end';
|
||||
}
|
||||
}
|
||||
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/CRLFX2.php
vendored
Normal file
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/CRLFX2.php
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Result\Reason;
|
||||
|
||||
class CRLFX2 implements Reason
|
||||
{
|
||||
public function code() : int
|
||||
{
|
||||
return 148;
|
||||
}
|
||||
|
||||
public function description() : string
|
||||
{
|
||||
return 'CR LF tokens found twice';
|
||||
}
|
||||
}
|
||||
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/CRNoLF.php
vendored
Normal file
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/CRNoLF.php
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Result\Reason;
|
||||
|
||||
class CRNoLF implements Reason
|
||||
{
|
||||
public function code() : int
|
||||
{
|
||||
return 150;
|
||||
}
|
||||
|
||||
public function description() : string
|
||||
{
|
||||
return 'Missing LF after CR';
|
||||
}
|
||||
}
|
||||
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/CharNotAllowed.php
vendored
Normal file
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/CharNotAllowed.php
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Result\Reason;
|
||||
|
||||
class CharNotAllowed implements Reason
|
||||
{
|
||||
public function code() : int
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
public function description() : string
|
||||
{
|
||||
return "Character not allowed";
|
||||
}
|
||||
}
|
||||
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/CommaInDomain.php
vendored
Normal file
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/CommaInDomain.php
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Result\Reason;
|
||||
|
||||
class CommaInDomain implements Reason
|
||||
{
|
||||
public function code() : int
|
||||
{
|
||||
return 200;
|
||||
}
|
||||
|
||||
public function description() : string
|
||||
{
|
||||
return "Comma ',' is not allowed in domain part";
|
||||
}
|
||||
}
|
||||
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/CommentsInIDRight.php
vendored
Normal file
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/CommentsInIDRight.php
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Result\Reason;
|
||||
|
||||
class CommentsInIDRight implements Reason
|
||||
{
|
||||
public function code() : int
|
||||
{
|
||||
return 400;
|
||||
}
|
||||
|
||||
public function description() : string
|
||||
{
|
||||
return 'Comments are not allowed in IDRight for message-id';
|
||||
}
|
||||
}
|
||||
17
plugins/email/vendor/egulias/email-validator/src/Result/Reason/ConsecutiveAt.php
vendored
Normal file
17
plugins/email/vendor/egulias/email-validator/src/Result/Reason/ConsecutiveAt.php
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Result\Reason;
|
||||
|
||||
class ConsecutiveAt implements Reason
|
||||
{
|
||||
public function code() : int
|
||||
{
|
||||
return 128;
|
||||
}
|
||||
|
||||
public function description() : string
|
||||
{
|
||||
return '@ found after another @';
|
||||
}
|
||||
|
||||
}
|
||||
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/ConsecutiveDot.php
vendored
Normal file
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/ConsecutiveDot.php
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Result\Reason;
|
||||
|
||||
class ConsecutiveDot implements Reason
|
||||
{
|
||||
public function code() : int
|
||||
{
|
||||
return 132;
|
||||
}
|
||||
|
||||
public function description() : string
|
||||
{
|
||||
return 'Concecutive DOT found';
|
||||
}
|
||||
}
|
||||
13
plugins/email/vendor/egulias/email-validator/src/Result/Reason/DetailedReason.php
vendored
Normal file
13
plugins/email/vendor/egulias/email-validator/src/Result/Reason/DetailedReason.php
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Result\Reason;
|
||||
|
||||
abstract class DetailedReason implements Reason
|
||||
{
|
||||
protected $detailedDescription;
|
||||
|
||||
public function __construct(string $details)
|
||||
{
|
||||
$this->detailedDescription = $details;
|
||||
}
|
||||
}
|
||||
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/DomainAcceptsNoMail.php
vendored
Normal file
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/DomainAcceptsNoMail.php
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Result\Reason;
|
||||
|
||||
class DomainAcceptsNoMail implements Reason
|
||||
{
|
||||
public function code() : int
|
||||
{
|
||||
return 154;
|
||||
}
|
||||
|
||||
public function description() : string
|
||||
{
|
||||
return 'Domain accepts no mail (Null MX, RFC7505)';
|
||||
}
|
||||
}
|
||||
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/DomainHyphened.php
vendored
Normal file
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/DomainHyphened.php
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Result\Reason;
|
||||
|
||||
class DomainHyphened extends DetailedReason
|
||||
{
|
||||
public function code() : int
|
||||
{
|
||||
return 144;
|
||||
}
|
||||
|
||||
public function description() : string
|
||||
{
|
||||
return 'S_HYPHEN found in domain';
|
||||
}
|
||||
}
|
||||
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/DomainTooLong.php
vendored
Normal file
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/DomainTooLong.php
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Result\Reason;
|
||||
|
||||
class DomainTooLong implements Reason
|
||||
{
|
||||
public function code() : int
|
||||
{
|
||||
return 244;
|
||||
}
|
||||
|
||||
public function description() : string
|
||||
{
|
||||
return 'Domain is longer than 253 characters';
|
||||
}
|
||||
}
|
||||
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/DotAtEnd.php
vendored
Normal file
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/DotAtEnd.php
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Result\Reason;
|
||||
|
||||
class DotAtEnd implements Reason
|
||||
{
|
||||
public function code() : int
|
||||
{
|
||||
return 142;
|
||||
}
|
||||
|
||||
public function description() : string
|
||||
{
|
||||
return 'Dot at the end';
|
||||
}
|
||||
}
|
||||
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/DotAtStart.php
vendored
Normal file
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/DotAtStart.php
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Result\Reason;
|
||||
|
||||
class DotAtStart implements Reason
|
||||
{
|
||||
public function code() : int
|
||||
{
|
||||
return 141;
|
||||
}
|
||||
|
||||
public function description() : string
|
||||
{
|
||||
return "Starts with a DOT";
|
||||
}
|
||||
}
|
||||
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/EmptyReason.php
vendored
Normal file
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/EmptyReason.php
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Result\Reason;
|
||||
|
||||
class EmptyReason implements Reason
|
||||
{
|
||||
public function code() : int
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function description() : string
|
||||
{
|
||||
return 'Empty reason';
|
||||
}
|
||||
}
|
||||
26
plugins/email/vendor/egulias/email-validator/src/Result/Reason/ExceptionFound.php
vendored
Normal file
26
plugins/email/vendor/egulias/email-validator/src/Result/Reason/ExceptionFound.php
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Result\Reason;
|
||||
|
||||
class ExceptionFound implements Reason
|
||||
{
|
||||
/**
|
||||
* @var \Exception
|
||||
*/
|
||||
private $exception;
|
||||
|
||||
public function __construct(\Exception $exception)
|
||||
{
|
||||
$this->exception = $exception;
|
||||
|
||||
}
|
||||
public function code() : int
|
||||
{
|
||||
return 999;
|
||||
}
|
||||
|
||||
public function description() : string
|
||||
{
|
||||
return $this->exception->getMessage();
|
||||
}
|
||||
}
|
||||
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/ExpectingATEXT.php
vendored
Normal file
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/ExpectingATEXT.php
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Result\Reason;
|
||||
|
||||
class ExpectingATEXT extends DetailedReason
|
||||
{
|
||||
public function code() : int
|
||||
{
|
||||
return 137;
|
||||
}
|
||||
|
||||
public function description() : string
|
||||
{
|
||||
return "Expecting ATEXT (Printable US-ASCII). Extended: " . $this->detailedDescription;
|
||||
}
|
||||
}
|
||||
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/ExpectingCTEXT.php
vendored
Normal file
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/ExpectingCTEXT.php
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Result\Reason;
|
||||
|
||||
class ExpectingCTEXT implements Reason
|
||||
{
|
||||
public function code() : int
|
||||
{
|
||||
return 139;
|
||||
}
|
||||
|
||||
public function description() : string
|
||||
{
|
||||
return 'Expecting CTEXT';
|
||||
}
|
||||
}
|
||||
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/ExpectingDTEXT.php
vendored
Normal file
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/ExpectingDTEXT.php
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Result\Reason;
|
||||
|
||||
class ExpectingDTEXT implements Reason
|
||||
{
|
||||
public function code() : int
|
||||
{
|
||||
return 129;
|
||||
}
|
||||
|
||||
public function description() : string
|
||||
{
|
||||
return 'Expecting DTEXT';
|
||||
}
|
||||
}
|
||||
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/ExpectingDomainLiteralClose.php
vendored
Normal file
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/ExpectingDomainLiteralClose.php
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Result\Reason;
|
||||
|
||||
class ExpectingDomainLiteralClose implements Reason
|
||||
{
|
||||
public function code() : int
|
||||
{
|
||||
return 137;
|
||||
}
|
||||
|
||||
public function description() : string
|
||||
{
|
||||
return "Closing bracket ']' for domain literal not found";
|
||||
}
|
||||
}
|
||||
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/LabelTooLong.php
vendored
Normal file
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/LabelTooLong.php
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Result\Reason;
|
||||
|
||||
class LabelTooLong implements Reason
|
||||
{
|
||||
public function code() : int
|
||||
{
|
||||
return 245;
|
||||
}
|
||||
|
||||
public function description() : string
|
||||
{
|
||||
return 'Domain "label" is longer than 63 characters';
|
||||
}
|
||||
}
|
||||
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/LocalOrReservedDomain.php
vendored
Normal file
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/LocalOrReservedDomain.php
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Result\Reason;
|
||||
|
||||
class LocalOrReservedDomain implements Reason
|
||||
{
|
||||
public function code() : int
|
||||
{
|
||||
return 153;
|
||||
}
|
||||
|
||||
public function description() : string
|
||||
{
|
||||
return 'Local, mDNS or reserved domain (RFC2606, RFC6762)';
|
||||
}
|
||||
}
|
||||
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/NoDNSRecord.php
vendored
Normal file
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/NoDNSRecord.php
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Result\Reason;
|
||||
|
||||
class NoDNSRecord implements Reason
|
||||
{
|
||||
public function code() : int
|
||||
{
|
||||
return 5;
|
||||
}
|
||||
|
||||
public function description() : string
|
||||
{
|
||||
return 'No MX or A DSN record was found for this email';
|
||||
}
|
||||
}
|
||||
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/NoDomainPart.php
vendored
Normal file
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/NoDomainPart.php
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Result\Reason;
|
||||
|
||||
class NoDomainPart implements Reason
|
||||
{
|
||||
public function code() : int
|
||||
{
|
||||
return 131;
|
||||
}
|
||||
|
||||
public function description() : string
|
||||
{
|
||||
return 'No domain part found';
|
||||
}
|
||||
}
|
||||
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/NoLocalPart.php
vendored
Normal file
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/NoLocalPart.php
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Result\Reason;
|
||||
|
||||
class NoLocalPart implements Reason
|
||||
{
|
||||
public function code() : int
|
||||
{
|
||||
return 130;
|
||||
}
|
||||
|
||||
public function description() : string
|
||||
{
|
||||
return "No local part";
|
||||
}
|
||||
}
|
||||
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/RFCWarnings.php
vendored
Normal file
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/RFCWarnings.php
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Result\Reason;
|
||||
|
||||
class RFCWarnings implements Reason
|
||||
{
|
||||
public function code() : int
|
||||
{
|
||||
return 997;
|
||||
}
|
||||
|
||||
public function description() : string
|
||||
{
|
||||
return 'Warnings found after validating';
|
||||
}
|
||||
}
|
||||
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/Reason.php
vendored
Normal file
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/Reason.php
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Result\Reason;
|
||||
|
||||
interface Reason
|
||||
{
|
||||
/**
|
||||
* Code for user land to act upon;
|
||||
*/
|
||||
public function code() : int;
|
||||
|
||||
/**
|
||||
* Short description of the result, human readable.
|
||||
*/
|
||||
public function description() : string;
|
||||
}
|
||||
17
plugins/email/vendor/egulias/email-validator/src/Result/Reason/SpoofEmail.php
vendored
Normal file
17
plugins/email/vendor/egulias/email-validator/src/Result/Reason/SpoofEmail.php
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Result\Reason;
|
||||
|
||||
class SpoofEmail implements Reason
|
||||
{
|
||||
public function code() : int
|
||||
{
|
||||
return 298;
|
||||
}
|
||||
|
||||
public function description() : string
|
||||
{
|
||||
return 'The email contains mixed UTF8 chars that makes it suspicious';
|
||||
}
|
||||
|
||||
}
|
||||
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/UnOpenedComment.php
vendored
Normal file
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/UnOpenedComment.php
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Result\Reason;
|
||||
|
||||
class UnOpenedComment implements Reason
|
||||
{
|
||||
public function code() : int
|
||||
{
|
||||
return 152;
|
||||
}
|
||||
|
||||
public function description(): string
|
||||
{
|
||||
return 'Missing opening comment parentheses - https://tools.ietf.org/html/rfc5322#section-3.2.2';
|
||||
}
|
||||
}
|
||||
19
plugins/email/vendor/egulias/email-validator/src/Result/Reason/UnableToGetDNSRecord.php
vendored
Normal file
19
plugins/email/vendor/egulias/email-validator/src/Result/Reason/UnableToGetDNSRecord.php
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Result\Reason;
|
||||
|
||||
/**
|
||||
* Used on SERVFAIL, TIMEOUT or other runtime and network errors
|
||||
*/
|
||||
class UnableToGetDNSRecord extends NoDNSRecord
|
||||
{
|
||||
public function code() : int
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
public function description() : string
|
||||
{
|
||||
return 'Unable to get DNS records for the host';
|
||||
}
|
||||
}
|
||||
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/UnclosedComment.php
vendored
Normal file
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/UnclosedComment.php
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Result\Reason;
|
||||
|
||||
class UnclosedComment implements Reason
|
||||
{
|
||||
public function code() : int
|
||||
{
|
||||
return 146;
|
||||
}
|
||||
|
||||
public function description(): string
|
||||
{
|
||||
return 'No closing comment token found';
|
||||
}
|
||||
}
|
||||
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/UnclosedQuotedString.php
vendored
Normal file
16
plugins/email/vendor/egulias/email-validator/src/Result/Reason/UnclosedQuotedString.php
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Result\Reason;
|
||||
|
||||
class UnclosedQuotedString implements Reason
|
||||
{
|
||||
public function code() : int
|
||||
{
|
||||
return 145;
|
||||
}
|
||||
|
||||
public function description() : string
|
||||
{
|
||||
return "Unclosed quoted string";
|
||||
}
|
||||
}
|
||||
26
plugins/email/vendor/egulias/email-validator/src/Result/Reason/UnusualElements.php
vendored
Normal file
26
plugins/email/vendor/egulias/email-validator/src/Result/Reason/UnusualElements.php
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Result\Reason;
|
||||
|
||||
class UnusualElements implements Reason
|
||||
{
|
||||
/**
|
||||
* @var string $element
|
||||
*/
|
||||
private $element = '';
|
||||
|
||||
public function __construct(string $element)
|
||||
{
|
||||
$this->element = $element;
|
||||
}
|
||||
|
||||
public function code() : int
|
||||
{
|
||||
return 201;
|
||||
}
|
||||
|
||||
public function description() : string
|
||||
{
|
||||
return 'Unusual element found, wourld render invalid in majority of cases. Element found: ' . $this->element;
|
||||
}
|
||||
}
|
||||
27
plugins/email/vendor/egulias/email-validator/src/Result/Result.php
vendored
Normal file
27
plugins/email/vendor/egulias/email-validator/src/Result/Result.php
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Result;
|
||||
|
||||
interface Result
|
||||
{
|
||||
/**
|
||||
* Is validation result valid?
|
||||
*/
|
||||
public function isValid() : bool;
|
||||
|
||||
/**
|
||||
* Is validation result invalid?
|
||||
* Usually the inverse of isValid()
|
||||
*/
|
||||
public function isInvalid() : bool;
|
||||
|
||||
/**
|
||||
* Short description of the result, human readable.
|
||||
*/
|
||||
public function description() : string;
|
||||
|
||||
/**
|
||||
* Code for user land to act upon.
|
||||
*/
|
||||
public function code() : int;
|
||||
}
|
||||
14
plugins/email/vendor/egulias/email-validator/src/Result/SpoofEmail.php
vendored
Normal file
14
plugins/email/vendor/egulias/email-validator/src/Result/SpoofEmail.php
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
namespace Egulias\EmailValidator\Result;
|
||||
|
||||
use Egulias\EmailValidator\Result\InvalidEmail;
|
||||
use Egulias\EmailValidator\Result\Reason\SpoofEmail as ReasonSpoofEmail;
|
||||
|
||||
class SpoofEmail extends InvalidEmail
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->reason = new ReasonSpoofEmail();
|
||||
parent::__construct($this->reason, '');
|
||||
}
|
||||
}
|
||||
27
plugins/email/vendor/egulias/email-validator/src/Result/ValidEmail.php
vendored
Normal file
27
plugins/email/vendor/egulias/email-validator/src/Result/ValidEmail.php
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Result;
|
||||
|
||||
class ValidEmail implements Result
|
||||
{
|
||||
public function isValid(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function isInvalid(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function description(): string
|
||||
{
|
||||
return "Valid email";
|
||||
}
|
||||
|
||||
public function code(): int
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
192
plugins/email/vendor/egulias/email-validator/src/Validation/DNSCheckValidation.php
vendored
Normal file
192
plugins/email/vendor/egulias/email-validator/src/Validation/DNSCheckValidation.php
vendored
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Validation;
|
||||
|
||||
use Egulias\EmailValidator\Validation\DNSGetRecordWrapper;
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Result\InvalidEmail;
|
||||
use Egulias\EmailValidator\Result\Reason\DomainAcceptsNoMail;
|
||||
use Egulias\EmailValidator\Result\Reason\LocalOrReservedDomain;
|
||||
use Egulias\EmailValidator\Result\Reason\NoDNSRecord as ReasonNoDNSRecord;
|
||||
use Egulias\EmailValidator\Result\Reason\UnableToGetDNSRecord;
|
||||
use Egulias\EmailValidator\Warning\NoDNSMXRecord;
|
||||
|
||||
class DNSCheckValidation implements EmailValidation
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected const DNS_RECORD_TYPES_TO_CHECK = DNS_MX + DNS_A + DNS_AAAA;
|
||||
|
||||
/**
|
||||
* Reserved Top Level DNS Names (https://tools.ietf.org/html/rfc2606#section-2),
|
||||
* mDNS and private DNS Namespaces (https://tools.ietf.org/html/rfc6762#appendix-G)
|
||||
*/
|
||||
const RESERVED_DNS_TOP_LEVEL_NAMES = [
|
||||
// Reserved Top Level DNS Names
|
||||
'test',
|
||||
'example',
|
||||
'invalid',
|
||||
'localhost',
|
||||
|
||||
// mDNS
|
||||
'local',
|
||||
|
||||
// Private DNS Namespaces
|
||||
'intranet',
|
||||
'internal',
|
||||
'private',
|
||||
'corp',
|
||||
'home',
|
||||
'lan',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $warnings = [];
|
||||
|
||||
/**
|
||||
* @var InvalidEmail|null
|
||||
*/
|
||||
private $error;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $mxRecords = [];
|
||||
|
||||
/**
|
||||
* @var DNSGetRecordWrapper
|
||||
*/
|
||||
private $dnsGetRecord;
|
||||
|
||||
public function __construct(DNSGetRecordWrapper $dnsGetRecord = null)
|
||||
{
|
||||
if (!function_exists('idn_to_ascii')) {
|
||||
throw new \LogicException(sprintf('The %s class requires the Intl extension.', __CLASS__));
|
||||
}
|
||||
|
||||
if ($dnsGetRecord == null) {
|
||||
$dnsGetRecord = new DNSGetRecordWrapper();
|
||||
}
|
||||
|
||||
$this->dnsGetRecord = $dnsGetRecord;
|
||||
}
|
||||
|
||||
public function isValid(string $email, EmailLexer $emailLexer) : bool
|
||||
{
|
||||
// use the input to check DNS if we cannot extract something similar to a domain
|
||||
$host = $email;
|
||||
|
||||
// Arguable pattern to extract the domain. Not aiming to validate the domain nor the email
|
||||
if (false !== $lastAtPos = strrpos($email, '@')) {
|
||||
$host = substr($email, $lastAtPos + 1);
|
||||
}
|
||||
|
||||
// Get the domain parts
|
||||
$hostParts = explode('.', $host);
|
||||
|
||||
$isLocalDomain = count($hostParts) <= 1;
|
||||
$isReservedTopLevel = in_array($hostParts[(count($hostParts) - 1)], self::RESERVED_DNS_TOP_LEVEL_NAMES, true);
|
||||
|
||||
// Exclude reserved top level DNS names
|
||||
if ($isLocalDomain || $isReservedTopLevel) {
|
||||
$this->error = new InvalidEmail(new LocalOrReservedDomain(), $host);
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->checkDns($host);
|
||||
}
|
||||
|
||||
public function getError() : ?InvalidEmail
|
||||
{
|
||||
return $this->error;
|
||||
}
|
||||
|
||||
public function getWarnings() : array
|
||||
{
|
||||
return $this->warnings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $host
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function checkDns($host)
|
||||
{
|
||||
$variant = INTL_IDNA_VARIANT_UTS46;
|
||||
|
||||
$host = rtrim(idn_to_ascii($host, IDNA_DEFAULT, $variant), '.') . '.';
|
||||
|
||||
return $this->validateDnsRecords($host);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validate the DNS records for given host.
|
||||
*
|
||||
* @param string $host A set of DNS records in the format returned by dns_get_record.
|
||||
*
|
||||
* @return bool True on success.
|
||||
*/
|
||||
private function validateDnsRecords($host) : bool
|
||||
{
|
||||
$dnsRecordsResult = $this->dnsGetRecord->getRecords($host, static::DNS_RECORD_TYPES_TO_CHECK);
|
||||
|
||||
if ($dnsRecordsResult->withError()) {
|
||||
$this->error = new InvalidEmail(new UnableToGetDNSRecord(), '');
|
||||
return false;
|
||||
}
|
||||
|
||||
$dnsRecords = $dnsRecordsResult->getRecords();
|
||||
|
||||
// No MX, A or AAAA DNS records
|
||||
if ($dnsRecords === []) {
|
||||
$this->error = new InvalidEmail(new ReasonNoDNSRecord(), '');
|
||||
return false;
|
||||
}
|
||||
|
||||
// For each DNS record
|
||||
foreach ($dnsRecords as $dnsRecord) {
|
||||
if (!$this->validateMXRecord($dnsRecord)) {
|
||||
// No MX records (fallback to A or AAAA records)
|
||||
if (empty($this->mxRecords)) {
|
||||
$this->warnings[NoDNSMXRecord::CODE] = new NoDNSMXRecord();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate an MX record
|
||||
*
|
||||
* @param array $dnsRecord Given DNS record.
|
||||
*
|
||||
* @return bool True if valid.
|
||||
*/
|
||||
private function validateMxRecord($dnsRecord) : bool
|
||||
{
|
||||
if (!isset($dnsRecord['type'])) {
|
||||
$this->error = new InvalidEmail(new ReasonNoDNSRecord(), '');
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($dnsRecord['type'] !== 'MX') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// "Null MX" record indicates the domain accepts no mail (https://tools.ietf.org/html/rfc7505)
|
||||
if (empty($dnsRecord['target']) || $dnsRecord['target'] === '.') {
|
||||
$this->error = new InvalidEmail(new DomainAcceptsNoMail(), "");
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->mxRecords[] = $dnsRecord;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
28
plugins/email/vendor/egulias/email-validator/src/Validation/DNSGetRecordWrapper.php
vendored
Normal file
28
plugins/email/vendor/egulias/email-validator/src/Validation/DNSGetRecordWrapper.php
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
namespace Egulias\EmailValidator\Validation;
|
||||
|
||||
class DNSGetRecordWrapper
|
||||
{
|
||||
/**
|
||||
* @param string $host
|
||||
* @param int $type
|
||||
*/
|
||||
public function getRecords(string $host, int $type) : DNSRecords
|
||||
{
|
||||
// A workaround to fix https://bugs.php.net/bug.php?id=73149
|
||||
/** @psalm-suppress InvalidArgument */
|
||||
set_error_handler(
|
||||
static function (int $errorLevel, string $errorMessage): ?bool {
|
||||
throw new \RuntimeException("Unable to get DNS record for the host: $errorMessage");
|
||||
}
|
||||
);
|
||||
try {
|
||||
// Get all MX, A and AAAA DNS records for host
|
||||
return new DNSRecords(dns_get_record($host, $type));
|
||||
} catch (\RuntimeException $exception) {
|
||||
return new DNSRecords([], true);
|
||||
} finally {
|
||||
restore_error_handler();
|
||||
}
|
||||
}
|
||||
}
|
||||
35
plugins/email/vendor/egulias/email-validator/src/Validation/DNSRecords.php
vendored
Normal file
35
plugins/email/vendor/egulias/email-validator/src/Validation/DNSRecords.php
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Validation;
|
||||
|
||||
class DNSRecords
|
||||
{
|
||||
|
||||
/**
|
||||
* @var array $records
|
||||
*/
|
||||
private $records = [];
|
||||
|
||||
/**
|
||||
* @var bool $error
|
||||
*/
|
||||
private $error = false;
|
||||
|
||||
public function __construct(array $records, bool $error = false)
|
||||
{
|
||||
$this->records = $records;
|
||||
$this->error = $error;
|
||||
}
|
||||
|
||||
public function getRecords() : array
|
||||
{
|
||||
return $this->records;
|
||||
}
|
||||
|
||||
public function withError() : bool
|
||||
{
|
||||
return $this->error;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
34
plugins/email/vendor/egulias/email-validator/src/Validation/EmailValidation.php
vendored
Normal file
34
plugins/email/vendor/egulias/email-validator/src/Validation/EmailValidation.php
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Validation;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Result\InvalidEmail;
|
||||
use Egulias\EmailValidator\Warning\Warning;
|
||||
|
||||
interface EmailValidation
|
||||
{
|
||||
/**
|
||||
* Returns true if the given email is valid.
|
||||
*
|
||||
* @param string $email The email you want to validate.
|
||||
* @param EmailLexer $emailLexer The email lexer.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid(string $email, EmailLexer $emailLexer) : bool;
|
||||
|
||||
/**
|
||||
* Returns the validation error.
|
||||
*
|
||||
* @return InvalidEmail|null
|
||||
*/
|
||||
public function getError() : ?InvalidEmail;
|
||||
|
||||
/**
|
||||
* Returns the validation warnings.
|
||||
*
|
||||
* @return Warning[]
|
||||
*/
|
||||
public function getWarnings() : array;
|
||||
}
|
||||
16
plugins/email/vendor/egulias/email-validator/src/Validation/Exception/EmptyValidationList.php
vendored
Normal file
16
plugins/email/vendor/egulias/email-validator/src/Validation/Exception/EmptyValidationList.php
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Validation\Exception;
|
||||
|
||||
use Exception;
|
||||
|
||||
class EmptyValidationList extends \InvalidArgumentException
|
||||
{
|
||||
/**
|
||||
* @param int $code
|
||||
*/
|
||||
public function __construct($code = 0, Exception $previous = null)
|
||||
{
|
||||
parent::__construct("Empty validation list is not allowed", $code, $previous);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue