PluginProbe ʕ •ᴥ•ʔ
WP Mail SMTP by WPForms – The Most Popular SMTP and Email Log Plugin / 4.9.0
WP Mail SMTP by WPForms – The Most Popular SMTP and Email Log Plugin v4.9.0
4.9.0 0.9.6 1.0.0 1.0.1 1.0.2 1.1.0 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.3.0 1.3.1 1.3.2 1.3.3 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.6.0 1.6.2 1.7.0 1.7.1 1.8.0 1.8.1 1.9.0 2.0.0 2.0.1 2.1.1 2.2.1 2.3.1 2.4.0 2.5.0 2.5.1 2.6.0 2.7.0 2.8.0 2.9.0 3.0.1 3.0.2 3.0.3 3.1.0 3.10.0 3.11.0 3.11.1 3.2.0 3.2.1 3.3.0 3.4.0 3.5.0 3.5.1 3.5.2 3.6.1 3.7.0 3.8.0 3.8.2 3.9.0 4.0.1 4.1.0 4.1.1 4.2.0 4.3.0 4.4.0 4.5.0 4.6.0 4.7.0 4.7.1 4.8.0 trunk 0.10.0 0.10.1 0.11.1 0.11.2 0.3.1 0.3.2 0.4 0.4.1 0.4.2 0.5.0 0.5.1 0.5.2 0.6 0.7 0.8 0.8.2 0.8.3 0.8.4 0.8.5 0.8.6 0.8.7 0.9.0 0.9.1 0.9.2 0.9.3 0.9.4 0.9.5
wp-mail-smtp / src / Processor.php
wp-mail-smtp / src Last commit date
Abilities 5 days ago Admin 5 days ago Compatibility 5 days ago Helpers 5 days ago Integrations 5 days ago Providers 5 days ago Queue 5 days ago Reports 5 days ago Tasks 5 days ago TestEmail 5 days ago UsageTracking 5 days ago WPCLI 5 days ago AbstractConnection.php 5 days ago Conflicts.php 5 days ago Connect.php 5 days ago Connection.php 5 days ago ConnectionInterface.php 5 days ago ConnectionsManager.php 5 days ago Core.php 5 days ago DBRepair.php 5 days ago Debug.php 5 days ago EmailSendingDebug.php 5 days ago Geo.php 5 days ago MailCatcher.php 5 days ago MailCatcherInterface.php 5 days ago MailCatcherTrait.php 5 days ago MailCatcherV6.php 5 days ago Migration.php 5 days ago MigrationAbstract.php 5 days ago Migrations.php 5 days ago OptimizedEmailSending.php 5 days ago Options.php 5 days ago Processor.php 5 days ago SiteHealth.php 5 days ago Upgrade.php 5 days ago Uploads.php 5 days ago WP.php 5 days ago WPMailArgs.php 5 days ago WPMailInitiator.php 5 days ago
Processor.php
529 lines
1 <?php
2
3 namespace WPMailSMTP;
4
5 /**
6 * Class Processor modifies the behaviour of wp_mail() function.
7 *
8 * @since 1.0.0
9 */
10 class Processor {
11
12 /**
13 * This attribute will hold the "original" WP from email address passed to the wp_mail_from filter,
14 * that is not equal to the default email address.
15 *
16 * It should hold an email address set via the wp_mail_from filter, before we might overwrite it.
17 *
18 * @since 2.1.0
19 *
20 * @var string
21 */
22 protected $wp_mail_from;
23
24 /**
25 * Connections manager.
26 *
27 * @since 3.7.0
28 *
29 * @var ConnectionsManager
30 */
31 private $connections_manager;
32
33 /**
34 * This attribute will hold the arguments passed to the `wp_mail` function.
35 *
36 * @since 4.0.0
37 *
38 * @var array
39 */
40 private $original_wp_mail_args;
41
42 /**
43 * This attribute will hold the arguments passed to the `wp_mail` function and filtered via `wp_mail` filter.
44 *
45 * @since 4.0.0
46 *
47 * @var array
48 */
49 private $filtered_wp_mail_args;
50
51 /**
52 * This attribute will hold the From address filtered via the `wp_mail_from` filter.
53 *
54 * @since 4.0.0
55 *
56 * @var string
57 */
58 private $filtered_from_email;
59
60 /**
61 * This attribute will hold the From name filtered via the `wp_mail_from_name` filter.
62 *
63 * @since 4.0.0
64 *
65 * @var string
66 */
67 private $filtered_from_name;
68
69 /**
70 * Class constructor.
71 *
72 * @since 3.7.0
73 *
74 * @param ConnectionsManager $connections_manager Connections manager.
75 */
76 public function __construct( $connections_manager = null ) {
77
78 if ( is_null( $connections_manager ) ) {
79 $this->connections_manager = wp_mail_smtp()->get_connections_manager();
80 } else {
81 $this->connections_manager = $connections_manager;
82 }
83 }
84
85 /**
86 * Assign all hooks to proper places.
87 *
88 * @since 1.0.0
89 */
90 public function hooks() {
91
92 add_action( 'phpmailer_init', [ $this, 'phpmailer_init' ] );
93
94 // High priority number tries to ensure our plugin code executes last and respects previous hooks, if not forced.
95 add_filter( 'wp_mail_from', [ $this, 'filter_mail_from_email' ], PHP_INT_MAX );
96 add_filter( 'wp_mail_from_name', [ $this, 'filter_mail_from_name' ], PHP_INT_MAX );
97
98 add_action( 'wp_mail', [ $this, 'capture_early_wp_mail_filter_call' ], - PHP_INT_MAX );
99 add_action( 'wp_mail', [ $this, 'capture_late_wp_mail_filter_call' ], PHP_INT_MAX );
100 }
101
102 /**
103 * Redefine certain PHPMailer options with our custom ones.
104 *
105 * @since 1.0.0
106 *
107 * @param \PHPMailer $phpmailer It's passed by reference, so no need to return anything.
108 */
109 public function phpmailer_init( $phpmailer ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.MaxExceeded
110
111 $connection = $this->connections_manager->get_mail_connection();
112 $connection_options = $connection->get_options();
113 $mailer = $connection->get_mailer_slug();
114
115 // Check that mailer is not blank, and if mailer=smtp, host is not blank.
116 if (
117 ! $mailer ||
118 ( 'smtp' === $mailer && ! $connection_options->get( 'smtp', 'host' ) )
119 ) {
120 return;
121 }
122
123 // If the mailer is pepipost, make sure we have a username and password.
124 if (
125 'pepipost' === $mailer &&
126 ( ! $connection_options->get( 'pepipost', 'user' ) && ! $connection_options->get( 'pepipost', 'pass' ) )
127 ) {
128 return;
129 }
130
131 // phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
132
133 // Set the mailer type as per config above, this overrides the already called isMail method.
134 // It's basically always 'smtp'.
135 $phpmailer->Mailer = $mailer;
136
137 // Set the Sender (return-path) if required.
138 if ( $connection_options->get( 'mail', 'return_path' ) ) {
139 $phpmailer->Sender = $phpmailer->From;
140 }
141
142 // Set the SMTPSecure value, if set to none, leave this blank. Possible values: 'ssl', 'tls', ''.
143 if ( 'none' === $connection_options->get( $mailer, 'encryption' ) ) {
144 $phpmailer->SMTPSecure = '';
145 } else {
146 $phpmailer->SMTPSecure = $connection_options->get( $mailer, 'encryption' );
147 }
148
149 // Check if user has disabled SMTPAutoTLS.
150 if ( $connection_options->get( $mailer, 'encryption' ) !== 'tls' && ! $connection_options->get( $mailer, 'autotls' ) ) {
151 $phpmailer->SMTPAutoTLS = false;
152 }
153
154 // Check if original WP from email can be set as the reply_to attribute.
155 if ( $this->allow_setting_original_from_email_to_reply_to( $phpmailer->getReplyToAddresses(), $mailer ) ) {
156 $phpmailer->addReplyTo( $this->wp_mail_from );
157 }
158
159 // If we're sending via SMTP, set the host.
160 if ( 'smtp' === $mailer ) {
161 // Set the other options.
162 $phpmailer->Host = $connection_options->get( $mailer, 'host' );
163 $phpmailer->Port = $connection_options->get( $mailer, 'port' );
164
165 // If we're using smtp auth, set the username & password.
166 if ( $connection_options->get( $mailer, 'auth' ) ) {
167 $phpmailer->SMTPAuth = true;
168 $phpmailer->Username = $connection_options->get( $mailer, 'user' );
169 $phpmailer->Password = $connection_options->get( $mailer, 'pass' );
170 }
171 } elseif ( 'pepipost' === $mailer ) {
172 // Set the Pepipost settings for BC.
173 $phpmailer->Mailer = 'smtp';
174 $phpmailer->Host = 'smtp.pepipost.com';
175 $phpmailer->Port = $connection_options->get( $mailer, 'port' );
176 $phpmailer->SMTPSecure = $connection_options->get( $mailer, 'encryption' ) === 'none' ? '' : $connection_options->get( $mailer, 'encryption' );
177 $phpmailer->SMTPAuth = true;
178 $phpmailer->Username = $connection_options->get( $mailer, 'user' );
179 $phpmailer->Password = $connection_options->get( $mailer, 'pass' );
180 }
181
182 $phpmailer->Timeout = 30;
183 // phpcs:enable
184
185 // Maybe set default reply-to header.
186 $this->set_default_reply_to( $phpmailer );
187
188 // You can add your own options here.
189 // See the phpmailer documentation for more info: https://github.com/PHPMailer/PHPMailer/tree/5.2-stable.
190 /* @noinspection PhpUnusedLocalVariableInspection It's passed by reference. */
191 $phpmailer = apply_filters( 'wp_mail_smtp_custom_options', $phpmailer );
192 }
193
194 /**
195 * Check if it's allowed to set the original WP from email to the reply_to field.
196 *
197 * @since 2.1.0
198 *
199 * @param array $reply_to Array of currently set reply to emails.
200 * @param string $mailer The slug of current mailer.
201 *
202 * @return bool
203 */
204 protected function allow_setting_original_from_email_to_reply_to( $reply_to, $mailer ) {
205
206 $connection = $this->connections_manager->get_mail_connection();
207 $connection_options = $connection->get_options();
208 $forced = $connection_options->get( 'mail', 'from_email_force' );
209 $from_email = $connection_options->get( 'mail', 'from_email' );
210
211 if ( ! empty( $reply_to ) || empty( $this->wp_mail_from ) ) {
212 return false;
213 }
214
215 if ( in_array( $mailer, [ 'zoho' ], true ) ) {
216 $sender = $connection_options->get( $mailer, 'user_details' );
217 $from_email = ! empty( $sender['email'] ) ? $sender['email'] : '';
218 $forced = true;
219 }
220
221 if (
222 $from_email === $this->wp_mail_from ||
223 ! $forced
224 ) {
225 return false;
226 }
227
228 return true;
229 }
230
231 /**
232 * Deprecated stub kept for backwards compatibility.
233 *
234 * Previously registered as PHPMailer's $action_function and dispatched per recipient
235 * via PHPMailer's doCallback(). The fan-out caused duplicate `wp_mail_smtp_mailcatcher_smtp_send_after`
236 * invocations and racing EmailSendingDebug writes on partial-recipient failures.
237 *
238 * Logic moved to MailCatcherTrait: doCallback() collects failed recipients, and
239 * smtp_send() fires the after-send action exactly once per email in its success
240 * and failure paths.
241 *
242 * No replacement. Kept for third-party code that may still reference the
243 * callable directly. Behavior is now a no-op.
244 *
245 * @since 1.3.0
246 * @since 1.5.0 Added a do_action() to be able to hook into.
247 * @deprecated {VERSION}
248 *
249 * @param bool $is_sent If the email was sent.
250 * @param array $to To email address.
251 * @param array $cc CC email addresses.
252 * @param array $bcc BCC email addresses.
253 * @param string $subject The email subject.
254 * @param string $body The email body.
255 * @param string $from The from email address.
256 */
257 public static function send_callback( $is_sent, $to, $cc, $bcc, $subject, $body, $from ) { }
258
259 /**
260 * Validate the email address.
261 *
262 * @since 3.6.0
263 *
264 * @param string $email The email address.
265 *
266 * @return boolean True if email address is valid, false on failure.
267 */
268 public static function is_email_callback( $email ) {
269
270 return (bool) is_email( $email );
271 }
272
273 /**
274 * Modify the email address that is used for sending emails.
275 *
276 * @since 1.0.0
277 * @since 1.3.0 Forcing email rewrite if option is selected.
278 * @since 1.7.0 Default email may be empty, so pay attention to that as well.
279 *
280 * @param string $wp_email The email address passed by the filter.
281 *
282 * @return string
283 */
284 public function filter_mail_from_email( $wp_email ) {
285
286 // Save the original from address.
287 $this->filtered_from_email = filter_var( $wp_email, FILTER_VALIDATE_EMAIL );
288
289 $connection = $this->connections_manager->get_mail_connection();
290 $connection_options = $connection->get_options();
291 $forced = $connection_options->get( 'mail', 'from_email_force' );
292 $from_email = $connection_options->get( 'mail', 'from_email' );
293 $def_email = WP::get_default_email();
294
295 // Save the "original" set WP email from address for later use.
296 if ( $wp_email !== $def_email ) {
297 $this->wp_mail_from = filter_var( $wp_email, FILTER_VALIDATE_EMAIL );
298 }
299
300 // Return FROM EMAIL if forced in settings.
301 if ( $forced && ! empty( $from_email ) ) {
302 return $from_email;
303 }
304
305 // If the FROM EMAIL is not the default, return it unchanged.
306 if ( ! empty( $def_email ) && $wp_email !== $def_email ) {
307 return $wp_email;
308 }
309
310 return ! empty( $from_email ) ? $from_email : $wp_email;
311 }
312
313 /**
314 * Modify the sender name that is used for sending emails.
315 *
316 * @since 1.0.0
317 * @since 1.3.0 Forcing name rewrite if option is selected.
318 *
319 * @param string $name The from name passed through the filter.
320 *
321 * @return string
322 */
323 public function filter_mail_from_name( $name ) {
324
325 // Save the original from name.
326 $this->filtered_from_name = $name;
327
328 $connection = $this->connections_manager->get_mail_connection();
329 $connection_options = $connection->get_options();
330 $force = $connection_options->get( 'mail', 'from_name_force' );
331
332 // If the FROM NAME is not the default and not forced, return it unchanged.
333 if ( ! $force && $name !== $this->get_default_name() ) {
334 return $name;
335 }
336
337 $name = $connection_options->get( 'mail', 'from_name' );
338
339 return $name;
340 }
341
342 /**
343 * Get the default email address based on domain name.
344 *
345 * @since 1.0.0
346 * @since 1.7.0 May return an empty string.
347 *
348 * @return string Empty string when we aren't able to get the site domain (CLI, misconfigured server etc).
349 */
350 public function get_default_email() {
351
352 $server_name = Geo::get_site_domain();
353
354 if ( empty( $server_name ) ) {
355 return '';
356 }
357
358 // Get rid of www.
359 $sitename = strtolower( $server_name );
360 if ( substr( $sitename, 0, 4 ) === 'www.' ) {
361 $sitename = substr( $sitename, 4 );
362 }
363
364 return 'wordpress@' . $sitename;
365 }
366
367 /**
368 * Get the default email FROM NAME generated by WordPress.
369 *
370 * @since 1.3.0
371 *
372 * @return string
373 */
374 public function get_default_name() {
375 return 'WordPress';
376 }
377
378 /**
379 * Get or create the phpmailer.
380 *
381 * @since 1.9.0
382 *
383 * @return MailCatcherInterface
384 */
385 public function get_phpmailer() {
386
387 global $phpmailer;
388
389 // Make sure the PHPMailer class has been instantiated.
390 if ( ! is_object( $phpmailer ) || ! is_a( $phpmailer, 'PHPMailer' ) ) {
391 $phpmailer = wp_mail_smtp()->generate_mail_catcher( true ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
392 }
393
394 return $phpmailer;
395 }
396
397 /**
398 * Set the default reply_to header, if:
399 * - no other reply_to headers are already set and,
400 * - the default reply_to address filter `wp_mail_smtp_processor_default_reply_to_addresses` is configured.
401 *
402 * @since 2.1.1
403 *
404 * @param MailCatcherInterface $phpmailer The PHPMailer object.
405 */
406 private function set_default_reply_to( $phpmailer ) {
407
408 if ( ! empty( $phpmailer->getReplyToAddresses() ) ) {
409 return;
410 }
411
412 $default_reply_to_emails = apply_filters( 'wp_mail_smtp_processor_set_default_reply_to', '' );
413
414 if ( empty( $default_reply_to_emails ) ) {
415 return;
416 }
417
418 foreach ( explode( ',', $default_reply_to_emails ) as $email ) {
419 $email = trim( $email );
420
421 if ( filter_var( $email, FILTER_VALIDATE_EMAIL ) ) {
422 $phpmailer->addReplyTo( $email );
423 }
424 }
425 }
426
427 /**
428 * Capture `wp_mail` filter call on earliest priority.
429 *
430 * Currently used to capture the original `wp_mail` arguments before they are filtered.
431 *
432 * @since 4.0.0
433 *
434 * @param array $args The original `wp_mail` arguments.
435 *
436 * @return array
437 */
438 public function capture_early_wp_mail_filter_call( $args ) {
439
440 $this->original_wp_mail_args = $args;
441
442 return $args;
443 }
444
445 /**
446 * Capture `wp_mail` filter call on latest priority.
447 *
448 * Currently used to capture the `wp_mail` arguments after they are filtered
449 * and capture `wp_mail` function call.
450 *
451 * @since 4.0.0
452 *
453 * @param array $args The filtered `wp_mail` arguments.
454 *
455 * @return array
456 */
457 public function capture_late_wp_mail_filter_call( $args ) {
458
459 $this->filtered_wp_mail_args = $args;
460
461 $this->capture_wp_mail_call();
462
463 return $args;
464 }
465
466 /**
467 * Capture `wp_mail` function call.
468 *
469 * @since 4.0.0
470 */
471 private function capture_wp_mail_call() {
472
473 /**
474 * Fires on `wp_mail` function call.
475 *
476 * @since 4.0.0
477 */
478 do_action( 'wp_mail_smtp_processor_capture_wp_mail_call' );
479 }
480
481 /**
482 * Get the original `wp_mail` arguments.
483 *
484 * @since 4.0.0
485 *
486 * @return array
487 */
488 public function get_original_wp_mail_args() {
489
490 return $this->original_wp_mail_args;
491 }
492
493 /**
494 * Get the filtered `wp_mail` arguments.
495 *
496 * @since 4.0.0
497 *
498 * @return array
499 */
500 public function get_filtered_wp_mail_args() {
501
502 return $this->filtered_wp_mail_args;
503 }
504
505 /**
506 * Get the filtered `wp_mail_from` value.
507 *
508 * @since 4.0.0
509 *
510 * @return string
511 */
512 public function get_filtered_from_email() {
513
514 return $this->filtered_from_email;
515 }
516
517 /**
518 * Get the filtered `wp_mail_from_name` value.
519 *
520 * @since 4.0.0
521 *
522 * @return string
523 */
524 public function get_filtered_from_name() {
525
526 return $this->filtered_from_name;
527 }
528 }
529