PluginProbe
Mailgun for WordPress / 2.2.2
Mailgun for WordPress v2.2.2
2.2.3 2.2.2 2.2.1 trunk 1.9.9 2.0.0 2.0.1 2.1.0 2.1.1 2.1.10 2.1.2 2.1.3 2.1.4 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0
mailgun / mailgun.php

mailgun.php in Mailgun for WordPress 2.2.2, at mailgun.php

580 lines 20.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Plugin Name: Mailgun
4 * Plugin URI: http://wordpress.org/extend/plugins/mailgun/
5 * Description: Mailgun integration for WordPress
6 * Version: 2.2.2
7 * Requires PHP: 7.4
8 * Requires at least: 5.6
9 * Author: Mailgun
10 * Author URI: http://www.mailgun.com/
11 * License: GPLv2 or later
12 * Text Domain: mailgun
13 * Domain Path: /languages/.
14 *
15 * @package Mailgun
16 */
17
18 /*
19 * mailgun-wordpress-plugin - Sending mail from Wordpress using Mailgun
20 * Copyright (C) 2016 Mailgun, et al.
21 *
22 * This program is free software; you can redistribute it and/or modify
23 * it under the terms of the GNU General Public License as published by
24 * the Free Software Foundation; either version 2 of the License, or
25 * (at your option) any later version.
26 *
27 * This program is distributed in the hope that it will be useful,
28 * but WITHOUT ANY WARRANTY; without even the implied warranty of
29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 * GNU General Public License for more details.
31 *
32 * You should have received a copy of the GNU General Public License along
33 * with this program; if not, write to the Free Software Foundation, Inc.,
34 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
35 */
36
37 /**
38 * Entrypoint for the Mailgun plugin. Sets up the mailing "strategy" -
39 * either API or SMTP.
40 *
41 * Registers handlers for later actions and sets up config variables with
42 * WordPress.
43 */
44 class Mailgun {
45
46 /**
47 * @var Mailgun $instance
48 */
49 private static Mailgun $instance;
50
51 /**
52 * @var false|mixed|null
53 */
54 private $options;
55
56 /**
57 * @var string
58 */
59 protected string $plugin_file;
60
61 /**
62 * @var string
63 */
64 protected string $plugin_basename;
65
66 /**
67 * @var string
68 */
69 protected string $assetsDir;
70
71 /**
72 * @var string
73 */
74 private string $api_endpoint;
75
76 /**
77 * Setup shared functionality for Admin and Front End.
78 */
79 public function __construct() {
80 $this->options = get_option( 'mailgun' );
81 $this->plugin_file = __FILE__;
82 $this->plugin_basename = plugin_basename( $this->plugin_file );
83 $this->assetsDir = plugin_dir_url( $this->plugin_file ) . 'assets/';
84
85 // Either override the wp_mail function or configure PHPMailer to use the
86 // Mailgun SMTP servers
87 // When using SMTP, we also need to inject a `wp_mail` filter to make "from" settings
88 // work properly. Fixed issues with 1.5.7+
89 if ( $this->get_option( 'useAPI' ) || ( defined( 'MAILGUN_USEAPI' ) && MAILGUN_USEAPI ) ) {
90 if ( ! function_exists( 'wp_mail' ) ) {
91 if ( ! include_once __DIR__ . '/includes/wp-mail-api.php' ) {
92 $this->deactivate_and_die( __DIR__ . '/includes/wp-mail-api.php' );
93 }
94 }
95 } else {
96 // Using SMTP, include the SMTP filter
97 if ( ! function_exists( 'mg_smtp_mail_filter' ) ) {
98 if ( ! include __DIR__ . '/includes/wp-mail-smtp.php' ) {
99 $this->deactivate_and_die( __DIR__ . '/includes/wp-mail-smtp.php' );
100 }
101 }
102 add_filter( 'wp_mail', 'mg_smtp_mail_filter' );
103 add_action( 'phpmailer_init', array( &$this, 'phpmailer_init' ) );
104 add_action( 'wp_mail_failed', 'wp_mail_failed' );
105 }
106 }
107
108 /**
109 * @return static
110 */
111 public static function getInstance(): Mailgun {
112 if ( ! isset( self::$instance ) ) {
113 self::$instance = new self();
114 }
115
116 return self::$instance;
117 }
118
119 /**
120 * Get specific option from the options table.
121 *
122 * @param string $option Name of option to be used as array key for retrieving the specific value
123 * @param array|null $options Array to iterate over for specific values
124 * @param bool $defaultValue Default value to return if option is not found
125 * @return mixed
126 */
127 public function get_option( string $option, ?array $options = null, bool $defaultValue = false ) {
128 if ( is_null( $options ) ) {
129 $options = &$this->options;
130 }
131
132 if ( isset( $options[ $option ] ) ) {
133 return $options[ $option ];
134 }
135
136 return $defaultValue;
137 }
138
139 /**
140 * Hook into phpmailer to override SMTP based configurations
141 * to use the Mailgun SMTP server.
142 *
143 * @param object $phpmailer The PHPMailer object to modify by reference
144 *
145 * @return void
146 */
147 public function phpmailer_init( &$phpmailer ): void {
148 $username = ( defined( 'MAILGUN_USERNAME' ) && MAILGUN_USERNAME ) ? MAILGUN_USERNAME : $this->get_option( 'username' );
149 $domain = ( defined( 'MAILGUN_DOMAIN' ) && MAILGUN_DOMAIN ) ? MAILGUN_DOMAIN : $this->get_option( 'domain' );
150 $username = preg_replace( '/@.+$/', '', $username ) . "@{$domain}";
151 $secure = ( defined( 'MAILGUN_SECURE' ) && MAILGUN_SECURE ) ? MAILGUN_SECURE : $this->get_option( 'secure' );
152 $sectype = ( defined( 'MAILGUN_SECTYPE' ) && MAILGUN_SECTYPE ) ? MAILGUN_SECTYPE : $this->get_option( 'sectype' );
153 $password = ( defined( 'MAILGUN_PASSWORD' ) && MAILGUN_PASSWORD ) ? MAILGUN_PASSWORD : $this->get_option( 'password' );
154 $region = ( defined( 'MAILGUN_REGION' ) && MAILGUN_REGION ) ? MAILGUN_REGION : $this->get_option( 'region' );
155
156 $smtp_endpoint = mg_smtp_get_region( $region );
157 $smtp_endpoint = (bool) $smtp_endpoint ? $smtp_endpoint : 'smtp.mailgun.org';
158
159 $phpmailer->Mailer = 'smtp';
160 $phpmailer->Host = $smtp_endpoint;
161
162 if ( 'ssl' === $sectype ) {
163 // For SSL-only connections, use 465
164 $phpmailer->Port = 465;
165 } else {
166 // Otherwise, use 587.
167 $phpmailer->Port = 587;
168 }
169
170 $phpmailer->SMTPAuth = true;
171 $phpmailer->Username = $username;
172 $phpmailer->Password = $password;
173
174 $phpmailer->SMTPSecure = (bool) $secure ? $sectype : '';
175 // Without this line... wp_mail for SMTP-only will always return false. But why? :(
176 $phpmailer->Debugoutput = 'mg_smtp_debug_output';
177 $phpmailer->SMTPDebug = 2;
178
179 // Emit some logging for SMTP connection
180 mg_smtp_debug_output(
181 sprintf( 'PHPMailer configured to send via %s:%s', $phpmailer->Host, $phpmailer->Port ),
182 'DEBUG'
183 );
184 }
185
186 /**
187 * Deactivate this plugin and die.
188 * Deactivate the plugin when files critical to it's operation cannot be loaded
189 *
190 * @param string $file files critical to plugin functionality
191 *
192 * @return void
193 */
194 public function deactivate_and_die( $file ): void {
195 load_plugin_textdomain( 'mailgun', false, 'mailgun/languages' );
196 $message = sprintf(
197 __( 'Mailgun has been automatically deactivated because the file <strong>%s</strong> is missing. Please reinstall the plugin and reactivate.' ),
198 $file
199 );
200 if ( ! function_exists( 'deactivate_plugins' ) ) {
201 include ABSPATH . 'wp-admin/includes/plugin.php';
202 }
203 deactivate_plugins( __FILE__ );
204 wp_die( $message );
205 }
206
207 /**
208 * Make a Mailgun api call.
209 *
210 * @param string $uri The endpoint for the Mailgun API
211 * @param array $params Array of parameters passed to the API
212 * @param string $method The form request type
213 *
214 * @return string
215 */
216 public function api_call( string $uri, array $params = [], string $method = 'POST' ): string {
217 $options = get_option( 'mailgun' );
218 $getRegion = ( defined( 'MAILGUN_REGION' ) && MAILGUN_REGION ) ? MAILGUN_REGION : $options['region'];
219 $apiKey = ( defined( 'MAILGUN_APIKEY' ) && MAILGUN_APIKEY ) ? MAILGUN_APIKEY : $options['apiKey'];
220 $domain = ( defined( 'MAILGUN_DOMAIN' ) && MAILGUN_DOMAIN ) ? MAILGUN_DOMAIN : $options['domain'];
221
222 if ( ! function_exists( 'mg_api_get_region' ) ) {
223 include __DIR__ . '/includes/mg-filter.php';
224 }
225 $region = mg_api_get_region( $getRegion );
226 $this->api_endpoint = ( $region ) ?: 'https://api.mailgun.net/v3/';
227
228 $time = time();
229 $url = $this->api_endpoint . $uri;
230 $headers = [
231 'Authorization' => 'Basic ' . base64_encode( "api:{$apiKey}" ),
232 ];
233
234 switch ( $method ) {
235 case 'GET':
236 $params['sess'] = '';
237 $querystring = http_build_query( $params );
238 $url = $url . '?' . $querystring;
239 $params = '';
240 break;
241 case 'POST':
242 case 'PUT':
243 case 'DELETE':
244 $params['sess'] = '';
245 $params['time'] = $time;
246 $params['hash'] = hash( 'sha256', date( 'U' ) );
247 break;
248 }
249
250 // make the request
251 $args = [
252 'method' => $method,
253 'body' => $params,
254 'headers' => $headers,
255 'sslverify' => true,
256 ];
257
258 // make the remote request
259 $result = wp_remote_request( $url, $args );
260 if ( ! is_wp_error( $result ) ) {
261 return $result['body'];
262 }
263
264 if ( is_callable( $result ) ) {
265 return $result->get_error_message();
266 }
267
268 if ( is_array( $result ) ) {
269 if ( isset( $result['response'] ) ) {
270 return $result['response']['message'] ?? '';
271 }
272 }
273
274 return '';
275 }
276
277 /**
278 * Get account associated lists.
279 *
280 * @return array
281 *
282 * @throws JsonException
283 */
284 public function get_lists(): array {
285 $results = [];
286
287 $lists_json = $this->api_call( 'lists', [], 'GET' );
288
289 $lists_arr = json_decode( $lists_json, true, 512, JSON_THROW_ON_ERROR );
290 if ( isset( $lists_arr['items'] ) && ! empty( $lists_arr['items'] ) ) {
291 $results = $lists_arr['items'];
292 }
293
294 return $results;
295 }
296
297 /**
298 * @return array
299 * @throws JsonException
300 */
301 public function getTrackingSettings(): array
302 {
303 $domain = ( defined( 'MAILGUN_DOMAIN' ) && MAILGUN_DOMAIN ) ? MAILGUN_DOMAIN : $this->get_option( 'domain' );
304 if (!$domain) {
305 return [];
306 }
307 $settings = $this->api_call(sprintf("domains/%s/tracking", $domain), [], 'GET' );
308
309 return json_decode( $settings, true, 512, JSON_THROW_ON_ERROR );
310 }
311
312 /**
313 * Handle add list ajax post.
314 *
315 * @return void json
316 *
317 * @throws JsonException
318 */
319 public function add_list(): void {
320 if ( ! isset( $_POST['mailgun_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['mailgun_nonce'] ) ), 'mailgun_subscribe' ) ) {
321 wp_send_json( [ 'status' => 403, 'message' => 'Invalid request.' ], 403 );
322 return;
323 }
324
325 $name = sanitize_text_field( $_POST['name'] ?? null );
326 $email = sanitize_text_field( $_POST['email'] ?? null );
327 $list_addresses = [];
328
329 $valid_lists = array_column( $this->get_lists(), 'address' );
330
331 foreach ( ( $_POST['addresses'] ?? [] ) as $address => $val ) {
332 $sanitized_address = sanitize_text_field( $address );
333 if ( in_array( $sanitized_address, $valid_lists, true ) ) {
334 $list_addresses[ $sanitized_address ] = sanitize_text_field( $val );
335 }
336 }
337
338 if ( ! empty( $list_addresses ) ) {
339 $result = [];
340 foreach ( $list_addresses as $address => $val ) {
341 $result[] = $this->api_call(
342 "lists/{$address}/members",
343 [
344 'address' => $email,
345 'name' => $name,
346 ]
347 );
348 }
349 $message = 'Thank you!';
350 if ( $result ) {
351 $message = 'Something went wrong';
352 $response = json_decode( $result[0], true );
353 if ( is_array( $response ) && isset( $response['message'] ) ) {
354 $message = $response['message'];
355 }
356 }
357 echo json_encode(
358 [
359 'status' => 200,
360 'message' => $message,
361 ],
362 JSON_THROW_ON_ERROR
363 );
364 } else {
365 echo json_encode(
366 [
367 'status' => 500,
368 'message' => 'Uh oh. We weren\'t able to add you to the list' . count( $list_addresses ) ? 's.' : '. Please try again.',
369 ],
370 JSON_THROW_ON_ERROR
371 );
372 }
373 wp_die();
374 }
375
376 /**
377 * Frontend List Form.
378 *
379 * @param string $list_address Mailgun address list id
380 * @param array $args widget arguments
381 * @throws JsonException
382 */
383 public function list_form( string $list_address, array $args = []): void {
384 $widgetId = $args['widget_id'] ?? 0;
385 $widget_class_id = "mailgun-list-widget-{$widgetId}";
386 $form_class_id = "list-form-{$widgetId}";
387
388 // List addresses from the plugin config
389 $list_addresses = array_map( 'trim', explode( ',', $list_address ) );
390
391 // All list info from the API; used for list info when more than one list is available to subscribe to
392 $all_list_addresses = $this->get_lists();
393 ?>
394 <div class="mailgun-list-widget-front <?php echo esc_attr( $widget_class_id ); ?> widget">
395 <form class="list-form <?php echo esc_attr( $form_class_id ); ?>">
396 <div class="mailgun-list-widget-inputs">
397 <?php if ( isset( $args['list_title'] ) ) : ?>
398 <div class="mailgun-list-title">
399 <h4 class="widget-title">
400 <span><?php echo wp_kses_data( $args['list_title'] ); ?></span>
401 </h4>
402 </div>
403 <?php endif; ?>
404 <?php if ( isset( $args['list_description'] ) ) : ?>
405 <div class="mailgun-list-description">
406 <p class="widget-description">
407 <span><?php echo wp_kses_data( $args['list_description'] ); ?></span>
408 </p>
409 </div>
410 <?php endif; ?>
411 <?php if ( isset( $args['collect_name'] ) && (int) $args['collect_name'] === 1 ) : ?>
412 <p class="mailgun-list-widget-name">
413 <label><strong>Name:</strong></label>
414 <input type="text" name="name" placeholder="Name"/>
415 </p>
416 <?php endif; ?>
417 <p class="mailgun-list-widget-email">
418 <label><strong>Email:</strong></label>
419 <input type="text" name="email" placeholder="Email"/>
420 </p>
421 </div>
422
423 <div class="mailgun-list-widget-lists">
424 <?php if ( count( $list_addresses ) > '1' ) : ?>
425 <ul class="mailgun-lists" style="list-style: none;">
426 <?php
427 foreach ( $all_list_addresses as $la ) :
428 if ( ! in_array( $la['address'], $list_addresses, true ) ) :
429 continue;
430 endif;
431 ?>
432 <li>
433 <input type="checkbox" class="mailgun-list-name"
434 name="addresses[<?php echo esc_attr( $la['address'] ); ?>]"/> <?php echo esc_attr( $la['name'] ?: $la['address'] ); ?>
435 </li>
436 <?php endforeach; ?>
437 </ul>
438 <?php else : ?>
439 <input type="hidden" name="addresses[<?php echo esc_attr( $list_addresses[0] ); ?>]" value="on"/>
440 <?php endif; ?>
441
442 <input class="mailgun-list-submit-button" data-form-id="<?php echo esc_attr( $form_class_id ); ?>" type="button"
443 value="Subscribe"/>
444 <input type="hidden" name="mailgun-submission" value="1"/>
445 <?php wp_nonce_field( 'mailgun_subscribe', 'mailgun_nonce' ); ?></div>
446
447 </form>
448 <div class="widget-list-panel result-panel" style="display:none;">
449 <span>Thank you for subscribing!</span>
450 </div>
451 </div>
452
453 <script>
454 jQuery(document).ready(function () {
455
456 jQuery('.mailgun-list-submit-button').on('click', function () {
457
458 var form_id = jQuery(this).data('form-id')
459
460 if (jQuery('.mailgun-list-name').length > 0 && jQuery('.' + form_id + ' .mailgun-list-name:checked').length < 1) {
461 alert('Please select a list to subscribe to.')
462 return
463 }
464
465 if (jQuery('.' + form_id + ' .mailgun-list-widget-name input') && jQuery('.' + form_id + ' .mailgun-list-widget-name input').val() === '') {
466 alert('Please enter your subscription name.')
467 return
468 }
469
470 if (jQuery('.' + form_id + ' .mailgun-list-widget-email input').val() === '') {
471 alert('Please enter your subscription email.')
472 return
473 }
474
475 // Email validation regex pattern
476 var emailPattern = /.+@.+\..{2,}/;
477
478 if (!emailPattern.test(jQuery('.' + form_id + ' .mailgun-list-widget-email input').val())) {
479 alert('Please enter a valid email address.')
480 return
481 }
482
483 jQuery.ajax({
484 url: '<?php echo admin_url( 'admin-ajax.php?action=add_list' ); ?>',
485 action: 'add_list',
486 type: 'post',
487 dataType: 'json',
488 data: jQuery('.' + form_id + '').serialize(),
489 success: function (data) {
490 data_msg = data.message
491 already_exists = false
492 if (data_msg !== undefined) {
493 already_exists = data_msg.indexOf('Address already exists') > -1
494 }
495
496 // success
497 if ((data.status === 200)) {
498 jQuery('.<?php echo esc_attr( $widget_class_id ); ?> .widget-list-panel').css('display', 'none')
499 jQuery('.<?php echo esc_attr( $widget_class_id ); ?> .list-form').css('display', 'none')
500 jQuery('.<?php echo esc_attr( $widget_class_id ); ?> .result-panel').css('display', 'block')
501 // error
502 } else {
503 alert(data_msg)
504 }
505 }
506 })
507 })
508 })
509 </script>
510
511 <?php
512 }
513
514 /**
515 * Initialize List Form.
516 *
517 * @param array $atts Form attributes
518 *
519 * @return string
520 *
521 * @throws JsonException
522 */
523 public function build_list_form( array $atts ): string {
524 if ( isset( $atts['id'] ) && $atts['id'] !== '' ) {
525 $args['widget_id'] = hash( 'sha256', rand( 10000, 99999 ) . $atts['id'] );
526
527 if (isset( $atts['collect_name'] ) ) {
528 $args['collect_name'] = true;
529 }
530
531 if (isset( $atts['title'] ) ) {
532 $args['list_title'] = $atts['title'];
533 }
534
535 if (isset( $atts['description'] ) ) {
536 $args['list_description'] = $atts['description'];
537 }
538
539 ob_start();
540 $this->list_form( $atts['id'], $args );
541 return ob_get_clean();
542 }
543
544 return '<span>Mailgun list ID needed to render form!</span>
545 <br/>
546 <strong>Example :</strong> [mailgun id="[your list id]"]';
547 }
548
549 /**
550 * Initialize List Widget.
551 */
552 public function load_list_widget() {
553 register_widget( 'List_Widget' );
554 add_shortcode( 'mailgun', array( &$this, 'build_list_form' ) );
555 }
556
557 /**
558 * @return string
559 */
560 public function getAssetsPath(): string {
561 return $this->assetsDir;
562 }
563 }
564
565 $mailgun = Mailgun::getInstance();
566
567 if (include __DIR__ . '/includes/widget.php' ) {
568 add_action('widgets_init', array( &$mailgun, 'load_list_widget' ));
569 add_action('wp_ajax_nopriv_add_list', array( &$mailgun, 'add_list' ));
570 add_action('wp_ajax_add_list', array( &$mailgun, 'add_list' ));
571 }
572
573 if (is_admin()) {
574 if (include __DIR__ . '/includes/admin.php') {
575 $mailgunAdmin = new MailgunAdmin();
576 } else {
577 $mailgun->deactivate_and_die(__DIR__ . '/includes/admin.php');
578 }
579 }
580