PluginProbe
Mail Queue / 1.3
Mail Queue v1.3
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.3, at mail-queue.php

327 lines 13.2 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 outgoing emails, log their sendings and get alerted, if your website wants to send more emails than usual.
7 * Version: 1.3
8 * Requires at least: 5.9
9 * Requires PHP: 7.2
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 DEFAULT SETTINGS
21 **************************************************************** */
22 function wdm_wpma_get_settings() {
23 $defaults = array(
24 'enabled' => '0',
25 'alert_enabled' => '0',
26 'email' => get_option('admin_email'),
27 'email_amount' => '10',
28 'queue_amount' => '1',
29 'queue_interval' => '5',
30 'queue_interval_unit' => 'minutes',
31 'clear_queue' => '14',
32 'tableName' => 'mail_queue',
33 'triggercount' => 0,
34 );
35 $args = get_option('wdm_wpma_settings');
36 $options = wp_parse_args($args,$defaults);
37
38 if ($options['queue_interval_unit'] == 'seconds') {
39 $options['queue_interval'] = intval($options['queue_interval']);
40 if ($options['queue_interval'] < 10) { $options['queue_interval'] = 10; } // Minimum Interval 10 Seconds
41 } else {
42 $options['queue_interval'] = intval($options['queue_interval']) * 60;
43 }
44
45 $options['clear_queue'] = intval($options['clear_queue']) * 24;
46 return $options;
47 }
48
49
50
51
52
53 /* ***************************************************************
54 Overwrite wp_mail() if Plugin enabled and no Cron is running
55 **************************************************************** */
56 $wdm_wpma_mailid = 0;
57 $wdm_wpma_options = wdm_wpma_get_settings(); // Get Settings
58
59 if ($wdm_wpma_options['enabled'] == '1' && wp_doing_cron() == false) {
60 add_filter( 'pre_wp_mail' , 'wdm_wpma_prewpmail', 10, 2);
61 }
62
63 // pre WP Mail Filter
64 function wdm_wpma_prewpmail($null, $atts) {
65
66 global $wpdb, $wdm_wpma_options;
67
68 // Mail Variables
69 $to = $atts['to'];
70 $subject = $atts['subject'];
71 $message = $atts['message'];
72 $headers = $atts['headers'];
73 $attachments = $atts['attachments'];
74 $status = 'queue';
75
76 // Instant Sending or Prio Mail?
77 if (isset($headers) && is_array($headers) && count($headers) > 0) {
78 foreach($headers as $index => $val) {
79 if (preg_match("#^X-Mail-Queue-Prio: +Instant *$#i",$val)) {
80 array_splice($headers,$index,1);
81 $status = 'instant';
82 break;
83 } else if (preg_match("#^X-Mail-Queue-Prio: +High *$#i",$val)) {
84 array_splice($headers,$index,1);
85 $status = 'high';
86 break;
87 }
88 }
89 }
90
91 // Write email in Queue
92 $tableName = $wpdb->prefix.$wdm_wpma_options['tableName'];
93 $data = array(
94 'timestamp'=> current_time('mysql',false),
95 'recipient'=> maybe_serialize($to),
96 'subject'=> $subject,
97 'message'=> $message,
98 'status' => $status,
99 'attachments' => ''
100 );
101 if (isset($headers) && $headers) { $data['headers'] = maybe_serialize($headers); }
102
103 // store attachments in /attachments/ Folder, to address them later
104 if (isset($attachments) && $attachments && $attachments != '') {
105
106 $subfolder = time().'-'.rand(0,999999);
107 $foldercreated = wp_mkdir_p(plugin_dir_path(__FILE__).'attachments/'.$subfolder);
108 if (!$foldercreated) {
109 error_log('Could not create Subfolder for Email attachment');
110 $data['info'] = 'Error: Could not store attachments';
111 } else {
112 if (!is_array($attachments)) { $attachments = array($attachments); }
113 $newattachments = array();
114 global $wp_filesystem;
115 if ( ! is_a( $wp_filesystem, 'WP_Filesystem_Base') ){
116 include_once(ABSPATH . 'wp-admin/includes/file.php');
117 WP_Filesystem();
118 }
119 foreach($attachments as $item) {
120 $newfile = plugin_dir_path(__FILE__).'attachments/'.$subfolder.'/'.basename($item);
121 $wp_filesystem->copy($item,$newfile);
122 array_push($newattachments,$newfile);
123 }
124 $data['attachments'] = maybe_serialize($newattachments);
125 }
126 }
127 $wpdb->insert($tableName,$data);
128
129 if ($status == 'instant') {
130 return null;
131 } else {
132 // Fake Submit by returning 'True'
133 return true;
134 }
135
136 }
137
138
139
140 // show wp_mail() errors
141 function wdm_wpma_mail_failed( $wp_error ) {
142 global $wpdb,$wdm_wpma_options,$wdm_wpma_mailid;
143 if (isset($wdm_wpma_mailid) && $wdm_wpma_mailid != 0) {
144 $tableName = $wpdb->prefix.$wdm_wpma_options['tableName'];
145 $wpMailFailedError = isset( $wp_error->errors ) && isset( $wp_error->errors['wp_mail_failed'][0] ) ? implode( '; ', $wp_error->errors['wp_mail_failed'] ) : '<em>Unknown</em>';
146 $wpdb->update($tableName,array('timestamp'=>current_time('mysql',false),'status'=>'error', 'info'=>$wpMailFailedError),array('id'=>intval($wdm_wpma_mailid)),array('%s', '%s', '%s'),'%d');
147 }
148 return error_log(print_r($wp_error, true));
149 }
150 add_action('wp_mail_failed','wdm_wpma_mail_failed',10,1);
151
152
153
154
155 /* ***************************************************************
156 CRON
157 **************************************************************** */
158 function wdm_wpma_search_mail_from_queue() {
159
160 global $wpdb,$wdm_wpma_options,$wdm_wpma_mailid;
161 if ($wdm_wpma_options['enabled'] != '1') { return; }
162 $tableName = $wpdb->prefix.$wdm_wpma_options['tableName'];
163
164 // Triggercount to avoid multiple runs
165 $wdm_wpma_options['triggercount']++;
166 if ($wdm_wpma_options['triggercount'] > 1) { return; }
167
168 // Mails in Queue?
169 $mailjobs = $wpdb->get_results("SELECT * FROM `$tableName` WHERE `status` = 'queue' ORDER BY `id`",'ARRAY_A');
170 $mailsInQueue = is_array($mailjobs) ? count($mailjobs) : 0;
171
172 // Alert Admin, if too many mails in the Queue.
173 if ($wdm_wpma_options['alert_enabled'] == '1' && $mailsInQueue > intval($wdm_wpma_options['email_amount'])) {
174
175 // Last alerts older than 6 hours?
176 $alerts = $wpdb->get_results("SELECT * FROM `$tableName` WHERE `status` = 'alert' AND `timestamp` > NOW() - INTERVAL 6 HOUR",'ARRAY_A');
177
178 // If no alerts, then send one
179 if (!$alerts) {
180 $alertMessage = 'Hi,';
181 $alertMessage .= "\n\n";
182 $alertMessage .= 'this is an important message from your WordPress website '.esc_url(get_option('siteurl')).'.';
183 $alertMessage .= "\n";
184 $alertMessage .= "\n".'The Mail Queue Plugin has detected that your website tries to send more emails than expected (currently '.$mailsInQueue.').';
185 $alertMessage .= "\n".'Please take a close look at the email queue, because it contains more messages than the specified limit.';
186 $alertMessage .= "\n";
187 $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.';
188 $alertMessage .= "\n\n";
189 $alertMessage .= "-- ";
190 $alertMessage .= "\n";
191 $alertMessage .= admin_url();
192 $alertSubject = '🔴 WordPress Mail Queue Alert - '.esc_html(get_option('blogname'));
193 $data = array(
194 'timestamp'=> current_time('mysql',false),
195 'recipient'=> sanitize_email($wdm_wpma_options['email']),
196 'subject' => $alertSubject,
197 'message' => $alertMessage,
198 'status' => 'alert',
199 'info' => json_encode([
200 'in_queue' => strval( $mailsInQueue ),
201 'email_amount' => intval($wdm_wpma_options['email_amount']),
202 'queue_amount' => intval($wdm_wpma_options['queue_amount']),
203 'queue_interval' => intval($wdm_wpma_options['queue_interval']),
204 ]),
205 );
206 $wpdb->insert($tableName,$data);
207 wp_mail($wdm_wpma_options['email'],$alertSubject,$alertMessage);
208 }
209
210 }
211
212 // Send Mails in Queue
213 if ($mailsInQueue > 0) {
214 $results = array_slice($mailjobs,0,intval($wdm_wpma_options['queue_amount']));
215 if ($results && count($results) > 0) {
216 foreach($results as $index => $item) {
217 if ($item['recipient'] && $item['recipient'] != '') { $to = maybe_unserialize($item['recipient']); } else { $to = $wdm_wpma_options['email']; $item['subject'] = 'ERROR // '.$item['subject']; }
218 if ($item['headers'] && $item['headers'] != '') { $headers = maybe_unserialize($item['headers']); } else { $headers = ''; }
219 if ($item['attachments'] && $item['attachments'] != '') { $attachments = maybe_unserialize($item['attachments']); } else { $attachments = ''; }
220 $wdm_wpma_mailid = $item['id'];
221
222 //$item['subject'] .= ' // Timestamp '.time().' // BlogID '.$wpdb->blogid.' // Cron '.wp_doing_cron().' // Resultscount '.count($results).' // Index '.$index;
223
224 remove_filter('pre_wp_mail','wdm_wpma_prewpmail',10);
225 $sendstatus = wp_mail($to,$item['subject'],$item['message'],$headers,$attachments); // Finally sends the email for real
226 if ($sendstatus) {
227 $wpdb->update($tableName,array('timestamp'=>current_time('mysql',false),'status'=>'sent'),array('id'=>$item['id']),'%s','%d');
228 }
229 if (is_array($attachments)) {
230 global $wp_filesystem;
231 if ( ! is_a( $wp_filesystem, 'WP_Filesystem_Base') ){
232 include_once(ABSPATH . 'wp-admin/includes/file.php');
233 WP_Filesystem();
234 }
235 $attachmentfolder = pathinfo($attachments[0]);
236 $wp_filesystem->delete($attachmentfolder['dirname'],true,'d');
237 }
238 }
239 }
240 }
241
242 // Delete old logs
243 $wpdb->query("DELETE FROM `$tableName` WHERE `status` != 'queue' AND `timestamp` < NOW() - INTERVAL ".esc_sql($wdm_wpma_options['clear_queue'])." HOUR");
244
245 }
246 add_action('wp_mail_queue_hook','wdm_wpma_search_mail_from_queue');
247
248 // Custom Cron Interval
249 function wdm_wpma_cron_interval( $schedules ) {
250 global $wdm_wpma_options;
251 $schedules['wdm_wpma_interval'] = array(
252 'interval' => $wdm_wpma_options['queue_interval'],
253 'display' => esc_html__('WP Mail Queue'), );
254 return $schedules;
255 }
256 add_filter('cron_schedules','wdm_wpma_cron_interval');
257
258 // Set or Remove Cron
259 $next_wpma_cron_timestamp = wp_next_scheduled('wp_mail_queue_hook');
260 if ($next_wpma_cron_timestamp && $wdm_wpma_options['enabled'] != '1') {
261 wp_unschedule_event($next_wpma_cron_timestamp,'wp_mail_queue_hook');
262 } else if (!$next_wpma_cron_timestamp && $wdm_wpma_options['enabled'] == '1') {
263 wp_schedule_event(time(),'wdm_wpma_interval','wp_mail_queue_hook');
264 }
265
266
267
268 /* ***************************************************************
269 Install/Uninstall
270 **************************************************************** */
271
272
273 /* Delete plugin options and database table */
274 function wdm_wpma_uninstall () {
275 global $wpdb;
276
277 $optionName = 'wdm_wpma_settings';
278 delete_option( $optionName );
279
280 $tableName = $wpdb->prefix.'mail_queue';
281 $wpdb->query( "DROP TABLE IF EXISTS $tableName" );
282 }
283
284 /* Delete Cron when Plugin deactivated */
285 function wdm_wpma_deactivate() {
286 wp_clear_scheduled_hook( 'wp_mail_queue_hook' );
287 }
288
289 /* Create MySQL Table on Activation: https://codex.wordpress.org/Creating_Tables_with_Plugins */
290 function wdm_wpma_installDatabaseTables() {
291 global $wpdb;
292
293 $tableName = $wpdb->prefix.'mail_queue';
294
295 $charset_collate = $wpdb->get_charset_collate();
296
297 $sql = "CREATE TABLE $tableName (
298 id mediumint(9) NOT NULL AUTO_INCREMENT,
299 timestamp TIMESTAMP NOT NULL,
300 status varchar(55) DEFAULT '' NOT NULL,
301 recipient varchar(255) DEFAULT '' NOT NULL,
302 subject varchar(255) DEFAULT '' NOT NULL,
303 message text NOT NULL,
304 headers text NOT NULL,
305 attachments text NOT NULL,
306 info varchar(255) DEFAULT '' NOT NULL,
307 PRIMARY KEY (id)
308 ) $charset_collate;";
309
310 require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
311 dbDelta( $sql );
312
313 register_uninstall_hook( __FILE__, 'wdm_wpma_uninstall' );
314
315 register_deactivation_hook( __FILE__, 'wdm_wpma_deactivate' );
316
317 }
318 register_activation_hook( __FILE__, 'wdm_wpma_installDatabaseTables' );
319
320
321 /* ***************************************************************
322 Options Page
323 **************************************************************** */
324 if (is_admin()) {
325 require_once( plugin_dir_path( __FILE__ ) . 'mail-queue-options.php' );
326 }
327