=== Dualsend SMTP Failover ===
Contributors: cshubh832
Tags: smtp, email, mail, wp-mail, deliverability
Requires at least: 6.0
Tested up to: 7.1
Requires PHP: 7.4
Stable tag: 1.1.0
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Send WordPress email through an SMTP server or provider API, with an automatic fallback server, connection tests and a debug log.

== Description ==

WordPress sends email with PHP's `mail()` function by default. Most hosts either block it or send it in a way that lands in spam, which is why contact form notifications, password resets and order confirmations go missing.

Dualsend SMTP Failover routes your site's email through a mail server you control, over SMTP or over the provider's HTTP API, and switches to a second server you choose when the first one fails.

= How delivery works =

Both servers are set on the **Settings** tab. Each message is tried in this order:

1. The **Primary SMTP Server**: over its provider API when it is set to API mode, then over SMTP.
2. The **Fallback SMTP Server**, while its switch is on: over its provider API when it is set to API mode, then over SMTP.
3. Background retries, when both servers fail: the email waits in a queue and is tried again later, primary server first and fallback server second, every **Retry Interval** minutes, up to **Maximum Retries** times. Both are set in the **Delivery and Diagnostics** card.
4. WordPress itself, whose own mail handling gets the email as a last resort after the last retry fails, or at once when Maximum Retries is 0.

Each server gets one attempt per pass. When it does not accept the message, the next one is tried straight away, with no waiting in the page, so a failing server never holds up your site. Retries happen only in the background, never while a visitor waits. Leave the fallback server empty, or switch it off, to send through the primary server alone. Fallback and retry settings saved by version 1.0.0 are used as they are.

= Sending over an API instead of SMTP =

Many hosts close outgoing SMTP ports. Where a provider offers an HTTP API, you can use it instead of SMTP by pasting in an API key, with no ports involved. These providers are supported:

Brevo, Elastic Email, MailerSend, Mailgun, Mailjet, Mandrill, Postmark, Resend, SendGrid, SendLayer, SMTP.com, SMTP2GO, SparkPost.

Any other provider, including Amazon SES, Gmail, Google Workspace, Microsoft 365, Outlook.com and Zoho Mail, works over standard SMTP.

= Features =

* A primary and a fallback mail server, each over SMTP or a provider API
* Automatic failover: when the primary server fails, the fallback server takes over at once
* Background retries when both servers fail, with an adjustable interval and number of retries
* Connection and credential tests built into the settings screen
* A test message that follows the same route as your real email
* A Feedback tab to send the Dualsend team a rating and a message, only when you submit it
* Optional debug log, with passwords and API keys masked before anything is written
* Short timeouts while a visitor is waiting on a form submission, so a slow mail server never holds up the page
* Works with Contact Form 7 and any other plugin that sends through `wp_mail()`

== External services ==

This plugin does not contact any service on its own. It has no telemetry, no license check, and no connection to any service run by the plugin author.

The only outgoing connections it makes are to the email provider **you** configure, and only when it is sending your mail or when you press one of the test buttons. Endpoints below are API hostnames, not web pages.

