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 / Providers / MailerAbstract.php
wp-mail-smtp / src / Providers Last commit date
AmazonSES 5 days ago ElasticEmail 5 days ago Gmail 5 days ago Mail 5 days ago MailerSend 5 days ago Mailgun 5 days ago Mailjet 5 days ago Mandrill 5 days ago Outlook 5 days ago Pepipost 5 days ago PepipostAPI 5 days ago Postmark 5 days ago Resend 5 days ago SMTP 5 days ago SMTP2GO 5 days ago SMTPcom 5 days ago Sendgrid 5 days ago Sendinblue 5 days ago Sendlayer 5 days ago SparkPost 5 days ago Zoho 5 days ago AuthAbstract.php 5 days ago AuthInterface.php 5 days ago Loader.php 5 days ago MailerAbstract.php 5 days ago MailerInterface.php 5 days ago OptionsAbstract.php 5 days ago OptionsInterface.php 5 days ago
MailerAbstract.php
758 lines
1 <?php
2
3 namespace WPMailSMTP\Providers;
4
5 use WPMailSMTP\Admin\DebugEvents\DebugEvents;
6 use WPMailSMTP\ConnectionInterface;
7 use WPMailSMTP\Helpers\Helpers;
8 use WPMailSMTP\MailCatcherInterface;
9 use WPMailSMTP\Options;
10 use WPMailSMTP\WP;
11
12 /**
13 * Class MailerAbstract.
14 *
15 * @since 1.0.0
16 */
17 abstract class MailerAbstract implements MailerInterface {
18
19 /**
20 * Which response code from HTTP provider is considered to be successful?
21 *
22 * @since 1.0.0
23 *
24 * @var int
25 */
26 protected $email_sent_code = 200;
27
28 /**
29 * @since 1.0.0
30 *
31 * @var Options
32 */
33 protected $options;
34
35 /**
36 * @since 1.0.0
37 *
38 * @var MailCatcherInterface
39 */
40 protected $phpmailer;
41
42 /**
43 * @since 1.0.0
44 *
45 * @var string
46 */
47 protected $mailer = '';
48
49 /**
50 * URL to make an API request to.
51 *
52 * @since 1.0.0
53 *
54 * @var string
55 */
56 protected $url = '';
57
58 /**
59 * @since 1.0.0
60 *
61 * @var array
62 */
63 protected $headers = array();
64
65 /**
66 * @since 1.0.0
67 *
68 * @var array
69 */
70 protected $body = array();
71
72 /**
73 * @since 1.0.0
74 *
75 * @var mixed
76 */
77 protected $response = array();
78
79 /**
80 * The error message recorded when email sending failed and the error can't be processed from the API response.
81 *
82 * @since 2.5.0
83 *
84 * @var string
85 */
86 protected $error_message = '';
87
88 /**
89 * The error code recorded when email sending failed.
90 *
91 * @since 4.8.0
92 *
93 * @var string
94 */
95 protected $error_code = '';
96
97 /**
98 * Should the email sent by this mailer have its "sent status" verified via its API?
99 *
100 * @since 2.5.0
101 *
102 * @var bool
103 */
104 protected $verify_sent_status = false;
105
106 /**
107 * The Connection object.
108 *
109 * @since 3.7.0
110 *
111 * @var ConnectionInterface
112 */
113 protected $connection;
114
115 /**
116 * The connection options object.
117 *
118 * @since 3.7.0
119 *
120 * @var Options
121 */
122 protected $connection_options;
123
124 /**
125 * Mailer constructor.
126 *
127 * @since 1.0.0
128 *
129 * @param MailCatcherInterface $phpmailer The MailCatcher object.
130 * @param ConnectionInterface $connection The Connection object.
131 */
132 public function __construct( MailCatcherInterface $phpmailer, $connection = null ) {
133
134 if ( ! is_null( $connection ) ) {
135 $this->connection = $connection;
136 } else {
137 $this->connection = wp_mail_smtp()->get_connections_manager()->get_primary_connection();
138 }
139
140 $this->connection_options = $this->connection->get_options();
141 $this->mailer = $this->connection->get_mailer_slug();
142 $this->options = Options::init();
143
144 // Only non-SMTP mailers need URL and extra processing for PHPMailer class.
145 if ( ! $this->connection_options->is_mailer_smtp() && empty( $this->url ) ) {
146 return;
147 }
148
149 $this->process_phpmailer( $phpmailer );
150 }
151
152 /**
153 * Re-use the MailCatcher class methods and properties.
154 *
155 * @since 1.0.0
156 *
157 * @param MailCatcherInterface $phpmailer The MailCatcher object.
158 */
159 public function process_phpmailer( $phpmailer ) {
160
161 // Make sure that we have access to PHPMailer class methods.
162 if ( ! wp_mail_smtp()->is_valid_phpmailer( $phpmailer ) ) {
163 return;
164 }
165
166 $this->phpmailer = $phpmailer;
167
168 // Prevent working with those methods, as they are not needed for SMTP-like mailers.
169 if ( $this->connection_options->is_mailer_smtp() ) {
170 return;
171 }
172
173 $this->set_headers( $this->phpmailer->getCustomHeaders() );
174 $this->set_from( $this->phpmailer->From, $this->phpmailer->FromName );
175 $this->set_recipients(
176 array(
177 'to' => $this->phpmailer->getToAddresses(),
178 'cc' => $this->phpmailer->getCcAddresses(),
179 'bcc' => $this->phpmailer->getBccAddresses(),
180 )
181 );
182 $this->set_subject( $this->phpmailer->Subject );
183 if ( $this->phpmailer->ContentType === 'text/plain' ) {
184 $this->set_content( $this->phpmailer->Body );
185 } else {
186 $this->set_content(
187 array(
188 'text' => $this->phpmailer->AltBody,
189 'html' => $this->phpmailer->Body,
190 )
191 );
192 }
193 $this->set_return_path( $this->phpmailer->From );
194 $this->set_reply_to( $this->phpmailer->getReplyToAddresses() );
195
196 /*
197 * In some cases we will need to modify the internal structure
198 * of the body content, if attachments are present.
199 * So lets make this call the last one.
200 */
201 $this->set_attachments( $this->phpmailer->getAttachments() );
202 }
203
204 /**
205 * Set the email headers.
206 *
207 * @since 1.0.0
208 *
209 * @param array $headers List of key=>value pairs.
210 */
211 public function set_headers( $headers ) {
212
213 foreach ( $headers as $header ) {
214 $name = isset( $header[0] ) ? $header[0] : false;
215 $value = isset( $header[1] ) ? $header[1] : false;
216
217 if ( empty( $name ) || empty( $value ) ) {
218 continue;
219 }
220
221 $this->set_header( $name, $value );
222 }
223 }
224
225 /**
226 * Set individual header key=>value pair for the email.
227 *
228 * @since 1.0.0
229 *
230 * @param string $name
231 * @param string $value
232 */
233 public function set_header( $name, $value ) {
234
235 $name = sanitize_text_field( $name );
236
237 $this->headers[ $name ] = WP::sanitize_value( $value );
238 }
239
240 /**
241 * Set email subject.
242 *
243 * @since 1.0.0
244 *
245 * @param string $subject
246 */
247 public function set_subject( $subject ) {
248
249 $this->set_body_param(
250 array(
251 'subject' => $subject,
252 )
253 );
254 }
255
256 /**
257 * Set the request params, that goes to the body of the HTTP request.
258 *
259 * @since 1.0.0
260 *
261 * @param array $param Key=>value of what should be sent to a 3rd party API.
262 *
263 * @internal param array $params
264 */
265 protected function set_body_param( $param ) {
266
267 $this->body = Options::array_merge_recursive( $this->body, $param );
268 }
269
270 /**
271 * Get the email body.
272 *
273 * @since 1.0.0
274 *
275 * @return string|array
276 */
277 public function get_body() {
278
279 return apply_filters( 'wp_mail_smtp_providers_mailer_get_body', $this->body, $this->mailer );
280 }
281
282 /**
283 * Get the email headers.
284 *
285 * @since 1.0.0
286 *
287 * @return array
288 */
289 public function get_headers() {
290
291 return apply_filters( 'wp_mail_smtp_providers_mailer_get_headers', $this->headers, $this->mailer );
292 }
293
294 /**
295 * Send the email.
296 *
297 * @since 1.0.0
298 * @since 1.8.0 Added timeout for requests, same as max_execution_time.
299 */
300 public function send() {
301
302 $timeout = (int) ini_get( 'max_execution_time' );
303
304 $params = Options::array_merge_recursive(
305 $this->get_default_params(),
306 array(
307 'headers' => $this->get_headers(),
308 'body' => $this->get_body(),
309 'timeout' => $timeout ? $timeout : 30,
310 )
311 );
312
313 $response = wp_safe_remote_post( $this->url, $params );
314
315 DebugEvents::add_debug(
316 esc_html__( 'An email request was sent.', 'wp-mail-smtp' )
317 );
318
319 $this->process_response( $response );
320 }
321
322 /**
323 * We might need to do something after the email was sent to the API.
324 * In this method we preprocess the response from the API.
325 *
326 * @since 1.0.0
327 *
328 * @param mixed $response Response array.
329 */
330 protected function process_response( $response ) {
331
332 if ( is_wp_error( $response ) ) {
333 // Save the error text.
334 foreach ( $response->errors as $error_code => $error_message ) {
335 $this->error_message .= Helpers::format_error_message( $error_message, $error_code ) . WP::EOL;
336 }
337
338 return;
339 }
340
341 if ( wp_remote_retrieve_response_code( $response ) !== $this->email_sent_code ) {
342 $this->error_code = wp_remote_retrieve_response_code( $response );
343 }
344
345 if ( isset( $response['body'] ) && WP::is_json( $response['body'] ) ) {
346 $response['body'] = json_decode( $response['body'] );
347 }
348
349 $this->response = $response;
350 }
351
352 /**
353 * Get the default params, required for wp_safe_remote_post().
354 *
355 * @since 1.0.0
356 *
357 * @return array
358 */
359 protected function get_default_params() {
360
361 return apply_filters(
362 'wp_mail_smtp_providers_mailer_get_default_params',
363 array(
364 'timeout' => 15,
365 'httpversion' => '1.1',
366 'blocking' => true,
367 ),
368 $this->mailer
369 );
370 }
371
372 /**
373 * Whether the email is sent or not.
374 * We basically check the response code from a request to provider.
375 * Might not be 100% correct, not guarantees that email is delivered.
376 *
377 * @since 1.0.0
378 *
379 * @return bool
380 */
381 public function is_email_sent() {
382
383 $is_sent = false;
384
385 if ( wp_remote_retrieve_response_code( $this->response ) === $this->email_sent_code ) {
386 $is_sent = true;
387 }
388
389 /**
390 * Filters whether the email is sent or not.
391 *
392 * @since 3.1.0
393 *
394 * @param bool $is_sent Whether the email is sent or not.
395 * @param MailerAbstract $mailer Mailer object.
396 */
397 return apply_filters( 'wp_mail_smtp_providers_mailer_is_email_sent', $is_sent, $this->mailer );
398 }
399
400 /**
401 * The error message when email sending failed.
402 * Should be overwritten when appropriate.
403 *
404 * @since 1.2.0
405 * @since 2.5.0 Return a non-empty error_message attribute.
406 *
407 * @return string
408 */
409 public function get_response_error() {
410
411 return ! empty( $this->error_message ) ? $this->error_message : '';
412 }
413
414 /**
415 * The error code when email sending failed.
416 * Should be overwritten when appropriate.
417 *
418 * @since 4.8.0
419 *
420 * @return string
421 */
422 public function get_response_error_code() {
423
424 return ! empty( $this->error_code ) ? $this->error_code : '';
425 }
426
427 /**
428 * Get a header from the retained send response.
429 *
430 * @since 4.9.0
431 *
432 * @param string $name Header name (case-insensitive).
433 *
434 * @return string
435 */
436 public function get_response_header( $name ) {
437
438 return wp_remote_retrieve_header( $this->response, $name );
439 }
440
441 /**
442 * Get the HTTP response code.
443 *
444 * @since 4.8.0
445 *
446 * @return int
447 */
448 public function get_response_code() {
449
450 if ( empty( $this->response ) || ! is_array( $this->response ) ) {
451 return 0;
452 }
453
454 return (int) wp_remote_retrieve_response_code( $this->response );
455 }
456
457 /**
458 * Whether the mailer supports the current PHP version or not.
459 *
460 * @since 1.0.0
461 *
462 * @return bool
463 */
464 public function is_php_compatible() {
465
466 $options = wp_mail_smtp()->get_providers()->get_options( $this->mailer, $this->connection );
467
468 return version_compare( phpversion(), $options->get_php_version(), '>=' );
469 }
470
471 /**
472 * This method is relevant to SMTP and Pepipost.
473 * All other custom mailers should override it with own information.
474 *
475 * @since 1.2.0
476 *
477 * @return string
478 */
479 public function get_debug_info() {
480
481 global $phpmailer;
482
483 $smtp_text = array();
484
485 // Mail mailer has nothing to return.
486 if ( $this->connection_options->is_mailer_smtp() ) {
487 // phpcs:disable
488 $smtp_text[] = '<strong>Host:</strong> ' . $phpmailer->Host;
489 $smtp_text[] = '<strong>Port:</strong> ' . $phpmailer->Port;
490 $smtp_text[] = '<strong>SMTPSecure:</strong> ' . Helpers::pvar( $phpmailer->SMTPSecure );
491 $smtp_text[] = '<strong>SMTPAutoTLS:</strong> ' . Helpers::pvar( $phpmailer->SMTPAutoTLS );
492 $smtp_text[] = '<strong>SMTPAuth:</strong> ' . Helpers::pvar( $phpmailer->SMTPAuth );
493 if ( ! empty( $phpmailer->SMTPOptions ) ) {
494 $smtp_text[] = '<strong>SMTPOptions:</strong> <code>' . wp_json_encode( $phpmailer->SMTPOptions ) . '</code>';
495 }
496 // phpcs:enable
497 }
498
499 $smtp_text[] = '<br><strong>Server:</strong>';
500 $smtp_text[] = '<strong>OpenSSL:</strong> ' . ( extension_loaded( 'openssl' ) && defined( 'OPENSSL_VERSION_TEXT' ) ? OPENSSL_VERSION_TEXT : 'No' );
501 if ( function_exists( 'apache_get_modules' ) ) {
502 $modules = apache_get_modules();
503 $smtp_text[] = '<strong>Apache.mod_security:</strong> ' . ( in_array( 'mod_security', $modules, true ) || in_array( 'mod_security2', $modules, true ) ? 'Yes' : 'No' );
504 }
505 if ( function_exists( 'selinux_is_enabled' ) ) {
506 $smtp_text[] = '<strong>OS.SELinux:</strong> ' . ( selinux_is_enabled() ? 'Yes' : 'No' );
507 }
508 if ( function_exists( 'grsecurity_is_enabled' ) ) {
509 $smtp_text[] = '<strong>OS.grsecurity:</strong> ' . ( grsecurity_is_enabled() ? 'Yes' : 'No' );
510 }
511
512 return implode( '<br>', $smtp_text );
513 }
514
515 /**
516 * Get the email addresses for the reply to email parameter.
517 *
518 * @deprecated 2.1.1
519 *
520 * @since 2.1.0
521 * @since 2.1.1 Not used anymore.
522 *
523 * @return array
524 */
525 public function get_reply_to_addresses() {
526
527 _deprecated_function( __CLASS__ . '::' . __METHOD__, '2.1.1 of WP Mail SMTP plugin' );
528
529 $reply_to = $this->phpmailer->getReplyToAddresses();
530
531 // Return the passed reply to addresses, if defined.
532 if ( ! empty( $reply_to ) ) {
533 return $reply_to;
534 }
535
536 // Return the default reply to addresses.
537 return apply_filters(
538 'wp_mail_smtp_providers_mailer_default_reply_to_addresses',
539 $this->default_reply_to_addresses()
540 );
541 }
542
543 /**
544 * Get the default email addresses for the reply to email parameter.
545 *
546 * @deprecated 2.1.1
547 *
548 * @since 2.1.0
549 * @since 2.1.1 Not used anymore.
550 *
551 * @return array
552 */
553 public function default_reply_to_addresses() {
554
555 _deprecated_function( __CLASS__ . '::' . __METHOD__, '2.1.1 of WP Mail SMTP plugin' );
556
557 return [
558 $this->phpmailer->From => [
559 $this->phpmailer->From,
560 $this->phpmailer->FromName,
561 ],
562 ];
563 }
564
565 /**
566 * Should the email sent by this mailer have its "sent status" verified via its API?
567 *
568 * @since 2.5.0
569 *
570 * @return bool
571 */
572 public function should_verify_sent_status() {
573
574 return $this->verify_sent_status;
575 }
576
577 /**
578 * Verify the "sent status" of the provided email log ID.
579 * The actual verification background task is triggered in the below action hook.
580 *
581 * @since 2.5.0
582 *
583 * @param int $email_log_id The ID of the email log.
584 */
585 public function verify_sent_status( $email_log_id ) {
586
587 if ( ! $this->should_verify_sent_status() ) {
588 return;
589 }
590
591 do_action( 'wp_mail_smtp_providers_mailer_verify_sent_status', $email_log_id, $this );
592 }
593
594 /**
595 * Get the name/slug of the current mailer.
596 *
597 * @since 2.5.0
598 *
599 * @return string
600 */
601 public function get_mailer_name() {
602
603 return $this->mailer;
604 }
605
606 /**
607 * Get PHPMailer attachment file content.
608 *
609 * @since 3.1.0
610 *
611 * @param array $attachment PHPMailer attachment.
612 *
613 * @return string|false
614 */
615 public function get_attachment_file_content( $attachment ) {
616
617 $file = false;
618
619 /*
620 * We are not using WP_Filesystem API as we can't reliably work with it.
621 * It is not always available, same as credentials for FTP.
622 */
623 try {
624 if ( $attachment[5] === true ) { // Whether there is string attachment.
625 $file = $attachment[0];
626 } elseif ( is_file( $attachment[0] ) && is_readable( $attachment[0] ) ) {
627 $file = file_get_contents( $attachment[0] );
628 }
629 } catch ( \Exception $e ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
630 // We don't handle this exception as we define a default value above.
631 }
632
633 return $file;
634 }
635
636 /**
637 * Get PHPMailer attachment file size.
638 *
639 * @since 3.4.0
640 *
641 * @param array $attachment PHPMailer attachment.
642 *
643 * @return int|false
644 */
645 public function get_attachment_file_size( $attachment ) {
646
647 $size = false;
648
649 if ( $attachment[5] === true ) { // Whether there is string attachment.
650 $size = Helpers::strsize( $attachment[0] );
651 } elseif ( is_file( $attachment[0] ) && is_readable( $attachment[0] ) ) {
652 $size = filesize( $attachment[0] );
653 }
654
655 return $size;
656 }
657
658 /**
659 * Get PHPMailer attachment file name.
660 *
661 * @since 3.4.0
662 *
663 * @param array $attachment PHPMailer attachment.
664 *
665 * @return string
666 */
667 public function get_attachment_file_name( $attachment ) {
668
669 $filetype = str_replace( ';', '', trim( $attachment[4] ) );
670
671 return ! empty( $attachment[2] ) ? trim( $attachment[2] ) : 'file-' . wp_hash( microtime() ) . '.' . $filetype;
672 }
673
674 /**
675 * Perform remote request with merged default params.
676 *
677 * @since 3.4.0
678 *
679 * @param string $url Request url.
680 * @param array $params Request params.
681 *
682 * @return array
683 */
684 public function remote_request( $url, $params ) {
685
686 if ( ! isset( $params['method'] ) ) {
687 $params['method'] = 'POST';
688 }
689
690 $params = Options::array_merge_recursive( $this->get_default_params(), $params );
691
692 /**
693 * Filters request params.
694 *
695 * @since 3.4.0
696 *
697 * @param array $params Request params.
698 * @param MailerAbstract $mailer Mailer object.
699 */
700 $params = apply_filters( 'wp_mail_smtp_providers_mailer_remote_request_params', $params, $this );
701
702 /**
703 * Filters request url.
704 *
705 * @since 3.4.0
706 *
707 * @param string $url Request url.
708 * @param MailerAbstract $mailer Mailer object.
709 */
710 $url = apply_filters( 'wp_mail_smtp_providers_mailer_remote_request_url', $url, $this );
711
712 return wp_safe_remote_request( $url, $params );
713 }
714
715 /**
716 * Get the Connection object.
717 *
718 * @since 3.7.0
719 *
720 * @return ConnectionInterface
721 */
722 public function get_connection() {
723
724 return $this->connection;
725 }
726
727 /**
728 * Sanitize email header values.
729 *
730 * @param string $name Name of the header.
731 * @param string $value Value of the header.
732 *
733 * @since 3.11.1
734 */
735 public function sanitize_header_value( $name, $value ) {
736
737 if (
738 in_array(
739 strtolower( $name ),
740 [
741 'cc',
742 'bcc',
743 'reply-to',
744 'message-id',
745 'list-unsubscribe',
746 'references',
747 'in-reply-to'
748 ],
749 true
750 )
751 ) {
752 return $value;
753 }
754
755 return WP::sanitize_value( $value );
756 }
757 }
758