PluginProbe
Mail Queue / 1.5.0
Mail Queue v1.5.0
1.6.1 1.6.0 1.5.1 trunk 1.0 1.1 1.2 1.3 1.3.1 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.5.0
mail-queue / mail-queue.php

mail-queue.php in Mail Queue 1.5.0, at mail-queue.php

687 lines 26.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Plugin Name: Mail Queue
5 * Plugin URI: https://www.webdesign-muenchen.de/wordpress-plugin-mail-queue/
6 * Description: Take Control and improve Security of wp_mail(). Queue and log outgoing emails, and get alerted, if your website wants to send more emails than usual.
7 * Version: 1.5.0
8 * Requires at least: 5.9
9 * Requires PHP: 7.4
10 * Author: WDM
11 * Author URI: https://www.webdesign-muenchen.de
12 * License: GPLv3 or later
13 * License URI: https://www.gnu.org/licenses/gpl-3.0.html
14 */
15
16 if (!defined('ABSPATH')) { exit; }
17
18
19 /* ***************************************************************
20 PLUGIN VERSION
21 **************************************************************** */
22
23 $wdm_wpma_version = '1.5.0';
24
25
26
27
28
29 /* ***************************************************************
30 PLUGIN DEFAULT SETTINGS
31 **************************************************************** */
32 function wdm_wpma_get_settings() {
33 $defaults = array(
34 'enabled' => '0',
35 'alert_enabled' => '0',
36 'email' => get_option('admin_email'),
37 'email_amount' => '10',
38 'pause_on_alert' => '0',
39 'queue_amount' => '1',
40 'queue_interval' => '5',
41 'queue_interval_unit' => 'minutes',
42 'clear_queue' => '14',
43 'tableName' => 'mail_queue',
44 'triggercount' => 0,
45 );
46 $args = get_option('wdm_wpma_settings');
47 $options = wp_parse_args($args,$defaults);
48
49 if ($options['queue_interval_unit'] == 'seconds') {
50 $options['queue_interval'] = intval($options['queue_interval']);
51 if ($options['queue_interval'] < 10) { $options['queue_interval'] = 10; } // Minimum Interval 10 Seconds
52 } else {
53 $options['queue_interval'] = intval($options['queue_interval']) * 60;
54 }
55
56 $options['clear_queue'] = intval($options['clear_queue']) * 24;
57 return $options;
58 }
59
60
61 function wdm_wpma_refresh_settings() {
62 global $wdm_wpma_options;
63 $wdm_wpma_options = wdm_wpma_get_settings();
64 return $wdm_wpma_options;
65 }
66
67
68 function wdm_wpma_is_pause_on_alert_active() {
69 return get_option('wdm_wpma_pause_on_alert_active') == '1';
70 }
71
72 function wdm_wpma_clear_pause_on_alert_state() {
73 delete_option('wdm_wpma_pause_on_alert_active');
74 }
75
76 function wdm_wpma_must_activate_pause_on_alert() {
77 global $wdm_wpma_options;
78
79 if (!isset($wdm_wpma_options['enabled']) || $wdm_wpma_options['enabled'] != '1') { return false; }
80 if (!isset($wdm_wpma_options['alert_enabled']) || $wdm_wpma_options['alert_enabled'] !== '1') { return false; }
81 if (!isset($wdm_wpma_options['pause_on_alert']) || $wdm_wpma_options['pause_on_alert'] !== '1') { return false; }
82 if (wdm_wpma_is_pause_on_alert_active()) { return false; }
83
84 return true;
85 }
86
87 function wdm_wpma_maybe_activate_pause_on_alert() {
88
89 if (!wdm_wpma_must_activate_pause_on_alert()) { return false; }
90
91 $settings = get_option('wdm_wpma_settings', array());
92 if (!is_array($settings)) { $settings = array(); }
93 $settings['enabled'] = 'paused';
94
95 update_option('wdm_wpma_settings', $settings, true);
96 update_option('wdm_wpma_pause_on_alert_active', '1', false);
97
98 wdm_wpma_push_queue_event([
99 'name' => 'autopause-activated',
100 ]);
101
102 wdm_wpma_refresh_settings();
103
104 return true;
105 }
106
107
108
109 $wdm_wpma_mailid = 0;
110 $wdm_wpma_options = wdm_wpma_get_settings(); // Get Settings
111
112
113
114
115
116 /* ***************************************************************
117 Overwrite wp_mail() if Plugin enabled and no Cron is running
118 **************************************************************** */
119 $wdm_wpma_pre_wp_mail_priority = 99999;
120
121 if (in_array($wdm_wpma_options['enabled'], ['1', 'paused']) && wp_doing_cron() == false) {
122 // High priority: run late in the game to react to previous filters
123 add_filter('pre_wp_mail', 'wdm_wpma_prewpmail', $wdm_wpma_pre_wp_mail_priority, 2);
124 }
125
126 // pre WP Mail Filter
127 function wdm_wpma_prewpmail($return, $atts) {
128
129 global $wpdb, $wdm_wpma_options;
130
131 if (!is_null($return)) {
132 // Another pre_wp_mail filter has already returned a value, so the mail is not added to the queue
133 return $return;
134 }
135
136 // Mail Variables
137 $to = $atts['to'];
138 $subject = $atts['subject'];
139 $message = $atts['message'];
140 $headers = $atts['headers'];
141 $attachments = $atts['attachments'];
142 $status = 'queue';
143
144 // Make sure that $headers always is an array
145 if ($headers) {
146 if (!is_array($headers)) {
147 $headers = explode( "\n", str_replace( "\r\n", "\n", $headers ) );
148 }
149 } else {
150 $headers = [];
151 }
152
153 // Loop through email headers
154 // - Instant Sending or Prio Mail?
155 // - Track if ContentType header is set
156 $hasContentTypeHeader = false;
157 $hasFromHeader = false;
158 foreach($headers as $index => $val) {
159 $val = trim($val);
160 if (preg_match("#^X-Mail-Queue-Prio: +Instant *$#i",$val)) {
161 array_splice($headers,$index,1);
162 $status = 'instant';
163 break;
164 } else if (preg_match("#^X-Mail-Queue-Prio: +High *$#i",$val)) {
165 array_splice($headers,$index,1);
166 $status = 'high';
167 break;
168 } else if (preg_match('#^Content-Type:#i',$val)) {
169 $hasContentTypeHeader = true;
170 } else if (preg_match('#^From:#i',$val)) {
171 $hasFromHeader = true;
172 }
173 }
174
175 // For all emails that are stored in the queue to be sent later:
176 // Store custom filtered values in headers if available.
177 // Support the following hooks used in wp_mail:
178 // - wp_mail_content_type
179 // - wp_mail_charset
180 // - wp_mail_from
181 // - wp_mail_from_name
182 if ($status !== 'instant') {
183 if (!$hasContentTypeHeader) {
184 $contentType = apply_filters('wp_mail_content_type','text/plain');
185 if ( $contentType ) {
186 if (stripos($contentType,'multipart') === false) {
187 $charset = apply_filters('wp_mail_charset',get_bloginfo('charset'));
188 } else {
189 $charset = '';
190 }
191 $headers[] = 'Content-Type: '.$contentType.($charset ? '; charset="'.$charset.'"' : '');
192 }
193 }
194 if (!$hasFromHeader) {
195 $from_Email = apply_filters('wp_mail_from','');
196 if ($from_Email) {
197 $fromName = apply_filters('wp_mail_from_name','');
198 if ($fromName) {
199 $headers[] = 'From: '.$fromName.' <'.$from_Email.'>';
200 } else {
201 $headers[] = 'From: '.$from_Email;
202 }
203 }
204 }
205 }
206
207
208 // Write email in Queue
209 $tableName = $wpdb->prefix.$wdm_wpma_options['tableName'];
210 $data = array(
211 'timestamp'=> current_time('mysql',false),
212 'recipient'=> maybe_serialize($to),
213 'subject'=> $subject,
214 'message'=> $message,
215 'status' => $status,
216 'attachments' => ''
217 );
218 if (isset($headers) && $headers) { $data['headers'] = maybe_serialize($headers); }
219
220 // store attachments in /attachments/ Folder, to address them later
221 if (isset($attachments) && $attachments && $attachments != '') {
222
223 $subfolder = time().'-'.rand(0,999999);
224 $foldercreated = wp_mkdir_p(plugin_dir_path(__FILE__).'attachments/'.$subfolder);
225 if (!$foldercreated) {
226 error_log('Could not create Subfolder for Email attachment');
227 $data['info'] = 'Error: Could not store attachments';
228 } else {
229 if (!is_array($attachments)) { $attachments = array($attachments); }
230 $newattachments = array();
231 global $wp_filesystem;
232 if ( ! is_a( $wp_filesystem, 'WP_Filesystem_Base') ){
233 include_once(ABSPATH . 'wp-admin/includes/file.php');
234 WP_Filesystem();
235 }
236 foreach($attachments as $item) {
237 $newfile = plugin_dir_path(__FILE__).'attachments/'.$subfolder.'/'.basename($item);
238 $wp_filesystem->copy($item,$newfile);
239 array_push($newattachments,$newfile);
240 }
241 $data['attachments'] = maybe_serialize($newattachments);
242 }
243 }
244 $inserted = $wpdb->insert($tableName,$data);
245
246 if ($inserted) {
247 global $wdm_wpma_mailid;
248 $wdm_wpma_mailid = (int) $wpdb->insert_id;
249 }
250
251 if ($status == 'instant') {
252 return null;
253 } else if ( !$inserted ) {
254 // No database entry, email cannot be send
255 return false;
256 } else {
257 // Fake Submit by returning 'True'
258 return true;
259 }
260
261 }
262
263
264
265 // show wp_mail() errors
266 function wdm_wpma_mail_failed( $wp_error ) {
267 global $wpdb,$wdm_wpma_options,$wdm_wpma_mailid;
268 if (isset($wdm_wpma_mailid) && $wdm_wpma_mailid != 0) {
269 $tableName = $wpdb->prefix.$wdm_wpma_options['tableName'];
270 $wpMailFailedError = isset( $wp_error->errors ) && isset( $wp_error->errors['wp_mail_failed'][0] ) ? implode( '; ', $wp_error->errors['wp_mail_failed'] ) : '<em>Unknown</em>';
271 $wpdb->update($tableName,array('timestamp'=>current_time('mysql',false),'status'=>'error', 'info'=>$wpMailFailedError),array('id'=>intval($wdm_wpma_mailid)),array('%s', '%s', '%s'),'%d');
272 }
273 $wdm_wpma_mailid = 0;
274 error_log(print_r($wp_error, true));
275 }
276 add_action('wp_mail_failed','wdm_wpma_mail_failed',10,1);
277
278
279
280 // really send email function to send an email item immediately, without being added to the queue again
281 function wdm_wpma_really_send_mail($item, $args = null) {
282 global $wpdb, $wdm_wpma_options, $wdm_wpma_mailid, $wdm_wpma_pre_wp_mail_priority;
283
284 $currentstatus = isset($item['status']) ? $item['status'] : '';
285 if ($item['recipient'] && $item['recipient'] != '') { $to = maybe_unserialize($item['recipient']); } else { $to = $wdm_wpma_options['email']; $item['subject'] = 'ERROR // '.$item['subject']; }
286 if ($item['headers'] && $item['headers'] != '') { $headers = maybe_unserialize($item['headers']); } else { $headers = ''; }
287 if ($item['attachments'] && $item['attachments'] != '') { $attachments = maybe_unserialize($item['attachments']); } else { $attachments = ''; }
288 $wdm_wpma_mailid = $item['id'];
289
290 if ( !in_array($currentstatus, ['queue', 'high'], /*strict*/true) || !$to ) {
291 return false;
292 }
293
294 remove_filter('pre_wp_mail', 'wdm_wpma_prewpmail', $wdm_wpma_pre_wp_mail_priority);
295 $sendstatus = wp_mail($to,$item['subject'],$item['message'],$headers,$attachments); // Send the email for real
296 add_filter('pre_wp_mail', 'wdm_wpma_prewpmail', $wdm_wpma_pre_wp_mail_priority, 2);
297
298 $wdm_wpma_mailid = 0;
299
300 $tableName = $wpdb->prefix.$wdm_wpma_options['tableName'];
301 if ($sendstatus) {
302 $info = '';
303 if (isset($args['send_mode']) && $args['send_mode']) {
304 $info = json_encode([
305 'send_mode' => $args['send_mode'],
306 ]);
307 } elseif ($currentstatus === 'high') {
308 $info = json_encode([
309 'prio' => 'high',
310 ]);
311 }
312 $wpdb->update($tableName,array('timestamp'=>current_time('mysql',false),'status'=>'sent','info'=>$info),array('id'=>$item['id']),'%s','%d');
313 }
314
315 // remove possible attachments from server after sending email
316 if (is_array($attachments)) {
317 global $wp_filesystem;
318 if ( ! is_a( $wp_filesystem, 'WP_Filesystem_Base') ){
319 include_once(ABSPATH . 'wp-admin/includes/file.php');
320 WP_Filesystem();
321 }
322 $attachmentfolder = pathinfo($attachments[0]);
323 $wp_filesystem->delete($attachmentfolder['dirname'],true,'d');
324 }
325
326 return $sendstatus;
327 }
328
329
330
331
332 /* ***************************************************************
333 CRON
334 **************************************************************** */
335 function wdm_wpma_search_mail_from_queue() {
336 global $wpdb,$wdm_wpma_options, $wdm_wpma_mailid, $wdm_wpma_pre_wp_mail_priority;
337
338 // Only run if plugin is enabled or paused
339 if ( !in_array($wdm_wpma_options['enabled'], ['1', 'paused'], /*strict*/true) ) { return; }
340 $tableName = $wpdb->prefix.$wdm_wpma_options['tableName'];
341
342 // Triggercount to avoid multiple runs
343 $wdm_wpma_options['triggercount']++;
344 if ($wdm_wpma_options['triggercount'] > 1) { return; }
345
346 // Total Mails waiting in the Queue?
347 $mailjobsTotal = $wpdb->get_var( "SELECT COUNT(*) FROM `$tableName` WHERE `status` = 'queue' OR `status` = 'high'" );
348
349 // Mails to send
350 $mailjobs = $wpdb->get_results("SELECT * FROM `$tableName` WHERE `status` = 'queue' OR `status` = 'high' ORDER BY `status` ASC, `id` LIMIT ".intval($wdm_wpma_options['queue_amount']),'ARRAY_A');
351 $mailsInQueue = is_array($mailjobs) ? count($mailjobs) : 0;
352
353 // Maybe alert admin and auto-pause if too many mails in the Queue.
354 if ($wdm_wpma_options['alert_enabled'] == '1' && $mailjobsTotal > intval($wdm_wpma_options['email_amount'])) {
355
356 // Pause sending other emails if option is active and not paused already
357 $must_trigger_auto_pause = wdm_wpma_must_activate_pause_on_alert();
358
359 // Last alerts older than 6 hours?
360 $alert_cutoff = gmdate('Y-m-d H:i:s', current_time('timestamp', false) - (6 * HOUR_IN_SECONDS));
361 $alerts = $wpdb->get_results($wpdb->prepare("SELECT * FROM `$tableName` WHERE `status` = 'alert' AND `timestamp` > %s ORDER BY `id` DESC", $alert_cutoff), 'ARRAY_A');
362
363 // If no recent alert exists, send one;
364 // In case new auto-pause is triggered, always send alert independent of existing recent alerts to inform about the pause
365 if (!$alerts || $must_trigger_auto_pause) {
366
367 $alertMessage = 'Hi,';
368 $alertMessage .= "\n\n";
369 $alertMessage .= 'this is an important message from your WordPress website '.esc_url(get_option('siteurl')).'.';
370 $alertMessage .= "\n";
371 $alertMessage .= "\n".'The Mail Queue Plugin has detected that your website tries to send more emails than expected (currently '.$mailjobsTotal.').';
372 $alertMessage .= "\n".'Please take a close look at the email queue, because it contains more messages than the specified limit.';
373 $alertMessage .= "\n";
374 if ($must_trigger_auto_pause) {
375 $alertMessage .= "\n".'Please note: The email sending has been paused automatically. It will remain paused until you re-enable it manually in the plugin settings.';
376 $alertMessage .= "\n";
377 } elseif ($wdm_wpma_options['enabled'] === 'paused') {
378 if (wdm_wpma_is_pause_on_alert_active()) {
379 $alertMessage .= "\n".'Please note: The email sending has been paused automatically due to a recent alert. It will remain paused until you re-enable it manually in the plugin settings.';
380 $alertMessage .= "\n";
381 } else {
382 $alertMessage .= "\n".'Please note: The email sending is currently paused. If this is not intentional, please check the plugin settings.';
383 $alertMessage .= "\n";
384 }
385 }
386 $alertMessage .= "\n".'In case this is the usual amount of emails, you can adjust the threshold for alerts in the settings of your Mail Queue Plugin.';
387 $alertMessage .= "\n\n";
388 $alertMessage .= "-- ";
389 $alertMessage .= "\n";
390 $alertMessage .= admin_url('admin.php?page=wdm_wpma_mail_queue');
391 $alertSubject = '🔴 WordPress Mail Queue Alert - '.esc_html(get_option('blogname'));
392 $data = array(
393 'timestamp'=> current_time('mysql',false),
394 'recipient'=> sanitize_email($wdm_wpma_options['email']),
395 'subject' => $alertSubject,
396 'message' => $alertMessage,
397 'status' => 'alert',
398 'info' => json_encode([
399 'in_queue' => strval( $mailjobsTotal ),
400 'email_amount' => intval($wdm_wpma_options['email_amount']),
401 'queue_amount' => intval($wdm_wpma_options['queue_amount']),
402 'queue_interval' => intval($wdm_wpma_options['queue_interval']),
403 ]),
404 );
405 $wpdb->insert($tableName,$data);
406 wp_mail($wdm_wpma_options['email'],$alertSubject,$alertMessage);
407 }
408
409 if ($must_trigger_auto_pause) {
410 wdm_wpma_maybe_activate_pause_on_alert();
411 }
412 }
413
414 // Alert might have triggered a pause, so check again before sending emails
415 if ($wdm_wpma_options['enabled'] != '1') { return; }
416
417 // Send Mails in Queue
418 if ($mailsInQueue > 0) {
419 $results = array_slice($mailjobs,0,intval($wdm_wpma_options['queue_amount']));
420 if ($results && count($results) > 0) {
421 foreach($results as $index => $item) {
422 wdm_wpma_really_send_mail($item);
423 }
424 }
425 }
426
427 // Delete old logs
428 $clear_queue_cutoff = gmdate('Y-m-d H:i:s', current_time('timestamp', false) - (intval($wdm_wpma_options['clear_queue']) * HOUR_IN_SECONDS));
429 $wpdb->query($wpdb->prepare("DELETE FROM `$tableName` WHERE `status` != 'queue' AND `status` != 'high' AND `timestamp` < %s", $clear_queue_cutoff));
430
431 }
432 add_action('wp_mail_queue_hook','wdm_wpma_search_mail_from_queue');
433
434 // Custom Cron Interval
435 function wdm_wpma_cron_interval( $schedules ) {
436 global $wdm_wpma_options;
437 $schedules['wdm_wpma_interval'] = array(
438 'interval' => $wdm_wpma_options['queue_interval'],
439 'display' => esc_html__('WP Mail Queue'), );
440 return $schedules;
441 }
442 add_filter('cron_schedules','wdm_wpma_cron_interval');
443
444 // Set or Remove Cron
445 $next_wpma_cron_timestamp = wp_next_scheduled('wp_mail_queue_hook');
446 if ($next_wpma_cron_timestamp && !in_array($wdm_wpma_options['enabled'], ['1', 'paused'], true)) {
447 wp_unschedule_event($next_wpma_cron_timestamp,'wp_mail_queue_hook');
448 } else if (!$next_wpma_cron_timestamp && in_array($wdm_wpma_options['enabled'], ['1', 'paused'], true)) {
449 wp_schedule_event(time(),'wdm_wpma_interval','wp_mail_queue_hook');
450 }
451
452
453
454
455
456 /* ***************************************************************
457 Queue Events
458 **************************************************************** */
459
460 function wdm_wpma_push_queue_event ($args) {
461 global $wpdb, $wdm_wpma_options;
462
463 $tableName = $wpdb->prefix.$wdm_wpma_options['tableName'];
464 $event_name = isset($args['name']) ? $args['name'] : '';
465 $event_data = isset($args['data']) ? $args['data'] : '';
466 if (!$event_name) { return false; }
467
468 $data = array(
469 'timestamp'=> current_time('mysql',false),
470 'status' => 'event',
471 'recipient'=> '',
472 'subject' => $event_name,
473 'message' => '',
474 'info' => $event_data ? (is_string($event_data) ? $event_data : json_encode($event_data)) : '',
475 );
476 $inserted = $wpdb->insert($tableName,$data);
477
478 return $inserted !== false;
479 }
480
481
482
483
484
485 /* ***************************************************************
486 WordPress Password Notification Emails
487 **************************************************************** */
488
489 function wdm_wpma_prioritize_password_reset_mail( $email, $key, $user_login, $user_data ) {
490 global $wdm_wpma_options;
491
492 // If Mail Queue is paused send password reset emails instantly to prevent lockouts.
493 // Otherwise, send with high priority to make sure that password reset emails are sent before other queued emails.
494 $prio_header = isset( $wdm_wpma_options['enabled'] ) && $wdm_wpma_options['enabled'] === 'paused' ? 'X-Mail-Queue-Prio: Instant' : 'X-Mail-Queue-Prio: High';
495
496 if ( empty( $email['headers'] ) ) {
497 $email['headers'] = array( $prio_header );
498 } elseif ( is_array( $email['headers'] ) ) {
499 if ( ! in_array( $prio_header, $email['headers'], true ) ) {
500 $email['headers'][] = $prio_header;
501 }
502 } elseif ( stripos( $email['headers'], $prio_header ) === false ) {
503 $email['headers'] .= ( $email['headers'] ? "\r\n" : '' ) . $prio_header;
504 }
505
506 return $email;
507 }
508 add_filter('retrieve_password_notification_email', 'wdm_wpma_prioritize_password_reset_mail', 10, 4);
509
510
511
512
513
514 /* ***************************************************************
515 Install/Uninstall/Upgrade
516 **************************************************************** */
517
518
519 /* Delete plugin options and database table */
520 function wdm_wpma_uninstall () {
521 global $wpdb;
522
523 delete_option( 'wdm_wpma_settings' );
524 delete_option( 'wdm_wpma_version' );
525 delete_option( 'wdm_wpma_pause_on_alert_active' );
526 // delete_option( 'wdm_wpma_pause_on_alert_expire' );
527
528 $tableName = $wpdb->prefix.'mail_queue';
529 $wpdb->query( "DROP TABLE IF EXISTS $tableName" );
530 }
531
532 /* Delete Cron when Plugin deactivated */
533 function wdm_wpma_deactivate() {
534 wp_clear_scheduled_hook( 'wp_mail_queue_hook' );
535 }
536
537 /* Create/Upgrade MySQL Table on Activation/Upgrade: https://codex.wordpress.org/Creating_Tables_with_Plugins */
538 function wdm_wpma_updateDatabaseTables() {
539 global $wpdb, $wdm_wpma_version;
540
541 $tableName = $wpdb->prefix.'mail_queue';
542
543 $charset_collate = $wpdb->get_charset_collate();
544
545 $sql = "CREATE TABLE $tableName (
546 id mediumint(9) NOT NULL AUTO_INCREMENT,
547 timestamp TIMESTAMP NOT NULL,
548 status varchar(55) DEFAULT '' NOT NULL,
549 recipient varchar(255) DEFAULT '' NOT NULL,
550 subject varchar(255) DEFAULT '' NOT NULL,
551 message mediumtext NOT NULL,
552 headers text NOT NULL,
553 attachments text NOT NULL,
554 info varchar(255) DEFAULT '' NOT NULL,
555 PRIMARY KEY (id)
556 ) $charset_collate;";
557
558 require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
559 dbDelta( $sql );
560
561 update_option( 'wdm_wpma_version', $wdm_wpma_version, /*autoload*/true );
562 }
563
564 /* Update database and register hooks on activation */
565 function wdm_wpma_activate() {
566 wdm_wpma_updateDatabaseTables();
567 register_uninstall_hook( __FILE__, 'wdm_wpma_uninstall' );
568 register_deactivation_hook( __FILE__, 'wdm_wpma_deactivate' );
569 }
570 register_activation_hook( __FILE__, 'wdm_wpma_activate' );
571
572 /* Upgrade routine: check for mismatching version numbers and run database update if necessary */
573 function wdm_wpma_check_update_db () {
574 global $wdm_wpma_version;
575 if ( get_option( 'wdm_wpma_version' ) !== $wdm_wpma_version ) {
576 wdm_wpma_updateDatabaseTables();
577 }
578 }
579 add_action( 'plugins_loaded', 'wdm_wpma_check_update_db', 10, 0 );
580
581
582
583
584 /* ***************************************************************
585 Options Page
586 **************************************************************** */
587 if (is_admin()) {
588 require_once( plugin_dir_path( __FILE__ ) . 'mail-queue-options.php' );
589 }
590
591
592
593
594 /* ***************************************************************
595 REST API
596 **************************************************************** */
597
598
599 function wdm_wpma_add_rest_endpoints () {
600 register_rest_route('wpma/v1', '/message/(?P<id>[\d]+)', array(
601 'methods' => 'GET',
602 'callback' => 'wdm_wpma_rest_get_message',
603 'permission_callback' => function () {
604 return current_user_can( 'manage_options' );
605 },
606 ));
607 }
608 add_action('rest_api_init', 'wdm_wpma_add_rest_endpoints', 10, 0);
609
610
611 function wdm_wpma_rest_get_message ($request) {
612 global $wpdb, $wdm_wpma_options;
613 $tableName = $wpdb->prefix.$wdm_wpma_options['tableName'];
614 $id = intval($request['id']);
615 $row = $wpdb->get_row( $wpdb->prepare("SELECT * FROM `$tableName` WHERE `id` = %d", $id ), ARRAY_A );
616 if ($row) {
617 // Search for content-type header to detect html emails
618 $is_content_type_html = false;
619 $headers = maybe_unserialize( $row['headers'] );
620 if (is_string($headers)) {
621 $headers = [ $headers ];
622 } else if (!is_array($headers)) {
623 $headers = [];
624 }
625 foreach ( $headers as $header ) {
626 if ( preg_match( '/content-type: ?text\/html/i', $header ) ) {
627 $is_content_type_html = true;
628 break;
629 }
630 }
631 return array(
632 'status' => 'ok',
633 'data' => array(
634 'html' => wdm_wpma_render_list_message(maybe_unserialize($row['message']),$is_content_type_html),
635 ),
636 );
637 } else {
638 return new WP_Error( 'no_message', __( 'Message not found' ), array( 'status' => 404 ) );
639 }
640 }
641
642 function wdm_wpma_render_list_message ($message, $is_content_type_html) {
643 // Split html emails into parts and extract plain text preview
644 $parts = explode( '<body', $message );
645 $is_html = $is_content_type_html || count($parts) > 1;
646 if ($is_html) {
647 if (count($parts) > 1) {
648 $header = $parts[0];
649 $body = '<body'.$parts[1];
650 } else {
651 $header = '';
652 $body = $parts[0];
653 }
654 $parts = explode('</body>', $body);
655 if (count($parts) > 1) {
656 $body = $parts[0].'</body>';
657 $footer = $parts[1];
658 } else {
659 $body = $parts[0];
660 $footer = '';
661 }
662 if (!function_exists('convert_html_to_text')) {
663 require_once __DIR__.'/lib/html2text/html2text.php';
664 }
665 // ignore warnings when converting html containing non-converted HTML entities
666 $internal_errors = libxml_use_internal_errors(true);
667 $text = convert_html_to_text( $body );
668 libxml_use_internal_errors($internal_errors);
669 } else {
670 $text = $message;
671 $header = '';
672 $body = '';
673 $footer = '';
674 }
675 $html = '';
676 $html .= '<details class="wdm-wpma-email-source-meta" open><summary>Text</summary><pre class="wdm-wpma-email-plain-text">'.esc_html( $text ).'</pre></details>';
677 $html .= $header ? '<details class="wdm-wpma-email-source-meta"><summary>HTML Header</summary><pre>'.esc_html( wdm_wpma_render_html_for_display($header) ).'</pre></details>' : '';
678 $html .= $body ? '<details class="wdm-wpma-email-source-meta"><summary>HTML Body</summary><pre>'.esc_html( wdm_wpma_render_html_for_display($body) ).'</pre></details>' : '';
679 $html .= $footer ? '<details class="wdm-wpma-email-source-meta"><summary>HTML Footer</summary><pre>'.esc_html( wdm_wpma_render_html_for_display($footer) ).'</pre></details>' : '';
680 return $html;
681 }
682
683 function wdm_wpma_render_html_for_display ($html) {
684 $html = preg_replace( '/;base64,[^"\']+("|\')+/', ';base64, [...] $1', $html );
685 return $html;
686 }
687