* Brevo: sends to api.brevo.com. [terms](https://www.brevo.com/legal/termsofuse/), [privacy](https://www.brevo.com/legal/privacypolicy/)
* Elastic Email: sends to api.elasticemail.com. [terms](https://elasticemail.com/resources/usage-policies/terms-of-use/), [privacy](https://elasticemail.com/resources/usage-policies/privacy-policy/)
* MailerSend: sends to api.mailersend.com. [terms](https://www.mailersend.com/legal/terms-of-use), [privacy](https://www.mailersend.com/legal/privacy-policy)
* Mailgun: sends to api.mailgun.net or api.eu.mailgun.net. [terms](https://www.mailgun.com/legal/terms/), [privacy](https://www.mailgun.com/legal/privacy-policy/)
* Mailjet: sends to api.mailjet.com. [terms](https://www.mailjet.com/legal/terms/), [privacy](https://www.mailjet.com/legal/privacy-policy/)
* Mandrill: sends to mandrillapp.com. [terms](https://mailchimp.com/legal/terms/), [privacy](https://mailchimp.com/legal/privacy/)
* Postmark: sends to api.postmarkapp.com. [terms](https://postmarkapp.com/terms-of-service), [privacy](https://postmarkapp.com/privacy-policy)
* Resend: sends to api.resend.com. [terms](https://resend.com/legal/terms-of-service), [privacy](https://resend.com/legal/privacy-policy)
* SendGrid: sends to api.sendgrid.com. [terms](https://www.twilio.com/en-us/legal/tos), [privacy](https://www.twilio.com/en-us/legal/privacy)
* SendLayer: sends to console.sendlayer.com. [terms](https://sendlayer.com/terms-of-service/), [privacy](https://sendlayer.com/privacy-policy/)
* SMTP.com: sends to api.smtp.com. [terms](https://www.smtp.com/policies/terms-and-conditions/), [privacy](https://www.smtp.com/policies/privacy-policy/)
* SMTP2GO: sends to api.smtp2go.com. [terms](https://www.smtp2go.com/terms/), [privacy](https://www.smtp2go.com/privacy/)
* SparkPost: sends to api.sparkpost.com or api.eu.sparkpost.com. [terms](https://www.sparkpost.com/policies/tou/), [privacy](https://www.sparkpost.com/policies/privacy/)

When a message is sent, the data passed to your chosen provider is the message itself: recipients, subject, body, and the sender address you configured. That is the same data any mail server would receive. Nothing is sent anywhere else.

If you configure a plain SMTP server instead of an API, the plugin connects only to the host you entered.

**Feedback.** The Feedback tab sends nothing until an administrator fills in the form, ticks the consent box and presses Send feedback. The message is then sent with `wp_mail()`, through the mail server you configured, to the Dualsend team at cshubham832@gmail.com and iamshubhamchaurasia@gmail.com. It contains the rating, the message, the name and email you entered (both optional), your site address and the version of Dualsend SMTP Failover, plus the version of its add-on when one is installed. Nothing else is included. At most 3 messages an hour can be sent. The link to leave a review opens WordPress.org only when you click it.

== Installation ==

1. Upload the plugin through **Plugins → Add New → Upload Plugin**, or install it from the plugin directory.
2. Activate it.
3. Go to **Settings → Dualsend SMTP Failover**.
4. On the **Settings** tab, set the From address. It must be on a domain your provider is allowed to send for.
5. On the same tab, under **Primary SMTP Server**, choose **SMTP** and enter the server details, or choose **API**, pick your provider and paste its API key. Use **Test Connection** or **Test API Connection** to check it.
6. Optionally, under **Fallback SMTP Server**, enter a second server the same way. It is used only when the primary server fails.
7. On the **Test** tab, send yourself a test message.

== Frequently Asked Questions ==

= How does failover work? =

When the primary server does not accept a message, the plugin sends it through the fallback server straight away, with no waiting in the page. If the fallback server fails too, the message is queued for a background retry (see below). Fallback settings saved by version 1.0.0 are used as they are.

= How do background retries work? =

When both servers fail, the email is stored in a queue and the page that sent it carries on at once. A WP-Cron task tries the email again every **Retry Interval** minutes (1 to 60, 5 by default), through the primary server and then the fallback server, up to **Maximum Retries** times (0 to 10, 3 by default). After the last retry fails, the email is handed to WordPress mail as a last resort. Set Maximum Retries to 0 to turn retries off; the email then goes to WordPress mail at once, as it did before. The Delivery and Diagnostics card shows how many emails are waiting and when the next try is. Retry values saved by version 1.0.0 are used as they are.

At most 100 emails wait at once. When the queue is full, or the attachments of an email are larger than 10 MB in total, that email goes to WordPress mail at once instead.

WP-Cron runs when your site gets visits. If your site sets `DISABLE_WP_CRON`, make sure a real server cron job runs `wp-cron.php`, or queued email waits until it does.

= What does the retry queue store, and for how long? =

While an email waits for a retry, the whole email is stored in your WordPress database: the recipients, subject, headers and body. A body can hold private content, such as a password reset link. Attached files are copied into a private folder inside your uploads folder, under random names, with an empty index.php and a rule that blocks web access on Apache servers. On servers that do not read .htaccess files, such as nginx, the random names keep the files from being found; you can also add a deny rule for the `dualsend-smtp-failover-queue` folder.

The email and its copied files are deleted as soon as the email is delivered or handed to WordPress mail after the last retry. Copies left behind by a request that stopped before it could queue its email are deleted by the next background run, one hour after they were made. Nothing in the queue is sent anywhere except to your own mail servers and, as a last resort, WordPress mail. Nothing is ever sent to the plugin author. Deleting the plugin removes the queue and the folder. Set Maximum Retries to 0 to keep nothing.

= My host blocks SMTP ports. What do I do? =

Choose a provider that offers an API, such as SendGrid, Brevo, Postmark, Mailgun and most others in the list above, and set the connection type to **API**. API delivery runs over ordinary HTTPS, which is not blocked.

= Where are my passwords stored? =

In the WordPress options table, in the same database as the rest of your site, and only readable by users who can already manage plugin settings. They are never sent back to the browser once saved, and they are masked before anything is written to the debug log.

For extra safety on a shared host, keep your provider's API key scoped to sending only, rather than full account access.

= Why did my message send but land in spam? =

Almost always because the From address does not match a domain your provider is authorized to send for. Set up SPF, DKIM and DMARC records with your provider, and use a From address on that domain.

= Does it work with Contact Form 7? =

Yes, and it treats form submissions specially: while a visitor is waiting on a form, timeouts are shortened, so a slow mail server never leaves the page hanging.

Any plugin that sends through `wp_mail()` works, including WooCommerce, Gravity Forms, WPForms and the WordPress core notifications.

= Will it interfere with other plugins that set their own sender address? =

Not unless you ask it to. By default a plugin that sets its own From address keeps it. Switch on **Override other plugins** on the Settings tab if you want your configured address used everywhere. Until a delivery route is set up, the plugin does not change the sender of any email.

= Can I keep another SMTP plugin active, such as WP Mail SMTP, FluentSMTP or Post SMTP? =

It works, but only one of them sends your email. While Dualsend SMTP Failover has a delivery route, it takes over `wp_mail()` before the other plugin, so the mail settings of the other plugin are not used. Until it has a route, the other plugin sends as before. The settings page shows a short notice, which you can dismiss, while another SMTP plugin is active. Keeping one SMTP plugin active avoids confusion.

= Does it send inline images? =

Yes. Since WordPress 6.9, `wp_mail()` can embed images that the message refers to with `cid:`. Such a message is sent over SMTP, on the primary server and then the fallback server, because the provider APIs are not used for embedded images. When both servers fail, it goes to WordPress mail at once instead of the retry queue.

= What happens when both servers fail? =

With background retries on, the message is queued and tried again later, as described above. After the last retry, or at once when Maximum Retries is 0, it is handed to WordPress, which tries its own PHP mail handling. Switch on the debug log to see exactly what failed and why. The log never records message bodies.

== Screenshots ==

1. The Settings tab: the sender identity, the primary SMTP server and the fallback SMTP server, each with a passed connection test.
2. The primary server set to API delivery, with a provider chosen and its API key verified.
3. The Delivery and Diagnostics card, with background retries, the debug log switch and the Save Settings bar.
4. The Test tab, after sending a test message.
5. The Logs tab, showing a message that the fallback server delivered after the primary server failed.
6. The Feedback tab, which sends a rating and a message only when you submit it.

== Changelog ==

= 1.1.0 =
* Changed: the primary server, the fallback server and the delivery options are now on one Settings tab, saved by one button. The fallback server has its own on/off switch and uses the settings saved by 1.0.0 as they are.
* Changed: failover is immediate. Each server gets one attempt, then the next route is tried at once: primary, then fallback. Nothing waits in the page.
* Added background retries. When both servers fail, the email is queued and tried again by WP-Cron, primary then fallback, using Retry Interval (minutes) and Maximum Retries in the Delivery and Diagnostics card. After the last retry the email goes to WordPress mail. Values saved by 1.0.0 under the same settings are kept and used. Privacy: a queued email, body included, and copies of its attachments are kept on your site only until it is delivered or given up.
* Added the dualsend_smtp_failover_mail_queued action and the dualsend_smtp_failover_should_queue filter. An alert or notification email sent by an add-on is never queued, so a failed alert cannot start a loop of alerts.
* Changed: while background retries are on, dualsend_smtp_failover_mail_failed fires only after the last retry fails. A message that is queued fires dualsend_smtp_failover_mail_queued instead, and a message that a retry delivers fires dualsend_smtp_failover_mail_sent.
* Changed: a background retry, and a send while a visitor waits on a form, waits at most 10 seconds for each API request as well as for SMTP.
* Fixed: attachments passed to wp_mail() as one string of paths separated by new lines are kept when the email is queued.
* Fixed: the debug log drops every line of the message that is sent after the SMTP DATA command, so no part of a body is recorded.
* Changed: the API section lists only providers that offer API delivery. "Other SMTP" and the SMTP-only providers are set up in the SMTP section. Saving API mode without an API provider keeps the saved connection type and shows an error.
* Added a Feedback tab, which sends a rating and a message to the Dualsend team only when you submit it, and a link to leave a review.
* Added the dualsend_smtp_failover_servers filter. The dualsend_smtp_failover_mail_sent action now also passes the server that delivered the message, "primary" or "fallback".
* Added the dualsend_smtp_failover_admin_tabs filter and the dualsend_smtp_failover_render_tab_{$tab} action, so an add-on can place its own tabs on the settings page.
* Fixed: images embedded with the $embeds argument of wp_mail() (WordPress 6.9 and later) are sent with the message, over SMTP, instead of being dropped.
* Changed: the From address and name are applied only while the plugin has a delivery route. Activating the plugin before a server is set up no longer changes the sender of WordPress mail.
* Added a short notice on the settings page while another SMTP plugin is active, which can be dismissed. The dualsend_smtp_failover_mail_plugins filter changes the list of plugins it looks for.
* Changed: deleting the plugin also removes the rate limits of the Feedback tab and the dismissed state of the new notice, on every site of a network.
* Changed: the plugin now requires WordPress 6.0 or later.

= 1.0.0 =
* First release.

== Upgrade Notice ==

= 1.1.0 =
Failover is now immediate: when the primary server fails, the fallback server takes over at once, with no waiting in the page. When both fail, the email is retried in the background. Your saved fallback and retry settings are kept and used.

= 1.0.0 =
First release.
