Post SMTP Mailer/Email Log - Version 2.0.7

Version Description

  • 2020-01-12
  • Updated: Improve PHPMailer method.
  • Updated: Bug fixes.
Download this release

Release Info

Developer yehudah
Plugin Icon 128x128 Post SMTP Mailer/Email Log
Version 2.0.7
Comparing to
See all releases

Code changes from version 2.0.6 to 2.0.7

Postman/Extensions/Admin/PostmanAdmin.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( ! defined( 'ABSPATH' ) ) exit;
3
+
4
+ class PostmanAdmin {
5
+
6
+ public function __construct()
7
+ {
8
+ $PostmanLicenseManager = PostmanLicenseManager::get_instance();
9
+ $extensions = $PostmanLicenseManager->get_extensions();
10
+
11
+ if ( count( $extensions ) > 0 ) {
12
+ add_action('admin_menu', [ $this, 'add_menu' ], 20 );
13
+ }
14
+
15
+ }
16
+
17
+ public function add_menu() {
18
+ add_submenu_page(
19
+ PostmanViewController::POSTMAN_MENU_SLUG,
20
+ __('Extensions', 'post-smtp'),
21
+ __('Extensions', 'post-smtp'),
22
+ 'manage_options',
23
+ 'post-smtp-extensions',
24
+ [ $this, 'render_menu' ]
25
+ );
26
+ }
27
+
28
+ public function render_menu() {
29
+ include_once 'PostmanAdminView.php';
30
+ }
31
+ }
Postman/Extensions/Admin/PostmanAdminView.php ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if ( ! defined( 'ABSPATH' ) ) exit; ?>
2
+
3
+ <style>
4
+ .form-table .row {
5
+ display: flex;
6
+ }
7
+
8
+ .form-table .row .flex > *:not(:last-child) {
9
+ margin-right: 5px;
10
+ }
11
+
12
+ .form-table .label {
13
+ align-self: center;
14
+ font-weight: bold;
15
+ }
16
+
17
+ .form-table .flex {
18
+ display: flex;
19
+ }
20
+
21
+ .form-table .flex input {
22
+ border-radius: 3px;
23
+ height: 30px;
24
+ margin: 0;
25
+ margin-left: 5px;
26
+ }
27
+
28
+ .form-table .flex button {
29
+ box-shadow: none;
30
+ height: 100%;
31
+ }
32
+ </style>
33
+
34
+ <div class="wrap">
35
+ <h1>Post SMTP Installed Extensions</h1>
36
+ <form action="" method="post">
37
+ <div class="form-table">
38
+ <?php
39
+ $PostmanLicenseManager = PostmanLicenseManager::get_instance();
40
+ $extensions = $PostmanLicenseManager->get_extensions();
41
+
42
+ foreach ( $extensions as $slug => $extension) :
43
+ $short_name = $extension['license_manager']->get_slug( $extension['plugin_data']['Name'] );
44
+ $nonce = $short_name . '_license_key-nonce';
45
+
46
+ $license_data = get_option( $short_name . '_license_active' );
47
+ $license_key = get_option( $short_name . '_license_key' );
48
+
49
+ $license_valid = is_object( $license_data ) && $license_data->license === 'valid';
50
+ $license_field_class = $license_valid ? 'readonly' : '';
51
+ $license_field_value = $license_valid ? base64_encode($license_key) : '';
52
+
53
+ wp_nonce_field( $nonce, $nonce );
54
+ ?>
55
+
56
+ <div class="row">
57
+ <div class="label">
58
+ <?php echo esc_html( $extension['plugin_data']['Name'] ); ?>
59
+ </div>
60
+
61
+ <div class="flex">
62
+ <div class="input">
63
+ <input <?php echo $license_field_class; ?>
64
+ type="password"
65
+ name="post_smtp_extension[<?php echo $short_name . '_license_key'; ?>]"
66
+ class="regular-text"
67
+ value="<?php echo $license_field_value; ?>"
68
+ placeholder="Serial Key">
69
+ </div>
70
+
71
+ <div class="buttons">
72
+ <?php if ( ! $license_valid ) :?>
73
+ <button type="submit" name="post_smtp_extension[<?php echo $short_name; ?>_activate]" class="button button-primary">Activate</button>
74
+ <?php endif; ?>
75
+
76
+ <button type="submit" name="post_smtp_extension[<?php echo $short_name; ?>_deactivate]" class="button button-secondary">Deactivate</button>
77
+ </div>
78
+ </div>
79
+
80
+ </div>
81
+
82
+ <?php endforeach; ?>
83
+
84
+ </div>
85
+ </form>
86
+ </div>
Postman/{notifications → Extensions/Core/Notifications}/INotify.php RENAMED
File without changes
Postman/{notifications → Extensions/Core/Notifications}/PostmanMailNotify.php RENAMED
File without changes
Postman/Extensions/Core/Notifications/PostmanNotify.php ADDED
@@ -0,0 +1,241 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( ! defined( 'ABSPATH' ) ) {
3
+ exit; // Exit if accessed directly
4
+ }
5
+ require_once 'INotify.php';
6
+ require_once 'PostmanMailNotify.php';
7
+ require_once 'PostmanPushoverNotify.php';
8
+ require_once 'PostmanSlackNotify.php';
9
+ require_once 'PostmanNotifyOptions.php';
10
+
11
+ class PostmanNotify {
12
+
13
+ const NOTIFICATIONS_OPTIONS = 'postman_notifications_options';
14
+ const NOTIFICATIONS_SECTION = 'postman_notifications_section';
15
+ const NOTIFICATIONS_PUSHOVER_CRED = 'postman_pushover_cred';
16
+ const NOTIFICATIONS_SLACK_CRED = 'postman_slack_cred';
17
+
18
+ public function __construct() {
19
+
20
+ $this->options = new PostmanNotifyOptions();
21
+
22
+ add_filter( 'post_smtp_admin_tabs', array( $this, 'tabs' ) );
23
+ add_action( 'post_smtp_settings_menu', array( $this, 'menu' ) );
24
+ add_action( 'post_smtp_settings_fields', array( $this, 'settings' ) );
25
+ add_action( 'post_smtp_on_failed', array( $this, 'notify' ), 10, 5 );
26
+ add_filter( 'post_smtp_sanitize', array( $this, 'sanitize' ), 10, 3 );
27
+ }
28
+
29
+ public function menu() {
30
+ print '<section id="notifications">';
31
+ do_settings_sections( self::NOTIFICATIONS_OPTIONS );
32
+
33
+ $currentKey = $this->options->getNotificationService();
34
+ $pushover = $currentKey == 'pushover' ? 'block' : 'none';
35
+ $slack = $currentKey == 'slack' ? 'block' : 'none';
36
+
37
+ echo '<div id="pushover_cred" style="display: ' . $pushover . ';">';
38
+ do_settings_sections( self::NOTIFICATIONS_PUSHOVER_CRED );
39
+ echo '</div>';
40
+
41
+ echo '<div id="slack_cred" style="display: ' . $slack . ';">';
42
+ do_settings_sections( self::NOTIFICATIONS_SLACK_CRED );
43
+ echo '</div>';
44
+
45
+ do_action( 'post_smtp_notification_settings' );
46
+
47
+ print '</section>';
48
+ }
49
+
50
+ public function sanitize($new_input, $input, $sanitizer) {
51
+ // Notifications
52
+ $sanitizer->sanitizeString( 'Pushover Service', PostmanNotifyOptions::NOTIFICATION_SERVICE, $input, $new_input, $this->options->getNotificationService() );
53
+ $sanitizer->sanitizePassword( 'Pushover Username', PostmanNotifyOptions::PUSHOVER_USER, $input, $new_input, $this->options->getPushoverUser() );
54
+ $sanitizer->sanitizePassword( 'Pushover Token', PostmanNotifyOptions::PUSHOVER_TOKEN, $input, $new_input, $this->options->getPushoverToken() );
55
+ $sanitizer->sanitizePassword( 'Slack Token', PostmanNotifyOptions::SLACK_TOKEN, $input, $new_input, $this->options->getSlackToken() );
56
+
57
+ // Chrome extension
58
+ $sanitizer->sanitizeString( 'Push Chrome Extension', PostmanNotifyOptions::NOTIFICATION_USE_CHROME, $input, $new_input );
59
+ $sanitizer->sanitizePassword( 'Push Chrome Extension UID', PostmanNotifyOptions::NOTIFICATION_CHROME_UID, $input, $new_input, $this->options->getNotificationChromeUid() );
60
+
61
+ return $new_input;
62
+ }
63
+
64
+ public function tabs($tabs) {
65
+ $tabs['notifications'] = __( 'Notifications', 'post-smtp' );
66
+
67
+ return $tabs;
68
+ }
69
+
70
+ public function settings() {
71
+ // Notifications
72
+ add_settings_section( self::NOTIFICATIONS_SECTION, _x( 'Notifications Settings', 'Configuration Section Title', 'post-smtp' ), array(
73
+ $this,
74
+ 'printNotificationsSectionInfo',
75
+ ), self::NOTIFICATIONS_OPTIONS );
76
+
77
+ add_settings_field( PostmanNotifyOptions::NOTIFICATION_SERVICE, _x( 'Notification Service', 'Configuration Input Field', 'post-smtp' ), array(
78
+ $this,
79
+ 'notification_service_callback',
80
+ ), self::NOTIFICATIONS_OPTIONS, self::NOTIFICATIONS_SECTION );
81
+
82
+ // Pushover
83
+ add_settings_section( 'pushover_credentials', _x( 'Pushover Credentials', 'Configuration Section Title', 'post-smtp' ), array(
84
+ $this,
85
+ 'printNotificationsSectionInfo',
86
+ ), self::NOTIFICATIONS_PUSHOVER_CRED );
87
+
88
+ add_settings_field( PostmanNotifyOptions::PUSHOVER_USER, _x( 'Pushover User Key', 'Configuration Input Field', 'post-smtp' ), array(
89
+ $this,
90
+ 'pushover_user_callback',
91
+ ), self::NOTIFICATIONS_PUSHOVER_CRED, 'pushover_credentials' );
92
+
93
+ add_settings_field( PostmanNotifyOptions::PUSHOVER_TOKEN, _x( 'Pushover App Token', 'Configuration Input Field', 'post-smtp' ), array(
94
+ $this,
95
+ 'pushover_token_callback',
96
+ ), self::NOTIFICATIONS_PUSHOVER_CRED, 'pushover_credentials' );
97
+
98
+ // Slack
99
+ add_settings_section( 'slack_credentials', _x( 'Slack Credentials', 'Configuration Section Title', 'post-smtp' ), array(
100
+ $this,
101
+ 'printNotificationsSectionInfo',
102
+ ), self::NOTIFICATIONS_SLACK_CRED );
103
+
104
+ add_settings_field( PostmanNotifyOptions::SLACK_TOKEN, _x( 'Slack Webhook', 'Configuration Input Field', 'post-smtp' ), array(
105
+ $this,
106
+ 'slack_token_callback',
107
+ ), self::NOTIFICATIONS_SLACK_CRED, 'slack_credentials' );
108
+
109
+ add_settings_field( PostmanNotifyOptions::NOTIFICATION_USE_CHROME, _x( 'Push to chrome extension', 'Configuration Input Field', 'post-smtp' ), array(
110
+ $this,
111
+ 'notification_use_chrome_callback',
112
+ ), self::NOTIFICATIONS_OPTIONS, self::NOTIFICATIONS_SECTION );
113
+
114
+ add_settings_field( 'notification_chrome_uid', _x( 'Chrome Extension UID', 'Configuration Input Field', 'post-smtp' ), array(
115
+ $this,
116
+ 'notification_chrome_uid_callback',
117
+ ), self::NOTIFICATIONS_OPTIONS, self::NOTIFICATIONS_SECTION );
118
+ }
119
+
120
+ /**
121
+ * Print the Section text
122
+ */
123
+ public function printNotificationsSectionInfo() {
124
+ }
125
+
126
+ public function notification_service_callback() {
127
+ $inputDescription = __( 'Select the notification service you want to recieve alerts about failed emails.' );
128
+
129
+ $options = apply_filters('post_smtp_notification_service', array(
130
+ 'none' => __( 'None', 'post-smtp' ),
131
+ 'default' => __( 'WP Admin Email', 'post-smtp' ),
132
+ 'pushover' => __( 'Pushover', 'post-smtp' ),
133
+ 'slack' => __( 'Slack', 'post-smtp' ),
134
+ ));
135
+
136
+ printf( '<select id="input_%2$s" class="input_%2$s" name="%1$s[%2$s]">', 'postman_options', PostmanNotifyOptions::NOTIFICATION_SERVICE );
137
+ $currentKey = $this->options->getNotificationService();
138
+
139
+ foreach ( $options as $key => $label ) {
140
+ $this->printSelectOption( $label, $key, $currentKey );
141
+ }
142
+
143
+ printf( '</select><br/><span class="postman_input_description">%s</span>', $inputDescription );
144
+ }
145
+
146
+ public function notification_use_chrome_callback() {
147
+ $value = $this->options->useChromeExtension();
148
+ printf( '<input type="checkbox" id="input_%2$s" class="input_%2$s" name="%1$s[%2$s]" %3$s />', 'postman_options', PostmanNotifyOptions::NOTIFICATION_USE_CHROME, $value ? 'checked="checked"' : '' );
149
+ }
150
+
151
+ public function notification_chrome_uid_callback() {
152
+ printf( '<input type="password" id="input_%2$s" class="input_%2$s" name="%1$s[%2$s]" value="%3$s" />', 'postman_options', 'notification_chrome_uid', PostmanUtils::obfuscatePassword( $this->options->getNotificationChromeUid() ) );
153
+ }
154
+
155
+ public function pushover_user_callback() {
156
+ printf( '<input type="password" id="pushover_user" name="%s[%s]" value="%s" />', 'postman_options', PostmanNotifyOptions::PUSHOVER_USER, $this->options->getPushoverUser() );
157
+ }
158
+
159
+ public function pushover_token_callback() {
160
+ printf( '<input type="password" id="pushover_token" name="%s[%s]" value="%s" />', 'postman_options', PostmanNotifyOptions::PUSHOVER_TOKEN, $this->options->getPushoverToken() );
161
+ }
162
+
163
+ public function slack_token_callback() {
164
+ printf( '<input type="password" id="slack_token" name="%s[%s]" value="%s" />', 'postman_options', PostmanNotifyOptions::SLACK_TOKEN, $this->options->getSlackToken() );
165
+ echo '<a target="_blank" href="https://slack.postmansmtp.com/">' . __( 'Get your webhook URL here', 'post-smtp' ) . '</a>';
166
+
167
+ }
168
+
169
+ /**
170
+ * @param PostmanEmailLog $log
171
+ * @param PostmanMessage $message
172
+ * @param string $transcript
173
+ * @param PostmanTransport $transport
174
+ * @param string $errorMessage
175
+ */
176
+ public function notify ($log, $postmanMessage, $transcript, $transport, $errorMessage ) {
177
+ $message = __( 'You getting this message because an error detected while delivered your email.', 'post-smtp' );
178
+ $message .= "\r\n" . sprintf( __( 'For the domain: %1$s','post-smtp' ), get_bloginfo('url') );
179
+ $message .= "\r\n" . __( 'The log to paste when you open a support issue:', 'post-smtp' ) . "\r\n";
180
+
181
+ if ( $errorMessage && ! empty( $errorMessage ) ) {
182
+
183
+ $message = $message . $errorMessage;
184
+
185
+ $notification_service = PostmanNotifyOptions::getInstance()->getNotificationService();
186
+ switch ($notification_service) {
187
+ case 'none':
188
+ $notifyer = false;
189
+ break;
190
+ case 'default':
191
+ $notifyer = new PostmanMailNotify;
192
+ break;
193
+ case 'pushover':
194
+ $notifyer = new PostmanPushoverNotify;
195
+ break;
196
+ case 'slack':
197
+ $notifyer = new PostmanSlackNotify;
198
+ break;
199
+ default:
200
+ $notifyer = new PostmanMailNotify;
201
+ }
202
+
203
+ $notifyer = apply_filters('post_smtp_notifier', $notifyer, $notification_service);
204
+
205
+ // Notifications
206
+ if ( $notifyer ) {
207
+ $notifyer->send_message($message, $log);
208
+ }
209
+
210
+ $this->push_to_chrome($errorMessage);
211
+ }
212
+ }
213
+
214
+ public function push_to_chrome($message) {
215
+ $push_chrome = PostmanNotifyOptions::getInstance()->useChromeExtension();
216
+
217
+ if ( $push_chrome ) {
218
+ $uid = PostmanNotifyOptions::getInstance()->getNotificationChromeUid();
219
+
220
+ if ( empty( $uid ) ) {
221
+ return;
222
+ }
223
+
224
+ $url = 'https://postmansmtp.com/chrome/' . $uid;
225
+
226
+ $args = array(
227
+ 'body' => array(
228
+ 'message' => $message
229
+ )
230
+ );
231
+
232
+ $response = wp_remote_post( $url , $args );
233
+ }
234
+ }
235
+
236
+ private function printSelectOption( $label, $optionKey, $currentKey ) {
237
+ $optionPattern = '<option value="%1$s" %2$s>%3$s</option>';
238
+ printf( $optionPattern, $optionKey, $optionKey == $currentKey ? 'selected="selected"' : '', $label );
239
+ }
240
+ }
241
+ new PostmanNotify();
Postman/Extensions/Core/Notifications/PostmanNotifyOptions.php ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class PostmanNotifyOptions {
4
+
5
+ const DEFAULT_NOTIFICATION_SERVICE = 'default';
6
+ const NOTIFICATION_SERVICE = 'notification_service';
7
+ const NOTIFICATION_USE_CHROME = 'notification_use_chrome';
8
+ const NOTIFICATION_CHROME_UID = 'notification_chrome_uid';
9
+ const PUSHOVER_USER = 'pushover_user';
10
+ const PUSHOVER_TOKEN = 'pushover_token';
11
+ const SLACK_TOKEN = 'slack_token';
12
+
13
+ private $options;
14
+
15
+ public function __construct()
16
+ {
17
+ $this->options = get_option( 'postman_options' );
18
+ }
19
+
20
+ public function getNotificationService() {
21
+ if ( isset( $this->options [ self::NOTIFICATION_SERVICE ] ) ) {
22
+ return $this->options [ self::NOTIFICATION_SERVICE ];
23
+ } else {
24
+ return self::DEFAULT_NOTIFICATION_SERVICE;
25
+ }
26
+ }
27
+
28
+ public function getPushoverUser() {
29
+ if ( isset( $this->options [ self::PUSHOVER_USER ] ) ) {
30
+ return base64_decode( $this->options [ self::PUSHOVER_USER ] );
31
+ }
32
+ }
33
+
34
+ public function getPushoverToken() {
35
+ if ( isset( $this->options [ self::PUSHOVER_TOKEN ] ) ) {
36
+ return base64_decode( $this->options [ self::PUSHOVER_TOKEN ] );
37
+ }
38
+ }
39
+
40
+ public function getSlackToken() {
41
+ if ( isset( $this->options [ self::SLACK_TOKEN ] ) ) {
42
+ return base64_decode( $this->options [ self::SLACK_TOKEN ] );
43
+ }
44
+ }
45
+
46
+ public function useChromeExtension() {
47
+ if ( isset( $this->options [ self::NOTIFICATION_USE_CHROME ] ) ) {
48
+ return $this->options [ self::NOTIFICATION_USE_CHROME ];
49
+ }
50
+ }
51
+
52
+ public function getNotificationChromeUid() {
53
+ if ( isset( $this->options [ self::NOTIFICATION_CHROME_UID ] ) ) {
54
+ return base64_decode( $this->options [ self::NOTIFICATION_CHROME_UID ] );
55
+ }
56
+ }
57
+ }
Postman/{notifications → Extensions/Core/Notifications}/PostmanPushoverNotify.php RENAMED
File without changes
Postman/{notifications → Extensions/Core/Notifications}/PostmanSlackNotify.php RENAMED
File without changes
Postman/Extensions/License/EDD_SL_Plugin_Updater.php ADDED
@@ -0,0 +1,585 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+ /**
7
+ * Allows plugins to use their own update API.
8
+ *
9
+ * @author Easy Digital Downloads
10
+ * @version 1.6.19
11
+ */
12
+ class EDD_SL_Plugin_Updater {
13
+
14
+ private $api_url = '';
15
+ private $api_data = array();
16
+ private $name = '';
17
+ private $slug = '';
18
+ private $version = '';
19
+ private $wp_override = false;
20
+ private $cache_key = '';
21
+
22
+ private $health_check_timeout = 5;
23
+
24
+ /**
25
+ * Class constructor.
26
+ *
27
+ * @uses plugin_basename()
28
+ * @uses hook()
29
+ *
30
+ * @param string $_api_url The URL pointing to the custom API endpoint.
31
+ * @param string $_plugin_file Path to the plugin file.
32
+ * @param array $_api_data Optional data to send with API calls.
33
+ */
34
+ public function __construct( $_api_url, $_plugin_file, $_api_data = null ) {
35
+
36
+ global $edd_plugin_data;
37
+
38
+ $this->api_url = trailingslashit( $_api_url );
39
+ $this->api_data = $_api_data;
40
+ $this->name = plugin_basename( $_plugin_file );
41
+ $this->slug = basename( $_plugin_file, '.php' );
42
+ $this->version = $_api_data['version'];
43
+ $this->wp_override = isset( $_api_data['wp_override'] ) ? (bool) $_api_data['wp_override'] : false;
44
+ $this->beta = ! empty( $this->api_data['beta'] ) ? true : false;
45
+ $this->cache_key = 'edd_sl_' . md5( serialize( $this->slug . $this->api_data['license'] . $this->beta ) );
46
+
47
+ $edd_plugin_data[ $this->slug ] = $this->api_data;
48
+
49
+ /**
50
+ * Fires after the $edd_plugin_data is setup.
51
+ *
52
+ * @since x.x.x
53
+ *
54
+ * @param array $edd_plugin_data Array of EDD SL plugin data.
55
+ */
56
+ do_action( 'post_edd_sl_plugin_updater_setup', $edd_plugin_data );
57
+
58
+ // Set up hooks.
59
+ $this->init();
60
+
61
+ }
62
+
63
+ /**
64
+ * Set up WordPress filters to hook into WP's update process.
65
+ *
66
+ * @uses add_filter()
67
+ *
68
+ * @return void
69
+ */
70
+ public function init() {
71
+
72
+ add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ) );
73
+ add_filter( 'plugins_api', array( $this, 'plugins_api_filter' ), 10, 3 );
74
+ remove_action( 'after_plugin_row_' . $this->name, 'wp_plugin_update_row', 10 );
75
+ add_action( 'after_plugin_row_' . $this->name, array( $this, 'show_update_notification' ), 10, 2 );
76
+ add_action( 'admin_init', array( $this, 'show_changelog' ) );
77
+
78
+ }
79
+
80
+ /**
81
+ * Check for Updates at the defined API endpoint and modify the update array.
82
+ *
83
+ * This function dives into the update API just when WordPress creates its update array,
84
+ * then adds a custom API call and injects the custom plugin data retrieved from the API.
85
+ * It is reassembled from parts of the native WordPress plugin update code.
86
+ * See wp-includes/update.php line 121 for the original wp_update_plugins() function.
87
+ *
88
+ * @uses api_request()
89
+ *
90
+ * @param array $_transient_data Update array build by WordPress.
91
+ * @return array Modified update array with custom plugin data.
92
+ */
93
+ public function check_update( $_transient_data ) {
94
+
95
+ global $pagenow;
96
+
97
+ if ( ! is_object( $_transient_data ) ) {
98
+ $_transient_data = new stdClass;
99
+ }
100
+
101
+ if ( 'plugins.php' == $pagenow && is_multisite() ) {
102
+ return $_transient_data;
103
+ }
104
+
105
+ if ( ! empty( $_transient_data->response ) && ! empty( $_transient_data->response[ $this->name ] ) && false === $this->wp_override ) {
106
+ return $_transient_data;
107
+ }
108
+
109
+ $version_info = $this->get_cached_version_info();
110
+
111
+ if ( false === $version_info ) {
112
+ $version_info = $this->api_request( 'plugin_latest_version', array( 'slug' => $this->slug, 'beta' => $this->beta ) );
113
+
114
+ $this->set_version_info_cache( $version_info );
115
+
116
+ }
117
+
118
+ if ( false !== $version_info && is_object( $version_info ) && isset( $version_info->new_version ) ) {
119
+
120
+ if ( version_compare( $this->version, $version_info->new_version, '<' ) ) {
121
+
122
+ $_transient_data->response[ $this->name ] = $version_info;
123
+
124
+ // Make sure the plugin property is set to the plugin's name/location. See issue 1463 on Software Licensing's GitHub repo.
125
+ $_transient_data->response[ $this->name ]->plugin = $this->name;
126
+
127
+ }
128
+
129
+ $_transient_data->last_checked = time();
130
+ $_transient_data->checked[ $this->name ] = $this->version;
131
+
132
+ }
133
+
134
+ return $_transient_data;
135
+ }
136
+
137
+ /**
138
+ * show update nofication row -- needed for multisite subsites, because WP won't tell you otherwise!
139
+ *
140
+ * @param string $file
141
+ * @param array $plugin
142
+ */
143
+ public function show_update_notification( $file, $plugin ) {
144
+
145
+ if ( is_network_admin() ) {
146
+ return;
147
+ }
148
+
149
+ if( ! current_user_can( 'update_plugins' ) ) {
150
+ return;
151
+ }
152
+
153
+ if( ! is_multisite() ) {
154
+ return;
155
+ }
156
+
157
+ if ( $this->name != $file ) {
158
+ return;
159
+ }
160
+
161
+ // Remove our filter on the site transient
162
+ remove_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ), 10 );
163
+
164
+ $update_cache = get_site_transient( 'update_plugins' );
165
+
166
+ $update_cache = is_object( $update_cache ) ? $update_cache : new stdClass();
167
+
168
+ if ( empty( $update_cache->response ) || empty( $update_cache->response[ $this->name ] ) ) {
169
+
170
+ $version_info = $this->get_cached_version_info();
171
+
172
+ if ( false === $version_info ) {
173
+ $version_info = $this->api_request( 'plugin_latest_version', array( 'slug' => $this->slug, 'beta' => $this->beta ) );
174
+
175
+ // Since we disabled our filter for the transient, we aren't running our object conversion on banners, sections, or icons. Do this now:
176
+ if ( isset( $version_info->banners ) && ! is_array( $version_info->banners ) ) {
177
+ $version_info->banners = $this->convert_object_to_array( $version_info->banners );
178
+ }
179
+
180
+ if ( isset( $version_info->sections ) && ! is_array( $version_info->sections ) ) {
181
+ $version_info->sections = $this->convert_object_to_array( $version_info->sections );
182
+ }
183
+
184
+ if ( isset( $version_info->icons ) && ! is_array( $version_info->icons ) ) {
185
+ $version_info->icons = $this->convert_object_to_array( $version_info->icons );
186
+ }
187
+
188
+ if ( isset( $version_info->icons ) && ! is_array( $version_info->icons ) ) {
189
+ $version_info->icons = $this->convert_object_to_array( $version_info->icons );
190
+ }
191
+
192
+ if ( isset( $version_info->contributors ) && ! is_array( $version_info->contributors ) ) {
193
+ $version_info->contributors = $this->convert_object_to_array( $version_info->contributors );
194
+ }
195
+
196
+ $this->set_version_info_cache( $version_info );
197
+ }
198
+
199
+ if ( ! is_object( $version_info ) ) {
200
+ return;
201
+ }
202
+
203
+ if ( version_compare( $this->version, $version_info->new_version, '<' ) ) {
204
+
205
+ $update_cache->response[ $this->name ] = $version_info;
206
+
207
+ }
208
+
209
+ $update_cache->last_checked = time();
210
+ $update_cache->checked[ $this->name ] = $this->version;
211
+
212
+ set_site_transient( 'update_plugins', $update_cache );
213
+
214
+ } else {
215
+
216
+ $version_info = $update_cache->response[ $this->name ];
217
+
218
+ }
219
+
220
+ // Restore our filter
221
+ add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ) );
222
+
223
+ if ( ! empty( $update_cache->response[ $this->name ] ) && version_compare( $this->version, $version_info->new_version, '<' ) ) {
224
+
225
+ // build a plugin list row, with update notification
226
+ $wp_list_table = _get_list_table( 'WP_Plugins_List_Table' );
227
+ # <tr class="plugin-update-tr"><td colspan="' . $wp_list_table->get_column_count() . '" class="plugin-update colspanchange">
228
+ echo '<tr class="plugin-update-tr" id="' . $this->slug . '-update" data-slug="' . $this->slug . '" data-plugin="' . $this->slug . '/' . $file . '">';
229
+ echo '<td colspan="3" class="plugin-update colspanchange">';
230
+ echo '<div class="update-message notice inline notice-warning notice-alt">';
231
+
232
+ $changelog_link = self_admin_url( 'index.php?edd_sl_action=view_plugin_changelog&plugin=' . $this->name . '&slug=' . $this->slug . '&TB_iframe=true&width=772&height=911' );
233
+
234
+ if ( empty( $version_info->download_link ) ) {
235
+ printf(
236
+ __( 'There is a new version of %1$s available. %2$sView version %3$s details%4$s.', 'easy-digital-downloads' ),
237
+ esc_html( $version_info->name ),
238
+ '<a target="_blank" class="thickbox" href="' . esc_url( $changelog_link ) . '">',
239
+ esc_html( $version_info->new_version ),
240
+ '</a>'
241
+ );
242
+ } else {
243
+ printf(
244
+ __( 'There is a new version of %1$s available. %2$sView version %3$s details%4$s or %5$supdate now%6$s.', 'easy-digital-downloads' ),
245
+ esc_html( $version_info->name ),
246
+ '<a target="_blank" class="thickbox" href="' . esc_url( $changelog_link ) . '">',
247
+ esc_html( $version_info->new_version ),
248
+ '</a>',
249
+ '<a href="' . esc_url( wp_nonce_url( self_admin_url( 'update.php?action=upgrade-plugin&plugin=' ) . $this->name, 'upgrade-plugin_' . $this->name ) ) .'">',
250
+ '</a>'
251
+ );
252
+ }
253
+
254
+ do_action( "in_plugin_update_message-{$file}", $plugin, $version_info );
255
+
256
+ echo '</div></td></tr>';
257
+ }
258
+ }
259
+
260
+ /**
261
+ * Updates information on the "View version x.x details" page with custom data.
262
+ *
263
+ * @uses api_request()
264
+ *
265
+ * @param mixed $_data
266
+ * @param string $_action
267
+ * @param object $_args
268
+ * @return object $_data
269
+ */
270
+ public function plugins_api_filter( $_data, $_action = '', $_args = null ) {
271
+
272
+ if ( $_action != 'plugin_information' ) {
273
+
274
+ return $_data;
275
+
276
+ }
277
+
278
+ if ( ! isset( $_args->slug ) || ( $_args->slug != $this->slug ) ) {
279
+
280
+ return $_data;
281
+
282
+ }
283
+
284
+ $to_send = array(
285
+ 'slug' => $this->slug,
286
+ 'is_ssl' => is_ssl(),
287
+ 'fields' => array(
288
+ 'banners' => array(),
289
+ 'reviews' => false,
290
+ 'icons' => array(),
291
+ )
292
+ );
293
+
294
+ $cache_key = 'edd_api_request_' . md5( serialize( $this->slug . $this->api_data['license'] . $this->beta ) );
295
+
296
+ // Get the transient where we store the api request for this plugin for 24 hours
297
+ $edd_api_request_transient = $this->get_cached_version_info( $cache_key );
298
+
299
+ //If we have no transient-saved value, run the API, set a fresh transient with the API value, and return that value too right now.
300
+ if ( empty( $edd_api_request_transient ) ) {
301
+
302
+ $api_response = $this->api_request( 'plugin_information', $to_send );
303
+
304
+ // Expires in 3 hours
305
+ $this->set_version_info_cache( $api_response, $cache_key );
306
+
307
+ if ( false !== $api_response ) {
308
+ $_data = $api_response;
309
+ }
310
+
311
+ } else {
312
+ $_data = $edd_api_request_transient;
313
+ }
314
+
315
+ // Convert sections into an associative array, since we're getting an object, but Core expects an array.
316
+ if ( isset( $_data->sections ) && ! is_array( $_data->sections ) ) {
317
+ $_data->sections = $this->convert_object_to_array( $_data->sections );
318
+ }
319
+
320
+ // Convert banners into an associative array, since we're getting an object, but Core expects an array.
321
+ if ( isset( $_data->banners ) && ! is_array( $_data->banners ) ) {
322
+ $_data->banners = $this->convert_object_to_array( $_data->banners );
323
+ }
324
+
325
+ // Convert icons into an associative array, since we're getting an object, but Core expects an array.
326
+ if ( isset( $_data->icons ) && ! is_array( $_data->icons ) ) {
327
+ $_data->icons = $this->convert_object_to_array( $_data->icons );
328
+ }
329
+
330
+ // Convert contributors into an associative array, since we're getting an object, but Core expects an array.
331
+ if ( isset( $_data->contributors ) && ! is_array( $_data->contributors ) ) {
332
+ $_data->contributors = $this->convert_object_to_array( $_data->contributors );
333
+ }
334
+
335
+ if( ! isset( $_data->plugin ) ) {
336
+ $_data->plugin = $this->name;
337
+ }
338
+
339
+ return $_data;
340
+ }
341
+
342
+ /**
343
+ * Convert some objects to arrays when injecting data into the update API
344
+ *
345
+ * Some data like sections, banners, and icons are expected to be an associative array, however due to the JSON
346
+ * decoding, they are objects. This method allows us to pass in the object and return an associative array.
347
+ *
348
+ * @since 3.6.5
349
+ *
350
+ * @param stdClass $data
351
+ *
352
+ * @return array
353
+ */
354
+ private function convert_object_to_array( $data ) {
355
+ $new_data = array();
356
+ foreach ( $data as $key => $value ) {
357
+ $new_data[ $key ] = is_object( $value ) ? $this->convert_object_to_array( $value ) : $value;
358
+ }
359
+
360
+ return $new_data;
361
+ }
362
+
363
+ /**
364
+ * Disable SSL verification in order to prevent download update failures
365
+ *
366
+ * @param array $args
367
+ * @param string $url
368
+ * @return object $array
369
+ */
370
+ public function http_request_args( $args, $url ) {
371
+
372
+ $verify_ssl = $this->verify_ssl();
373
+ if ( strpos( $url, 'https://' ) !== false && strpos( $url, 'edd_action=package_download' ) ) {
374
+ $args['sslverify'] = $verify_ssl;
375
+ }
376
+ return $args;
377
+
378
+ }
379
+
380
+ /**
381
+ * Calls the API and, if successfull, returns the object delivered by the API.
382
+ *
383
+ * @uses get_bloginfo()
384
+ * @uses wp_remote_post()
385
+ * @uses is_wp_error()
386
+ *
387
+ * @param string $_action The requested action.
388
+ * @param array $_data Parameters for the API action.
389
+ * @return false|object
390
+ */
391
+ private function api_request( $_action, $_data ) {
392
+
393
+ global $wp_version, $edd_plugin_url_available;
394
+
395
+ $verify_ssl = $this->verify_ssl();
396
+
397
+ // Do a quick status check on this domain if we haven't already checked it.
398
+ $store_hash = md5( $this->api_url );
399
+ if ( ! is_array( $edd_plugin_url_available ) || ! isset( $edd_plugin_url_available[ $store_hash ] ) ) {
400
+ $test_url_parts = parse_url( $this->api_url );
401
+
402
+ $scheme = ! empty( $test_url_parts['scheme'] ) ? $test_url_parts['scheme'] : 'http';
403
+ $host = ! empty( $test_url_parts['host'] ) ? $test_url_parts['host'] : '';
404
+ $port = ! empty( $test_url_parts['port'] ) ? ':' . $test_url_parts['port'] : '';
405
+
406
+ if ( empty( $host ) ) {
407
+ $edd_plugin_url_available[ $store_hash ] = false;
408
+ } else {
409
+ $test_url = $scheme . '://' . $host . $port;
410
+ $response = wp_remote_get( $test_url, array( 'timeout' => $this->health_check_timeout, 'sslverify' => $verify_ssl ) );
411
+ $edd_plugin_url_available[ $store_hash ] = is_wp_error( $response ) ? false : true;
412
+ }
413
+ }
414
+
415
+ if ( false === $edd_plugin_url_available[ $store_hash ] ) {
416
+ return;
417
+ }
418
+
419
+ $data = array_merge( $this->api_data, $_data );
420
+
421
+ if ( $data['slug'] != $this->slug ) {
422
+ return;
423
+ }
424
+
425
+ if( $this->api_url == trailingslashit ( home_url() ) ) {
426
+ return false; // Don't allow a plugin to ping itself
427
+ }
428
+
429
+ $api_params = array(
430
+ 'edd_action' => 'get_version',
431
+ 'license' => ! empty( $data['license'] ) ? $data['license'] : '',
432
+ 'item_name' => isset( $data['item_name'] ) ? $data['item_name'] : false,
433
+ 'item_id' => isset( $data['item_id'] ) ? $data['item_id'] : false,
434
+ 'version' => isset( $data['version'] ) ? $data['version'] : false,
435
+ 'slug' => $data['slug'],
436
+ 'author' => $data['author'],
437
+ 'url' => home_url(),
438
+ 'beta' => ! empty( $data['beta'] ),
439
+ );
440
+
441
+ $request = wp_remote_post( $this->api_url, array( 'timeout' => 15, 'sslverify' => $verify_ssl, 'body' => $api_params ) );
442
+
443
+ if ( ! is_wp_error( $request ) ) {
444
+ $request = json_decode( wp_remote_retrieve_body( $request ) );
445
+ }
446
+
447
+ if ( $request && isset( $request->sections ) ) {
448
+ $request->sections = maybe_unserialize( $request->sections );
449
+ } else {
450
+ $request = false;
451
+ }
452
+
453
+ if ( $request && isset( $request->banners ) ) {
454
+ $request->banners = maybe_unserialize( $request->banners );
455
+ }
456
+
457
+ if ( $request && isset( $request->icons ) ) {
458
+ $request->icons = maybe_unserialize( $request->icons );
459
+ }
460
+
461
+ if( ! empty( $request->sections ) ) {
462
+ foreach( $request->sections as $key => $section ) {
463
+ $request->$key = (array) $section;
464
+ }
465
+ }
466
+
467
+ return $request;
468
+ }
469
+
470
+ public function show_changelog() {
471
+
472
+ global $edd_plugin_data;
473
+
474
+ if( empty( $_REQUEST['edd_sl_action'] ) || 'view_plugin_changelog' != $_REQUEST['edd_sl_action'] ) {
475
+ return;
476
+ }
477
+
478
+ if( empty( $_REQUEST['plugin'] ) ) {
479
+ return;
480
+ }
481
+
482
+ if( empty( $_REQUEST['slug'] ) ) {
483
+ return;
484
+ }
485
+
486
+ if( ! current_user_can( 'update_plugins' ) ) {
487
+ wp_die( __( 'You do not have permission to install plugin updates', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
488
+ }
489
+
490
+ $data = $edd_plugin_data[ $_REQUEST['slug'] ];
491
+ $beta = ! empty( $data['beta'] ) ? true : false;
492
+ $cache_key = md5( 'edd_plugin_' . sanitize_key( $_REQUEST['plugin'] ) . '_' . $beta . '_version_info' );
493
+ $version_info = $this->get_cached_version_info( $cache_key );
494
+
495
+ if( false === $version_info ) {
496
+
497
+ $api_params = array(
498
+ 'edd_action' => 'get_version',
499
+ 'item_name' => isset( $data['item_name'] ) ? $data['item_name'] : false,
500
+ 'item_id' => isset( $data['item_id'] ) ? $data['item_id'] : false,
501
+ 'slug' => $_REQUEST['slug'],
502
+ 'author' => $data['author'],
503
+ 'url' => home_url(),
504
+ 'beta' => ! empty( $data['beta'] )
505
+ );
506
+
507
+ $verify_ssl = $this->verify_ssl();
508
+ $request = wp_remote_post( $this->api_url, array( 'timeout' => 15, 'sslverify' => $verify_ssl, 'body' => $api_params ) );
509
+
510
+ if ( ! is_wp_error( $request ) ) {
511
+ $version_info = json_decode( wp_remote_retrieve_body( $request ) );
512
+ }
513
+
514
+
515
+ if ( ! empty( $version_info ) && isset( $version_info->sections ) ) {
516
+ $version_info->sections = maybe_unserialize( $version_info->sections );
517
+ } else {
518
+ $version_info = false;
519
+ }
520
+
521
+ if( ! empty( $version_info ) ) {
522
+ foreach( $version_info->sections as $key => $section ) {
523
+ $version_info->$key = (array) $section;
524
+ }
525
+ }
526
+
527
+ $this->set_version_info_cache( $version_info, $cache_key );
528
+
529
+ }
530
+
531
+ if( ! empty( $version_info ) && isset( $version_info->sections['changelog'] ) ) {
532
+ echo '<div style="background:#fff;padding:10px;">' . $version_info->sections['changelog'] . '</div>';
533
+ }
534
+
535
+ exit;
536
+ }
537
+
538
+ public function get_cached_version_info( $cache_key = '' ) {
539
+
540
+ if( empty( $cache_key ) ) {
541
+ $cache_key = $this->cache_key;
542
+ }
543
+
544
+ $cache = get_option( $cache_key );
545
+
546
+ if( empty( $cache['timeout'] ) || time() > $cache['timeout'] ) {
547
+ return false; // Cache is expired
548
+ }
549
+
550
+ // We need to turn the icons into an array, thanks to WP Core forcing these into an object at some point.
551
+ $cache['value'] = json_decode( $cache['value'] );
552
+ if ( ! empty( $cache['value']->icons ) ) {
553
+ $cache['value']->icons = (array) $cache['value']->icons;
554
+ }
555
+
556
+ return $cache['value'];
557
+
558
+ }
559
+
560
+ public function set_version_info_cache( $value = '', $cache_key = '' ) {
561
+
562
+ if( empty( $cache_key ) ) {
563
+ $cache_key = $this->cache_key;
564
+ }
565
+
566
+ $data = array(
567
+ 'timeout' => strtotime( '+3 hours', time() ),
568
+ 'value' => json_encode( $value )
569
+ );
570
+
571
+ update_option( $cache_key, $data, 'no' );
572
+
573
+ }
574
+
575
+ /**
576
+ * Returns if the SSL of the store should be verified.
577
+ *
578
+ * @since 1.6.13
579
+ * @return bool
580
+ */
581
+ private function verify_ssl() {
582
+ return (bool) apply_filters( 'edd_sl_api_request_verify_ssl', true, $this );
583
+ }
584
+
585
+ }
Postman/Extensions/License/PostmanLicenseHandler.php ADDED
@@ -0,0 +1,422 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+ if ( ! class_exists( 'PostmanLicenseHandler' ) ) :
7
+
8
+
9
+ class PostmanLicenseHandler {
10
+ private $file;
11
+ private $license;
12
+ private $license_data;
13
+ private $item_name;
14
+ private $item_id;
15
+ private $item_shortname;
16
+ private $version;
17
+ private $author;
18
+ private $api_url = 'http://localhost/psp/';
19
+
20
+
21
+ function __construct( $_file, $_item_name, $_version, $_author, $_optname = null, $_api_url = null, $_item_id = null ) {
22
+ $this->file = $_file;
23
+ $this->item_name = $_item_name;
24
+
25
+ if ( is_numeric( $_item_id ) ) {
26
+ $this->item_id = absint( $_item_id );
27
+ }
28
+
29
+ $this->item_shortname = $this->get_slug();
30
+ $this->version = $_version;
31
+ $this->license = trim( get_option( $this->item_shortname . '_license_key', '' ) );
32
+ $this->license_data = get_option( $this->item_shortname . '_license_active', '' );
33
+ $this->author = $_author;
34
+ $this->api_url = is_null( $_api_url ) ? $this->api_url : $_api_url;
35
+
36
+ /**
37
+ * Allows for backwards compatibility with old license options,
38
+ * i.e. if the plugins had license key fields previously, the license
39
+ * handler will automatically pick these up and use those in lieu of the
40
+ * user having to reactive their license.
41
+ */
42
+ if ( ! empty( $_optname ) ) {
43
+ $opt = get_option( $_optname, false );
44
+
45
+ if( isset( $opt ) && empty( $this->license ) ) {
46
+ $this->license = trim( $opt );
47
+ }
48
+ }
49
+
50
+ // Setup hooks
51
+ $this->includes();
52
+ $this->hooks();
53
+
54
+ }
55
+
56
+ /**
57
+ * Include the updater class
58
+ *
59
+ * @access private
60
+ * @return void
61
+ */
62
+ private function includes() {
63
+ if ( ! class_exists( 'EDD_SL_Plugin_Updater' ) ) {
64
+ require_once 'EDD_SL_Plugin_Updater.php';
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Setup hooks
70
+ *
71
+ * @access private
72
+ * @return void
73
+ */
74
+ public function hooks() {
75
+
76
+ // Activate license key on settings save
77
+ add_action( 'admin_init', array( $this, 'activate_license' ) );
78
+
79
+ // Deactivate license key
80
+ add_action( 'admin_init', array( $this, 'deactivate_license' ) );
81
+
82
+ add_action( 'init', array( $this, 'cron' ), 20 );
83
+
84
+ // Check that license is valid once per week
85
+ add_action( 'admin_init', array( $this, 'validate_license' ) );
86
+
87
+ // Updater
88
+ add_action( 'admin_init', array( $this, 'auto_updater' ), 0 );
89
+
90
+ // Display notices to admins
91
+ add_action( 'admin_notices', array( $this, 'notices' ) );
92
+
93
+ add_action( 'in_plugin_update_message-' . plugin_basename( $this->file ), array( $this, 'plugin_row_license_missing' ), 10, 2 );
94
+ }
95
+
96
+ /**
97
+ * Auto updater
98
+ *
99
+ * @access private
100
+ * @return void
101
+ */
102
+ public function auto_updater() {
103
+
104
+ $args = array(
105
+ 'version' => $this->version,
106
+ 'license' => $this->license,
107
+ 'author' => $this->author,
108
+ 'beta' => function_exists( 'edd_extension_has_beta_support' ) && edd_extension_has_beta_support( $this->item_shortname ),
109
+ );
110
+
111
+ if( ! empty( $this->item_id ) ) {
112
+ $args['item_id'] = $this->item_id;
113
+ } else {
114
+ $args['item_name'] = $this->item_name;
115
+ }
116
+
117
+ // Setup the updater
118
+ $edd_updater = new EDD_SL_Plugin_Updater(
119
+ $this->api_url,
120
+ $this->file,
121
+ $args
122
+ );
123
+ }
124
+
125
+ public function cron() {
126
+ if ( ! wp_next_scheduled( $this->item_shortname . '_scheduled_events' ) ) {
127
+ wp_schedule_event( current_time( 'timestamp', true ), 'daily', $this->item_shortname . '_scheduled_events' );
128
+ }
129
+ }
130
+
131
+ public function get_slug() {
132
+ return preg_replace( '/[^a-zA-Z0-9_\s]/', '', str_replace( ' ', '_', strtolower( $this->item_name ) ) );
133
+ }
134
+
135
+ /**
136
+ * Display help text at the top of the Licenses tag
137
+ *
138
+ * @since 2.5
139
+ * @param string $active_tab
140
+ * @return void
141
+ */
142
+ public function license_help_text( $active_tab = '' ) {
143
+
144
+ static $has_ran;
145
+
146
+ if( 'licenses' !== $active_tab ) {
147
+ return;
148
+ }
149
+
150
+ if( ! empty( $has_ran ) ) {
151
+ return;
152
+ }
153
+
154
+ echo '<p>' . sprintf(
155
+ __( 'Enter your extension license keys here to receive updates for purchased extensions. If your license key has expired, please <a href="%s" target="_blank">renew your license</a>.', 'easy-digital-downloads' ),
156
+ 'http://docs.easydigitaldownloads.com/article/1000-license-renewal'
157
+ ) . '</p>';
158
+
159
+ $has_ran = true;
160
+
161
+ }
162
+
163
+
164
+ /**
165
+ * Activate the license key
166
+ *
167
+ * @return void
168
+ */
169
+ public function activate_license() {
170
+
171
+ if ( ! isset( $_POST['post_smtp_extension'][ $this->item_shortname . '_activate'] ) ) {
172
+ return;
173
+ }
174
+
175
+ if ( ! isset( $_REQUEST[ $this->item_shortname . '_license_key-nonce'] ) || ! wp_verify_nonce( $_REQUEST[ $this->item_shortname . '_license_key-nonce'], $this->item_shortname . '_license_key-nonce' ) ) {
176
+
177
+ return;
178
+
179
+ }
180
+
181
+ if ( ! current_user_can( 'manage_options' ) ) {
182
+ return;
183
+ }
184
+
185
+ if ( empty( $_POST['post_smtp_extension'][ $this->item_shortname . '_license_key'] ) ) {
186
+
187
+ delete_option( $this->item_shortname . '_license_active' );
188
+ delete_option( $this->item_shortname . '_license_key' );
189
+
190
+ return;
191
+
192
+ }
193
+
194
+ foreach ( $_POST as $key => $value ) {
195
+ if( false !== strpos( $key, 'license_key_deactivate' ) ) {
196
+ // Don't activate a key when deactivating a different key
197
+ return;
198
+ }
199
+ }
200
+
201
+ $details = get_option( $this->item_shortname . '_license_active' );
202
+
203
+ if ( is_object( $details ) && 'valid' === $details->license ) {
204
+ return;
205
+ }
206
+
207
+ $license = sanitize_text_field( $_POST['post_smtp_extension'][ $this->item_shortname . '_license_key'] );
208
+
209
+ if( empty( $license ) ) {
210
+ return;
211
+ }
212
+
213
+ // Data to send to the API
214
+ $api_params = array(
215
+ 'edd_action' => 'activate_license',
216
+ 'license' => $license,
217
+ 'item_name' => urlencode( $this->item_name ),
218
+ 'url' => home_url()
219
+ );
220
+
221
+ if ( ! empty( $this->item_id ) ) {
222
+ $api_params['item_id'] = $this->item_id;
223
+ }
224
+
225
+ // Call the API
226
+ $response = wp_remote_post(
227
+ $this->api_url,
228
+ array(
229
+ 'timeout' => 15,
230
+ 'sslverify' => false,
231
+ 'body' => $api_params
232
+ )
233
+ );
234
+
235
+ // Make sure there are no errors
236
+ if ( is_wp_error( $response ) ) {
237
+ return;
238
+ }
239
+
240
+ // Tell WordPress to look for updates
241
+ set_site_transient( 'update_plugins', null );
242
+
243
+ // Decode license data
244
+ $license_data = json_decode( wp_remote_retrieve_body( $response ) );
245
+
246
+ update_option( $this->item_shortname . '_license_active', $license_data );
247
+ update_option( $this->item_shortname . '_license_key', $license );
248
+
249
+ $slug = plugin_basename($this->file);
250
+ PostmanLicenseManager::get_instance()->add_extension($slug);
251
+
252
+ }
253
+
254
+
255
+ /**
256
+ * Deactivate the license key
257
+ *
258
+ * @return void
259
+ */
260
+ public function deactivate_license() {
261
+
262
+ if ( ! isset( $_POST['post_smtp_extension'][ $this->item_shortname . '_deactivate'] ) ) {
263
+ return;
264
+ }
265
+
266
+ if ( ! isset( $_POST['post_smtp_extension'][ $this->item_shortname . '_license_key'] ) )
267
+ return;
268
+
269
+ if( ! wp_verify_nonce( $_REQUEST[ $this->item_shortname . '_license_key-nonce'], $this->item_shortname . '_license_key-nonce' ) ) {
270
+
271
+ wp_die( __( 'Nonce verification failed', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
272
+
273
+ }
274
+
275
+ if( ! current_user_can( 'manage_options' ) ) {
276
+ return;
277
+ }
278
+
279
+ $license_key = sanitize_text_field( base64_decode( $_POST['post_smtp_extension'][ $this->item_shortname . '_license_key'] ) );
280
+
281
+ // Run on deactivate button press
282
+ // Data to send to the API
283
+ $api_params = array(
284
+ 'edd_action' => 'deactivate_license',
285
+ 'license' => $license_key,
286
+ 'item_name' => urlencode( $this->item_name ),
287
+ 'url' => home_url()
288
+ );
289
+
290
+ if ( ! empty( $this->item_id ) ) {
291
+ $api_params['item_id'] = $this->item_id;
292
+ }
293
+
294
+ // Call the API
295
+ $response = wp_remote_post(
296
+ $this->api_url,
297
+ array(
298
+ 'timeout' => 15,
299
+ 'sslverify' => false,
300
+ 'body' => $api_params
301
+ )
302
+ );
303
+
304
+ // Make sure there are no errors
305
+ if ( is_wp_error( $response ) ) {
306
+ return;
307
+ }
308
+
309
+ // Decode the license data
310
+ $license_data = json_decode( wp_remote_retrieve_body( $response ) );
311
+
312
+ delete_option( $this->item_shortname . '_license_active' );
313
+ delete_option( $this->item_shortname . '_license_key' );
314
+
315
+ $slug = plugin_basename($this->file);
316
+ PostmanLicenseManager::get_instance()->remove_extension($slug);
317
+
318
+ }
319
+
320
+ public function validate_license() {
321
+ if ( false === ( $cron_data = get_transient( $this->item_shortname . '_cron' ) ) ) {
322
+ $this->license_check();
323
+
324
+ set_transient( $this->item_shortname . '_cron', true, rand( 12, 48 ) * HOUR_IN_SECONDS );
325
+ }
326
+ }
327
+
328
+
329
+ /**
330
+ * Check if license key is valid once per week
331
+ *
332
+ * @since 2.5
333
+ * @return void
334
+ */
335
+ public function license_check() {
336
+
337
+ // data to send in our API request
338
+ $api_params = array(
339
+ 'edd_action'=> 'check_license',
340
+ 'license' => $this->license,
341
+ 'item_name' => urlencode( $this->item_name ),
342
+ 'url' => home_url()
343
+ );
344
+
345
+ // Call the API
346
+ $response = wp_remote_post(
347
+ $this->api_url,
348
+ array(
349
+ 'timeout' => 15,
350
+ 'sslverify' => false,
351
+ 'body' => $api_params
352
+ )
353
+ );
354
+
355
+ // make sure the response came back okay
356
+ if ( is_wp_error( $response ) ) {
357
+ return false;
358
+ }
359
+
360
+ $license_data = json_decode( wp_remote_retrieve_body( $response ) );
361
+
362
+ update_option( $this->item_shortname . '_license_active', $license_data );
363
+
364
+ }
365
+
366
+
367
+ /**
368
+ * Admin notices for errors
369
+ *
370
+ * @return void
371
+ */
372
+ public function notices() {
373
+
374
+ $showed_invalid_message = null;
375
+
376
+ if( empty( $this->license ) ) {
377
+ return;
378
+ }
379
+
380
+ if( ! current_user_can( 'manage_options' ) ) {
381
+ return;
382
+ }
383
+
384
+ $messages = array();
385
+
386
+ $license = get_option( $this->item_shortname . '_license_active' );
387
+
388
+ if( is_object( $license ) && 'valid' !== $license->license && empty( $showed_invalid_message ) ) {
389
+
390
+ if( isset( $_GET['page'] ) && 'post-smtp-extensions' === $_GET['page'] ) {
391
+
392
+ $messages[] = sprintf(
393
+ __( '%s has invalid or expired license key for Post SMTP.'),
394
+ '<strong>' . $this->item_name . '</strong>'
395
+ );
396
+
397
+ $showed_invalid_message = true;
398
+
399
+ }
400
+
401
+ }
402
+
403
+ if( ! empty( $messages ) ) {
404
+
405
+ foreach( $messages as $message ) {
406
+
407
+ echo '<div class="error">';
408
+ echo '<p>' . $message . '</p>';
409
+ echo '</div>';
410
+
411
+ }
412
+
413
+ }
414
+
415
+ }
416
+
417
+ public function is_licensed() {
418
+ return is_object($this->license_data) && 'valid' === $this->license_data->license;
419
+ }
420
+ }
421
+
422
+ endif; // end class_exists check
Postman/Extensions/License/PostmanLicenseManager.php ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( ! defined( 'ABSPATH' ) ) exit;
3
+
4
+ class PostmanLicenseManager {
5
+
6
+ const ENDPOINT = 'https://postmansmtp.com';
7
+
8
+ const CORE_EXTENSIONS = [ 'gmail_api', 'sendgrid_api', 'mandrill_api', 'mailgun_api' ];
9
+
10
+ private $extensions;
11
+
12
+ private $rand_cache_interval = 12;
13
+
14
+ private static $instance;
15
+
16
+ public static function get_instance() {
17
+ if ( ! self::$instance ) {
18
+ self::$instance = new static();
19
+ }
20
+
21
+ return self::$instance;
22
+ }
23
+
24
+ /**
25
+ * PostmanLicenseManager constructor.
26
+ */
27
+ private function __construct()
28
+ {
29
+ $this->includes();
30
+ $this->rand_cache_interval = rand( 1, 24 );
31
+
32
+ add_filter( 'extra_plugin_headers', [ $this, 'add_extension_headers' ] );
33
+ }
34
+
35
+ public function includes() {
36
+ include_once 'PostmanLicenseHandler.php';
37
+
38
+ include_once ABSPATH . '/wp-admin/includes/plugin.php';
39
+
40
+ }
41
+
42
+
43
+ function add_extension_headers($headers) {
44
+ $headers[] = 'Class';
45
+ $headers[] = 'Slug';
46
+
47
+ return $headers;
48
+ }
49
+
50
+ /**
51
+ * Init
52
+ */
53
+ public function init() {
54
+
55
+ $plugins = get_plugins();
56
+ foreach ( $plugins as $plugin_dir_and_filename => $plugin_data ) {
57
+
58
+ if ( ! is_plugin_active( $plugin_dir_and_filename ) ) {
59
+ continue;
60
+ }
61
+
62
+ if ( false !== strpos( $plugin_dir_and_filename, 'post-smtp-extension' ) ) {
63
+ $slug = $plugin_dir_and_filename;
64
+ $class = $plugin_data['Class'];
65
+ $plugin_path = WP_CONTENT_DIR . '/plugins/' . $plugin_dir_and_filename;
66
+
67
+ $this->extensions[$slug]['plugin_data'] = $plugin_data;
68
+ $this->extensions[$slug]['plugin_dir_and_filename'] = $plugin_dir_and_filename;
69
+ $this->extensions[$slug]['license_manager'] = new PostmanLicenseHandler(
70
+ $plugin_path, $plugin_data['Name'],
71
+ $plugin_data['Version'], $plugin_data['Author']
72
+ );
73
+ if ( $this->extensions[$slug]['license_manager']->is_licensed() ) {
74
+ include_once $plugin_path;
75
+
76
+ $this->extensions[$slug]['instance'] = new $class;
77
+ }
78
+ }
79
+ }
80
+
81
+ if ( ! empty( $this->extensions ) ) {
82
+ new PostmanAdmin();
83
+ }
84
+ }
85
+
86
+ public function add_extension($slug) {
87
+ $plugin_path = WP_CONTENT_DIR . '/plugins/' . $this->extensions[$slug]['plugin_dir_and_filename'];
88
+ $class = $this->extensions[$slug]['plugin_data']['Class'];
89
+
90
+ include_once $plugin_path;
91
+ $this->extensions[$slug]['instance'] = new $class;
92
+ }
93
+
94
+ public function remove_extension($slug) {
95
+ $this->extensions[$slug]['instance'] = null;
96
+ unset($this->extensions[$slug]['instance']);
97
+ }
98
+
99
+ public function get_extensions() {
100
+ return $this->extensions;
101
+ }
102
+ }
Postman/Phpmailer/PostsmtpMailer.php CHANGED
@@ -2,8 +2,10 @@
2
  if ( ! defined( 'ABSPATH' ) ) {
3
  exit; // Exit if accessed directly
4
  }
5
- require_once ABSPATH . WPINC . '/class-phpmailer.php';
6
- require_once ABSPATH . WPINC . '/class-smtp.php';
 
 
7
 
8
  add_action('plugins_loaded', function() {
9
  global $phpmailer;
@@ -17,12 +19,53 @@ class PostsmtpMailer extends PHPMailer {
17
 
18
  private $error;
19
 
 
 
20
  public function __construct($exceptions = null)
21
  {
22
  parent::__construct($exceptions);
23
 
 
 
 
 
 
 
24
  $this->options = PostmanOptions::getInstance();
25
- add_filter( 'postman_wp_mail_result', [ $this, 'postman_wp_mail_result' ] );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  }
27
 
28
  public function send()
@@ -33,40 +76,46 @@ class PostsmtpMailer extends PHPMailer {
33
  $postmanWpMail = new PostmanWpMail();
34
  $postmanWpMail->init();
35
 
36
- $senderEmail = $this->options->getMessageSenderEmail();
37
- $senderName = $this->options->getMessageSenderName();
38
-
39
- // create a PostmanMessage instance
40
- $message = $postmanWpMail->createNewMessage();
41
-
42
- $message->setFrom( $senderEmail, $senderName );
43
- $message->addHeaders( $this->getHeaders() );
44
- $message->setBodyTextPart( $this->AltBody );
45
- $message->setBodyHtmlPart( $this->Body );
46
- $message->setBody( $this->Body );
47
- $message->setSubject( $this->Subject );
48
- $message->addTo( $this->flatArray($this->getToAddresses() ) );
49
- $message->setReplyTo( $this->flatArray( $this->getReplyToAddresses() ) );
50
- $message->addCc( $this->flatArray($this->getCcAddresses() ) );
51
- $message->addBCc( $this->flatArray( $this->getBccAddresses() ) );
52
- $message->setReplyTo( $this->flatArray( $this->getReplyToAddresses() ) );
53
- $message->setAttachments( $this->getAttachments() );
54
-
55
- // create a PostmanEmailLog instance
56
  $log = new PostmanEmailLog();
 
 
 
 
57
 
58
- $log->originalTo = $this->flatArray($this->getToAddresses() );
59
- $log->originalSubject = $this->Subject;
60
- $log->originalMessage = $this->Body;
61
- $log->originalHeaders = $this->getCustomHeaders();
62
 
63
  try {
64
- return $postmanWpMail->sendMessage( $message, $log );
 
 
 
 
 
 
 
 
 
 
 
65
  } catch (phpmailerException $exc) {
66
 
67
  $this->error = $exc;
68
 
69
  $this->mailHeader = '';
 
 
 
70
  $this->setError($exc->getMessage());
71
  if ($this->exceptions) {
72
  throw $exc;
@@ -76,50 +125,20 @@ class PostsmtpMailer extends PHPMailer {
76
 
77
  }
78
 
79
- public function getAttachments() {
80
- $attachments = parent::getAttachments();
81
-
82
- $data = array();
83
- foreach ( $attachments as $attachment ) {
84
- $data[] = $attachment[0];
85
  }
86
-
87
- return $data;
88
  }
89
 
90
- private function getHeaders() {
91
- $headers = array();
92
- foreach ( $this->getCustomHeaders() as $header ) {
93
- $headers[] = "{$header[0]}: {$header[1]}";
94
- }
95
 
96
- return $headers;
97
- }
98
-
99
- public function postman_wp_mail_result() {
100
  $result = [
101
  'time' => '',
102
  'exception' => $this->error,
103
- 'transcript' => '',
104
  ];
105
  return $result;
106
  }
107
-
108
- private function flatArray($arr) {
109
- $result = [];
110
- foreach ( $arr as $key => $value ) {
111
- if ( is_array( $value ) ) {
112
- foreach ($value as $k => $v ) {
113
- if ( empty( $v ) ) {
114
- continue;
115
- }
116
- $value = $v;
117
- }
118
- }
119
-
120
- $result[] = $value;
121
- }
122
-
123
- return implode(',', $result );
124
- }
125
  }
2
  if ( ! defined( 'ABSPATH' ) ) {
3
  exit; // Exit if accessed directly
4
  }
5
+
6
+ if ( ! class_exists( 'PHPMailer', false ) ) {
7
+ require_once ABSPATH . WPINC . '/class-phpmailer.php';
8
+ }
9
 
10
  add_action('plugins_loaded', function() {
11
  global $phpmailer;
19
 
20
  private $error;
21
 
22
+ private $transcript = '';
23
+
24
  public function __construct($exceptions = null)
25
  {
26
  parent::__construct($exceptions);
27
 
28
+ $this->set_vars();
29
+ $this->hooks();
30
+
31
+ }
32
+
33
+ public function set_vars() {
34
  $this->options = PostmanOptions::getInstance();
35
+ $this->Debugoutput = function($str, $level) {
36
+ $this->transcript .= $str;
37
+ };
38
+ }
39
+
40
+ public function hooks() {
41
+ if ( $this->options->getTransportType() == 'smtp' ) {
42
+ add_action( 'phpmailer_init', array( $this, 'phpmailer_smtp_init' ) );
43
+ }
44
+ }
45
+
46
+ /**
47
+ * @param PHPMailer $mail
48
+ */
49
+ public function phpmailer_smtp_init($mail) {
50
+ $mail->SMTPDebug = 3;
51
+ $mail->isSMTP();
52
+ $mail->Host = $this->options->getHostname();
53
+
54
+ if ( $this->options->getAuthenticationType() !== 'none' ) {
55
+ $mail->SMTPAuth = true;
56
+ $mail->Username = $this->options->getUsername();
57
+ $mail->Password = $this->options->getPassword();
58
+ }
59
+
60
+ if ( $this->options->getEncryptionType() !== 'none' ) {
61
+ $mail->SMTPSecure = $this->options->getEncryptionType();
62
+ }
63
+
64
+ $mail->Port = $this->options->getPort();
65
+
66
+ if ( $this->options->isPluginSenderEmailEnforced() ) {
67
+ $mail->setFrom( $this->options->getMessageSenderEmail() , $this->options->getMessageSenderName () );
68
+ }
69
  }
70
 
71
  public function send()
76
  $postmanWpMail = new PostmanWpMail();
77
  $postmanWpMail->init();
78
 
79
+ $backtrace = debug_backtrace();
80
+
81
+ list($to, $subject, $body, $headers, $attachments) = array_pad( $backtrace[1]['args'], 5, null );
82
+
83
+ // build the message
84
+ $postmanMessage = $postmanWpMail->processWpMailCall( $to, $subject, $body, $headers, $attachments );
85
+
86
+ // build the email log entry
 
 
 
 
 
 
 
 
 
 
 
 
87
  $log = new PostmanEmailLog();
88
+ $log->originalTo = $to;
89
+ $log->originalSubject = $subject;
90
+ $log->originalMessage = $body;
91
+ $log->originalHeaders = $headers;
92
 
93
+ // get the transport and create the transportConfig and engine
94
+ $transport = PostmanTransportRegistry::getInstance()->getActiveTransport();
95
+
96
+ add_filter( 'postman_wp_mail_result', [ $this, 'postman_wp_mail_result' ] );
97
 
98
  try {
99
+
100
+ if ( $send_email = apply_filters( 'post_smtp_do_send_email', true ) ) {
101
+ $result = $this->options->getTransportType() !== 'smtp' ?
102
+ $postmanWpMail->send( $to, $subject, $body, $headers, $attachments ) :
103
+ $this->sendSmtp();
104
+ }
105
+
106
+
107
+ do_action( 'post_smtp_on_success', $log, $postmanMessage, $this->transcript, $transport );
108
+
109
+ return $result;
110
+
111
  } catch (phpmailerException $exc) {
112
 
113
  $this->error = $exc;
114
 
115
  $this->mailHeader = '';
116
+
117
+ do_action( 'post_smtp_on_failed', $log, $postmanMessage, $this->transcript, $transport, $exc->getMessage() );
118
+
119
  $this->setError($exc->getMessage());
120
  if ($this->exceptions) {
121
  throw $exc;
125
 
126
  }
127
 
128
+ public function sendSmtp() {
129
+ if (!$this->preSend()) {
130
+ return false;
 
 
 
131
  }
132
+ return $this->postSend();
 
133
  }
134
 
 
 
 
 
 
135
 
136
+ public function postman_wp_mail_result() {
 
 
 
137
  $result = [
138
  'time' => '',
139
  'exception' => $this->error,
140
+ 'transcript' => $this->transcript,
141
  ];
142
  return $result;
143
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
144
  }
Postman/Postman-Configuration/PostmanConfigurationController.php CHANGED
@@ -201,13 +201,21 @@ class PostmanConfigurationController {
201
  print '<div class="wrap">';
202
 
203
  PostmanViewController::outputChildPageHeader( __( 'Settings', 'post-smtp' ), 'advanced_config' );
 
 
 
 
 
 
 
 
 
204
  print '<div id="config_tabs"><ul>';
205
- print sprintf( '<li><a href="#account_config">%s</a></li>', __( 'Account', 'post-smtp' ) );
206
- print sprintf( '<li><a href="#fallback">%s</a></li>', __( 'Fallback', 'post-smtp' ) );
207
- print sprintf( '<li><a href="#message_config">%s</a></li>', __( 'Message', 'post-smtp' ) );
208
- print sprintf( '<li><a href="#logging_config">%s</a></li>', __( 'Logging', 'post-smtp' ) );
209
- print sprintf( '<li><a href="#advanced_options_config">%s</a></li>', __( 'Advanced', 'post-smtp' ) );
210
- print sprintf( '<li><a href="#notifications">%s</a></li>', __( 'Notifications', 'post-smtp' ) );
211
  print '</ul>';
212
 
213
  print '<form method="post" action="options.php">';
@@ -294,9 +302,9 @@ class PostmanConfigurationController {
294
  <th scope="row"><?php _e('Security', 'post-smtp' ); ?></th>
295
  <?php
296
  $security_options = array(
297
- 'none' => __( 'None', 'post-smtp' ),
298
- 'ssl' => __( 'SSL', 'post-smtp' ),
299
- 'tls' => __( 'TLS', 'post-smtp' ),
300
  );
301
  ?>
302
  <td>
@@ -313,17 +321,17 @@ class PostmanConfigurationController {
313
  </td>
314
  </tr>
315
 
316
- <tr>
317
- <th scope="row"><?php _e('From Email', 'post-smtp' ); ?></th>
318
- <td>
319
- <input type="email" id="fallback-smtp-from-email"
320
- value="<?php echo $this->options->getFallbackFromEmail(); ?>"
321
- name="postman_options[<?php echo PostmanOptions::FALLBACK_FROM_EMAIL; ?>]"
322
- >
323
- <br>
324
- <small><?php _e( "Use allowed email, for example: If you are using Gmail, type your Gmail adress.", 'post-smtp' ); ?></small>
325
- </td>
326
- </tr>
327
 
328
  <tr valign="">
329
  <th scope="row"><?php _e( 'Use SMTP Authentication?', 'post-smtp' ); ?></th>
@@ -387,24 +395,7 @@ class PostmanConfigurationController {
387
  do_settings_sections( PostmanAdminController::ADVANCED_OPTIONS );
388
  print '</section>';
389
 
390
- print '<section id="notifications">';
391
- do_settings_sections( PostmanAdminController::NOTIFICATIONS_OPTIONS );
392
-
393
- $currentKey = $this->options->getNotificationService();
394
- $pushover = $currentKey == 'pushover' ? 'block' : 'none';
395
- $slack = $currentKey == 'slack' ? 'block' : 'none';
396
-
397
- echo '<div id="pushover_cred" style="display: ' . $pushover . ';">';
398
- do_settings_sections( PostmanAdminController::NOTIFICATIONS_PUSHOVER_CRED );
399
- echo '</div>';
400
-
401
- echo '<div id="slack_cred" style="display: ' . $slack . ';">';
402
- do_settings_sections( PostmanAdminController::NOTIFICATIONS_SLACK_CRED );
403
- echo '</div>';
404
-
405
- do_action( 'post_smtp_notification_settings' );
406
-
407
- print '</section>';
408
 
409
  submit_button();
410
  print '</form>';
@@ -762,7 +753,7 @@ class PostmanManageConfigurationAjaxHandler extends PostmanAbstractAjaxHandler {
762
  wp_send_json_success( $response );
763
  } else {
764
  /* translators: where %s is the URL to the Connectivity Test page */
765
- $configuration ['message'] = sprintf( __( 'Postman can\'t find any way to send mail on your system. Run a <a href="%s">connectivity test</a>.', 'post-smtp' ), PostmanViewController::getPageUrl( PostmanViewController::PORT_TEST_SLUG ) );
766
  $response ['configuration'] = $configuration;
767
  if ( $this->logger->isTrace() ) {
768
  $this->logger->trace( 'configuration:' );
201
  print '<div class="wrap">';
202
 
203
  PostmanViewController::outputChildPageHeader( __( 'Settings', 'post-smtp' ), 'advanced_config' );
204
+
205
+ $config_tabs = apply_filters( 'post_smtp_admin_tabs', array(
206
+ 'account_config' => __( 'Account', 'post-smtp' ),
207
+ 'fallback' => __( 'Fallback', 'post-smtp' ),
208
+ 'message_config' => __( 'Message', 'post-smtp' ),
209
+ 'logging_config' => __( 'Logging', 'post-smtp' ),
210
+ 'advanced_options_config' => __( 'Advanced', 'post-smtp' ),
211
+ ) );
212
+
213
  print '<div id="config_tabs"><ul>';
214
+
215
+ foreach ( $config_tabs as $slug => $tab ) :
216
+ printf( '<li><a href="#%s">%s</a></li>', $slug, $tab );
217
+ endforeach;
218
+
 
219
  print '</ul>';
220
 
221
  print '<form method="post" action="options.php">';
302
  <th scope="row"><?php _e('Security', 'post-smtp' ); ?></th>
303
  <?php
304
  $security_options = array(
305
+ 'none' => __( 'None', 'post-smtp' ),
306
+ 'ssl' => __( 'SSL', 'post-smtp' ),
307
+ 'tls' => __( 'TLS', 'post-smtp' ),
308
  );
309
  ?>
310
  <td>
321
  </td>
322
  </tr>
323
 
324
+ <tr>
325
+ <th scope="row"><?php _e('From Email', 'post-smtp' ); ?></th>
326
+ <td>
327
+ <input type="email" id="fallback-smtp-from-email"
328
+ value="<?php echo $this->options->getFallbackFromEmail(); ?>"
329
+ name="postman_options[<?php echo PostmanOptions::FALLBACK_FROM_EMAIL; ?>]"
330
+ >
331
+ <br>
332
+ <small><?php _e( "Use allowed email, for example: If you are using Gmail, type your Gmail adress.", 'post-smtp' ); ?></small>
333
+ </td>
334
+ </tr>
335
 
336
  <tr valign="">
337
  <th scope="row"><?php _e( 'Use SMTP Authentication?', 'post-smtp' ); ?></th>
395
  do_settings_sections( PostmanAdminController::ADVANCED_OPTIONS );
396
  print '</section>';
397
 
398
+ do_action( 'post_smtp_settings_menu' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
399
 
400
  submit_button();
401
  print '</form>';
753
  wp_send_json_success( $response );
754
  } else {
755
  /* translators: where %s is the URL to the Connectivity Test page */
756
+ $configuration ['message'] = sprintf( __( 'Postman can\'t find any way to send mail on your system. Run a <a href="%s">connectivity test</a>.', 'post-smtp' ), PostmanViewController::getPageUrl( PostmanConnectivityTestController::PORT_TEST_SLUG ) );
757
  $response ['configuration'] = $configuration;
758
  if ( $this->logger->isTrace() ) {
759
  $this->logger->trace( 'configuration:' );
Postman/Postman-Configuration/PostmanRegisterConfigurationSettings.php CHANGED
@@ -186,55 +186,7 @@ class PostmanSettingsRegistry {
186
  'temporaryDirectoryCallback',
187
  ), PostmanAdminController::ADVANCED_OPTIONS, PostmanAdminController::ADVANCED_SECTION );
188
 
189
- // Notifications
190
- add_settings_section( PostmanAdminController::NOTIFICATIONS_SECTION, _x( 'Notifications Settings', 'Configuration Section Title', 'post-smtp' ), array(
191
- $this,
192
- 'printNotificationsSectionInfo',
193
- ), PostmanAdminController::NOTIFICATIONS_OPTIONS );
194
-
195
- add_settings_field( PostmanOptions::NOTIFICATION_SERVICE, _x( 'Notification Service', 'Configuration Input Field', 'post-smtp' ), array(
196
- $this,
197
- 'notification_service_callback',
198
- ), PostmanAdminController::NOTIFICATIONS_OPTIONS, PostmanAdminController::NOTIFICATIONS_SECTION );
199
-
200
- // Pushover
201
- add_settings_section( 'pushover_credentials', _x( 'Pushover Credentials', 'Configuration Section Title', 'post-smtp' ), array(
202
- $this,
203
- 'printNotificationsSectionInfo',
204
- ), PostmanAdminController::NOTIFICATIONS_PUSHOVER_CRED );
205
-
206
- add_settings_field( PostmanOptions::PUSHOVER_USER, _x( 'Pushover User Key', 'Configuration Input Field', 'post-smtp' ), array(
207
- $this,
208
- 'pushover_user_callback',
209
- ), PostmanAdminController::NOTIFICATIONS_PUSHOVER_CRED, 'pushover_credentials' );
210
-
211
- add_settings_field( PostmanOptions::PUSHOVER_TOKEN, _x( 'Pushover App Token', 'Configuration Input Field', 'post-smtp' ), array(
212
- $this,
213
- 'pushover_token_callback',
214
- ), PostmanAdminController::NOTIFICATIONS_PUSHOVER_CRED, 'pushover_credentials' );
215
-
216
- // Slack
217
- add_settings_section( 'slack_credentials', _x( 'Slack Credentials', 'Configuration Section Title', 'post-smtp' ), array(
218
- $this,
219
- 'printNotificationsSectionInfo',
220
- ), PostmanAdminController::NOTIFICATIONS_SLACK_CRED );
221
-
222
- add_settings_field( PostmanOptions::SLACK_TOKEN, _x( 'Slack Webhook', 'Configuration Input Field', 'post-smtp' ), array(
223
- $this,
224
- 'slack_token_callback',
225
- ), PostmanAdminController::NOTIFICATIONS_SLACK_CRED, 'slack_credentials' );
226
-
227
- add_settings_field( PostmanOptions::NOTIFICATION_USE_CHROME, _x( 'Push to chrome extension', 'Configuration Input Field', 'post-smtp' ), array(
228
- $this,
229
- 'notification_use_chrome_callback',
230
- ), PostmanAdminController::NOTIFICATIONS_OPTIONS, PostmanAdminController::NOTIFICATIONS_SECTION );
231
-
232
- add_settings_field( PostmanOptions::NOTIFICATION_CHROME_UID, _x( 'Chrome Extension UID', 'Configuration Input Field', 'post-smtp' ), array(
233
- $this,
234
- 'notification_chrome_uid_callback',
235
- ), PostmanAdminController::NOTIFICATIONS_OPTIONS, PostmanAdminController::NOTIFICATIONS_SECTION );
236
-
237
- do_action( 'post_smtp_settings' );
238
  }
239
  }
240
 
@@ -442,48 +394,6 @@ class PostmanSettingsRegistry {
442
  printf( '</select><br/><span class="postman_input_description">%s</span>', $inputDescription );
443
  }
444
 
445
- public function notification_service_callback() {
446
- $inputDescription = __( 'Select the notification service you want to recieve alerts about failed emails.' );
447
-
448
- $options = apply_filters('post_smtp_notification_service', array(
449
- 'default' => __( 'WP Admin Email', 'post-smtp' ),
450
- 'pushover' => __( 'Pushover', 'post-smtp' ),
451
- 'slack' => __( 'Slack', 'post-smtp' ),
452
- ));
453
-
454
- printf( '<select id="input_%2$s" class="input_%2$s" name="%1$s[%2$s]">', PostmanOptions::POSTMAN_OPTIONS, PostmanOptions::NOTIFICATION_SERVICE );
455
- $currentKey = $this->options->getNotificationService();
456
-
457
- foreach ( $options as $key => $label ) {
458
- $this->printSelectOption( $label, $key, $currentKey );
459
- }
460
-
461
- printf( '</select><br/><span class="postman_input_description">%s</span>', $inputDescription );
462
- }
463
-
464
- public function notification_use_chrome_callback() {
465
- $value = $this->options->useChromeExtension();
466
- printf( '<input type="checkbox" id="input_%2$s" class="input_%2$s" name="%1$s[%2$s]" %3$s />', PostmanOptions::POSTMAN_OPTIONS, PostmanOptions::NOTIFICATION_USE_CHROME, $value ? 'checked="checked"' : '' );
467
- }
468
-
469
- public function notification_chrome_uid_callback() {
470
- printf( '<input type="password" id="input_%2$s" class="input_%2$s" name="%1$s[%2$s]" value="%3$s" />', PostmanOptions::POSTMAN_OPTIONS, PostmanOptions::NOTIFICATION_CHROME_UID, PostmanUtils::obfuscatePassword( $this->options->getNotificationChromeUid() ) );
471
- }
472
-
473
- public function pushover_user_callback() {
474
- printf( '<input type="password" id="pushover_user" name="%s[%s]" value="%s" />', PostmanOptions::POSTMAN_OPTIONS, PostmanOptions::PUSHOVER_USER, $this->options->getPushoverUser() );
475
- }
476
-
477
- public function pushover_token_callback() {
478
- printf( '<input type="password" id="pushover_token" name="%s[%s]" value="%s" />', PostmanOptions::POSTMAN_OPTIONS, PostmanOptions::PUSHOVER_TOKEN, $this->options->getPushoverToken() );
479
- }
480
-
481
- public function slack_token_callback() {
482
- printf( '<input type="password" id="slack_token" name="%s[%s]" value="%s" />', PostmanOptions::POSTMAN_OPTIONS, PostmanOptions::SLACK_TOKEN, $this->options->getSlackToken() );
483
- echo '<a target="_blank" href="https://slack.postmansmtp.com/">' . __( 'Get your webhook URL here', 'post-smtp' ) . '</a>';
484
-
485
- }
486
-
487
  private function printSelectOption( $label, $optionKey, $currentKey ) {
488
  $optionPattern = '<option value="%1$s" %2$s>%3$s</option>';
489
  printf( $optionPattern, $optionKey, $optionKey == $currentKey ? 'selected="selected"' : '', $label );
186
  'temporaryDirectoryCallback',
187
  ), PostmanAdminController::ADVANCED_OPTIONS, PostmanAdminController::ADVANCED_SECTION );
188
 
189
+ do_action( 'post_smtp_settings_fields' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
190
  }
191
  }
192
 
394
  printf( '</select><br/><span class="postman_input_description">%s</span>', $inputDescription );
395
  }
396
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
397
  private function printSelectOption( $label, $optionKey, $currentKey ) {
398
  $optionPattern = '<option value="%1$s" %2$s>%3$s</option>';
399
  printf( $optionPattern, $optionKey, $optionKey == $currentKey ? 'selected="selected"' : '', $label );
Postman/Postman-Email-Log/PostmanEmailLogService.php CHANGED
@@ -4,6 +4,7 @@ if ( ! defined( 'ABSPATH' ) ) {
4
  }
5
 
6
  require_once dirname(__DIR__ ) . '/PostmanLogFields.php';
 
7
 
8
  if ( ! class_exists( 'PostmanEmailLog' ) ) {
9
  class PostmanEmailLog {
@@ -56,6 +57,9 @@ if ( ! class_exists( 'PostmanEmailLogService' ) ) {
56
  */
57
  private function __construct() {
58
  $this->logger = new PostmanLogger( get_class( $this ) );
 
 
 
59
  }
60
 
61
  /**
@@ -69,6 +73,20 @@ if ( ! class_exists( 'PostmanEmailLogService' ) ) {
69
  return $inst;
70
  }
71
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  /**
73
  * Logs successful email attempts
74
  *
@@ -118,7 +136,6 @@ if ( ! class_exists( 'PostmanEmailLogService' ) ) {
118
 
119
  $options = PostmanOptions::getInstance();
120
 
121
- $this->checkForLogErrors( $log ,$message );
122
  $new_status = $log->statusMessage;
123
 
124
  if ( $options->is_fallback && empty( $log->statusMessage ) ) {
@@ -129,6 +146,8 @@ if ( ! class_exists( 'PostmanEmailLogService' ) ) {
129
  $new_status = '( ** Fallback ** ) ' . $log->statusMessage;
130
  }
131
 
 
 
132
  // nothing here is sanitized as WordPress should take care of
133
  // making database writes safe
134
  $my_post = array(
@@ -148,6 +167,7 @@ if ( ! class_exists( 'PostmanEmailLogService' ) ) {
148
  $message = $post_id->get_error_message();
149
 
150
  printf( '<div class="%1$s"><p>%2$s</p></div>', esc_attr( $class ), esc_html( $message ) );
 
151
  });
152
  }
153
 
@@ -187,50 +207,6 @@ if ( ! class_exists( 'PostmanEmailLogService' ) ) {
187
  $purger->truncateLogItems( PostmanOptions::getInstance()->getMailLoggingMaxEntries() );
188
  }
189
 
190
- private function checkForLogErrors( PostmanEmailLog $log, $postMessage ) {
191
- $message = __( 'You getting this message because an error detected while delivered your email.', 'post-smtp' );
192
- $message .= "\r\n" . sprintf( __( 'For the domain: %1$s','post-smtp' ), get_bloginfo('url') );
193
- $message .= "\r\n" . __( 'The log to paste when you open a support issue:', 'post-smtp' ) . "\r\n";
194
-
195
- if ( $log->statusMessage && ! empty( $log->statusMessage ) ) {
196
- require_once POST_SMTP_PATH . '/Postman/notifications/PostmanNotify.php';
197
-
198
- $message = $message . $log->statusMessage;
199
-
200
- $notification_service = PostmanOptions::getInstance()->getNotificationService();
201
- switch ($notification_service) {
202
- case 'default':
203
- $notifyer = new PostmanMailNotify;
204
- break;
205
- case 'pushover':
206
- $notifyer = new PostmanPushoverNotify;
207
- break;
208
- case 'slack':
209
- $notifyer = new PostmanSlackNotify;
210
- break;
211
- default:
212
- $notifyer = new PostmanMailNotify;
213
- }
214
-
215
- $notifyer = apply_filters( 'post_smtp_notifier', $notifyer, $notification_service );
216
-
217
- // Notifications
218
- $notify = new PostmanNotify( $notifyer );
219
- $notify->send($message, $log);
220
- $notify->push_to_chrome($log->statusMessage);
221
- }
222
-
223
- /**
224
- * @todo
225
- * After commented by me, check if it was needed.
226
- */
227
- preg_match_all( '/(.*)From/s', $log->sessionTranscript, $matches );
228
-
229
- if ( isset( $matches[1][0] ) && ! empty( $matches[1][0] ) && strpos( strtolower( $matches[1][0] ), 'error' ) !== false ) {
230
- $message = $message . $log->sessionTranscript;
231
- }
232
- }
233
-
234
  /**
235
  * Creates a Log object for use by writeToEmailLog()
236
  *
4
  }
5
 
6
  require_once dirname(__DIR__ ) . '/PostmanLogFields.php';
7
+ require_once POST_SMTP_PATH . '/Postman/Extensions/Core/Notifications/PostmanNotify.php';
8
 
9
  if ( ! class_exists( 'PostmanEmailLog' ) ) {
10
  class PostmanEmailLog {
57
  */
58
  private function __construct() {
59
  $this->logger = new PostmanLogger( get_class( $this ) );
60
+
61
+ add_action('post_smtp_on_success', array( $this, 'write_success_log' ), 10, 4 );
62
+ add_action('post_smtp_on_failed', array( $this, 'write_failed_log' ), 10, 5 );
63
  }
64
 
65
  /**
73
  return $inst;
74
  }
75
 
76
+ public function write_success_log($log, $message, $transcript, $transport) {
77
+ $options = PostmanOptions::getInstance();
78
+ if ( $options->getRunMode() == PostmanOptions::RUN_MODE_PRODUCTION || $options->getRunMode() == PostmanOptions::RUN_MODE_LOG_ONLY ) {
79
+ $this->writeSuccessLog( $log, $message, $transcript, $transport );
80
+ }
81
+ }
82
+
83
+ public function write_failed_log($log, $message, $transcript, $transport, $statusMessage) {
84
+ $options = PostmanOptions::getInstance();
85
+ if ( $options->getRunMode() == PostmanOptions::RUN_MODE_PRODUCTION || $options->getRunMode() == PostmanOptions::RUN_MODE_LOG_ONLY ) {
86
+ $this->writeFailureLog( $log, $message, $transcript, $transport, $statusMessage );
87
+ }
88
+ }
89
+
90
  /**
91
  * Logs successful email attempts
92
  *
136
 
137
  $options = PostmanOptions::getInstance();
138
 
 
139
  $new_status = $log->statusMessage;
140
 
141
  if ( $options->is_fallback && empty( $log->statusMessage ) ) {
146
  $new_status = '( ** Fallback ** ) ' . $log->statusMessage;
147
  }
148
 
149
+ $new_status = apply_filters( 'post_smtp_log_status', $new_status, $log, $message );
150
+
151
  // nothing here is sanitized as WordPress should take care of
152
  // making database writes safe
153
  $my_post = array(
167
  $message = $post_id->get_error_message();
168
 
169
  printf( '<div class="%1$s"><p>%2$s</p></div>', esc_attr( $class ), esc_html( $message ) );
170
+ return;
171
  });
172
  }
173
 
207
  $purger->truncateLogItems( PostmanOptions::getInstance()->getMailLoggingMaxEntries() );
208
  }
209
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
210
  /**
211
  * Creates a Log object for use by writeToEmailLog()
212
  *
Postman/Postman-Mail/PostmanMessage.php CHANGED
@@ -450,7 +450,13 @@ if ( ! class_exists( 'PostmanMessage' ) ) {
450
  break;
451
  case 'reply-to' :
452
  $this->logProcessHeader( 'Reply-To', $name, $content );
453
- $this->setReplyTo( $content );
 
 
 
 
 
 
454
  break;
455
  case 'sender' :
456
  $this->logProcessHeader( 'Sender', $name, $content );
450
  break;
451
  case 'reply-to' :
452
  $this->logProcessHeader( 'Reply-To', $name, $content );
453
+ $pattern = '/[a-z0-9_\-\+\.]+@[a-z0-9\-]+\.([a-z]{2,4})(?:\.[a-z]{2})?/i';
454
+ preg_match_all($pattern, $content, $matches);
455
+
456
+ if ( isset( $matches[0] ) && is_email( $matches[0] ) ) {
457
+ $this->setReplyTo( $content );
458
+ }
459
+
460
  break;
461
  case 'sender' :
462
  $this->logProcessHeader( 'Sender', $name, $content );
Postman/Postman.php CHANGED
@@ -70,6 +70,8 @@ class Postman {
70
  require_once 'Postman-Mail/PostmanMyMailConnector.php';
71
  require_once 'Postman-Mail/PostmanContactForm7.php';
72
  require_once 'Phpmailer/PostsmtpMailer.php';
 
 
73
  //require_once 'Postman-Mail/PostmanWooCommerce.php';
74
 
75
  // get plugin metadata - alternative to get_plugin_data
@@ -130,9 +132,6 @@ class Postman {
130
  // MyMail integration
131
  new PostmanMyMailConnector( $rootPluginFilenameAndPath );
132
 
133
- // Contact form 7
134
- new Postsmtp_ContactForm7;
135
-
136
  // WooCommerce Integration
137
  //new PostmanWoocommerce();
138
 
@@ -188,6 +187,9 @@ class Postman {
188
  * ref: http://codex.wordpress.org/Plugin_API/Action_Reference#Actions_Run_During_a_Typical_Request
189
  */
190
  public function on_plugins_loaded() {
 
 
 
191
  // load the text domain
192
  $this->loadTextDomain();
193
 
@@ -215,6 +217,7 @@ class Postman {
215
  * ref: https://codex.wordpress.org/Function_Reference/register_activation_hook
216
  */
217
  public function on_activation() {
 
218
  if ( $this->logger->isInfo() ) {
219
  $this->logger->info( 'Activating plugin' );
220
  }
70
  require_once 'Postman-Mail/PostmanMyMailConnector.php';
71
  require_once 'Postman-Mail/PostmanContactForm7.php';
72
  require_once 'Phpmailer/PostsmtpMailer.php';
73
+ require_once 'Extensions/License/PostmanLicenseManager.php';
74
+ require_once 'Extensions/Admin/PostmanAdmin.php';
75
  //require_once 'Postman-Mail/PostmanWooCommerce.php';
76
 
77
  // get plugin metadata - alternative to get_plugin_data
132
  // MyMail integration
133
  new PostmanMyMailConnector( $rootPluginFilenameAndPath );
134
 
 
 
 
135
  // WooCommerce Integration
136
  //new PostmanWoocommerce();
137
 
187
  * ref: http://codex.wordpress.org/Plugin_API/Action_Reference#Actions_Run_During_a_Typical_Request
188
  */
189
  public function on_plugins_loaded() {
190
+
191
+ PostmanLicenseManager::get_instance()->init();
192
+
193
  // load the text domain
194
  $this->loadTextDomain();
195
 
217
  * ref: https://codex.wordpress.org/Function_Reference/register_activation_hook
218
  */
219
  public function on_activation() {
220
+
221
  if ( $this->logger->isInfo() ) {
222
  $this->logger->info( 'Activating plugin' );
223
  }
Postman/PostmanAdminController.php CHANGED
@@ -55,10 +55,6 @@ if ( ! class_exists( 'PostmanAdminController' ) ) {
55
  const MULTISITE_SECTION = 'postman_multisite_section';
56
  const ADVANCED_OPTIONS = 'postman_advanced_options';
57
  const ADVANCED_SECTION = 'postman_advanced_section';
58
- const NOTIFICATIONS_OPTIONS = 'postman_notifications_options';
59
- const NOTIFICATIONS_SECTION = 'postman_notifications_section';
60
- const NOTIFICATIONS_PUSHOVER_CRED = 'postman_pushover_cred';
61
- const NOTIFICATIONS_SLACK_CRED = 'postman_slack_cred';
62
  const EMAIL_VALIDATION_SECTION = 'postman_email_validation_section';
63
  const EMAIL_VALIDATION_OPTIONS = 'postman_email_validation_options';
64
 
55
  const MULTISITE_SECTION = 'postman_multisite_section';
56
  const ADVANCED_OPTIONS = 'postman_advanced_options';
57
  const ADVANCED_SECTION = 'postman_advanced_section';
 
 
 
 
58
  const EMAIL_VALIDATION_SECTION = 'postman_email_validation_section';
59
  const EMAIL_VALIDATION_OPTIONS = 'postman_email_validation_options';
60
 
Postman/PostmanInputSanitizer.php CHANGED
@@ -73,16 +73,6 @@ if ( ! class_exists( 'PostmanInputSanitizer' ) ) {
73
  $this->sanitizeInt( 'Transcript Size', PostmanOptions::TRANSCRIPT_SIZE, $input, $new_input );
74
  $this->sanitizeString( 'Temporary Directory', PostmanOptions::TEMPORARY_DIRECTORY, $input, $new_input );
75
 
76
- // Notifications
77
- $this->sanitizeString( 'Pushover Service', PostmanOptions::NOTIFICATION_SERVICE, $input, $new_input, $this->options->getNotificationService() );
78
- $this->sanitizePassword( 'Pushover Username', PostmanOptions::PUSHOVER_USER, $input, $new_input, $this->options->getPushoverUser() );
79
- $this->sanitizePassword( 'Pushover Token', PostmanOptions::PUSHOVER_TOKEN, $input, $new_input, $this->options->getPushoverToken() );
80
- $this->sanitizePassword( 'Slack Token', PostmanOptions::SLACK_TOKEN, $input, $new_input, $this->options->getSlackToken() );
81
-
82
- // Chrome extension
83
- $this->sanitizeString( 'Push Chrome Extension', PostmanOptions::NOTIFICATION_USE_CHROME, $input, $new_input );
84
- $this->sanitizePassword( 'Push Chrome Extension UID', PostmanOptions::NOTIFICATION_CHROME_UID, $input, $new_input, $this->options->getNotificationChromeUid() );
85
-
86
  // Fallback
87
  $this->sanitizeString( 'Use fallback', PostmanOptions::FALLBACK_SMTP_ENABLED, $input, $new_input );
88
  $this->sanitizeString( 'Fallback hostname', PostmanOptions::FALLBACK_SMTP_HOSTNAME, $input, $new_input );
73
  $this->sanitizeInt( 'Transcript Size', PostmanOptions::TRANSCRIPT_SIZE, $input, $new_input );
74
  $this->sanitizeString( 'Temporary Directory', PostmanOptions::TEMPORARY_DIRECTORY, $input, $new_input );
75
 
 
 
 
 
 
 
 
 
 
 
76
  // Fallback
77
  $this->sanitizeString( 'Use fallback', PostmanOptions::FALLBACK_SMTP_ENABLED, $input, $new_input );
78
  $this->sanitizeString( 'Fallback hostname', PostmanOptions::FALLBACK_SMTP_HOSTNAME, $input, $new_input );
Postman/PostmanOptions.php CHANGED
@@ -109,12 +109,6 @@ if ( ! class_exists( 'PostmanOptions' ) ) {
109
  const TRANSCRIPT_SIZE = 'transcript_size';
110
  const TEMPORARY_DIRECTORY = 'tmp_dir';
111
  const DISABLE_EMAIL_VALIDAITON = 'disable_email_validation';
112
- const NOTIFICATION_SERVICE = 'notification_service';
113
- const NOTIFICATION_USE_CHROME = 'notification_use_chrome';
114
- const NOTIFICATION_CHROME_UID = 'notification_chrome_uid';
115
- const PUSHOVER_USER = 'pushover_user';
116
- const PUSHOVER_TOKEN = 'pushover_token';
117
- const SLACK_TOKEN = 'slack_token';
118
 
119
  // Fallback
120
  const FALLBACK_SMTP_ENABLED = 'fallback_smtp_enabled';
@@ -133,7 +127,6 @@ if ( ! class_exists( 'PostmanOptions' ) ) {
133
  const DEFAULT_MAIL_LOG_ENABLED = self::MAIL_LOG_ENABLED_OPTION_YES;
134
  const DEFAULT_MAIL_LOG_ENTRIES = 250;
135
  const DEFAULT_LOG_LEVEL = PostmanLogger::ERROR_INT;
136
- const DEFAULT_NOTIFICATION_SERVICE = 'default';
137
  const DEFAULT_TRANSPORT_TYPE = 'smtp'; // must match what's in PostmanSmtpModuleTransport
138
  const DEFAULT_TCP_READ_TIMEOUT = 60;
139
  const DEFAULT_TCP_CONNECTION_TIMEOUT = 10;
@@ -247,13 +240,6 @@ if ( ! class_exists( 'PostmanOptions' ) ) {
247
  } else { return self::DEFAULT_LOG_LEVEL; }
248
  }
249
 
250
- public function getNotificationService() {
251
- if ( isset( $this->options [ PostmanOptions::NOTIFICATION_SERVICE ] ) ) {
252
- return $this->options [ PostmanOptions::NOTIFICATION_SERVICE ];
253
- } else {
254
- return self::DEFAULT_NOTIFICATION_SERVICE;
255
- }
256
- }
257
 
258
  public function getForcedToRecipients() {
259
  if ( isset( $this->options [ self::FORCED_TO_RECIPIENTS ] ) ) {
@@ -481,36 +467,6 @@ if ( ! class_exists( 'PostmanOptions' ) ) {
481
  return $this->options [ PostmanOptions::MAILGUN_REGION ];
482
  }
483
  }
484
-
485
- public function getPushoverUser() {
486
- if ( isset( $this->options [ PostmanOptions::PUSHOVER_USER ] ) ) {
487
- return base64_decode( $this->options [ PostmanOptions::PUSHOVER_USER ] );
488
- }
489
- }
490
-
491
- public function getPushoverToken() {
492
- if ( isset( $this->options [ PostmanOptions::PUSHOVER_TOKEN ] ) ) {
493
- return base64_decode( $this->options [ PostmanOptions::PUSHOVER_TOKEN ] );
494
- }
495
- }
496
-
497
- public function getSlackToken() {
498
- if ( isset( $this->options [ PostmanOptions::SLACK_TOKEN ] ) ) {
499
- return base64_decode( $this->options [ PostmanOptions::SLACK_TOKEN ] );
500
- }
501
- }
502
-
503
- public function useChromeExtension() {
504
- if ( isset( $this->options [ PostmanOptions::NOTIFICATION_USE_CHROME ] ) ) {
505
- return $this->options [ PostmanOptions::NOTIFICATION_USE_CHROME ];
506
- }
507
- }
508
-
509
- public function getNotificationChromeUid() {
510
- if ( isset( $this->options [ PostmanOptions::NOTIFICATION_CHROME_UID ] ) ) {
511
- return base64_decode( $this->options [ PostmanOptions::NOTIFICATION_CHROME_UID ] );
512
- }
513
- }
514
 
515
  public function getReplyTo() {
516
  if ( isset( $this->options [ PostmanOptions::REPLY_TO ] ) ) {
109
  const TRANSCRIPT_SIZE = 'transcript_size';
110
  const TEMPORARY_DIRECTORY = 'tmp_dir';
111
  const DISABLE_EMAIL_VALIDAITON = 'disable_email_validation';
 
 
 
 
 
 
112
 
113
  // Fallback
114
  const FALLBACK_SMTP_ENABLED = 'fallback_smtp_enabled';
127
  const DEFAULT_MAIL_LOG_ENABLED = self::MAIL_LOG_ENABLED_OPTION_YES;
128
  const DEFAULT_MAIL_LOG_ENTRIES = 250;
129
  const DEFAULT_LOG_LEVEL = PostmanLogger::ERROR_INT;
 
130
  const DEFAULT_TRANSPORT_TYPE = 'smtp'; // must match what's in PostmanSmtpModuleTransport
131
  const DEFAULT_TCP_READ_TIMEOUT = 60;
132
  const DEFAULT_TCP_CONNECTION_TIMEOUT = 10;
240
  } else { return self::DEFAULT_LOG_LEVEL; }
241
  }
242
 
 
 
 
 
 
 
 
243
 
244
  public function getForcedToRecipients() {
245
  if ( isset( $this->options [ self::FORCED_TO_RECIPIENTS ] ) ) {
467
  return $this->options [ PostmanOptions::MAILGUN_REGION ];
468
  }
469
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
470
 
471
  public function getReplyTo() {
472
  if ( isset( $this->options [ PostmanOptions::REPLY_TO ] ) ) {
Postman/PostmanWpMail.php CHANGED
@@ -25,6 +25,8 @@ if ( ! class_exists( 'PostmanWpMail' ) ) {
25
  require_once 'Postman-Mail/PostmanMailEngine.php';
26
  require_once 'Postman-Auth/PostmanAuthenticationManagerFactory.php';
27
  require_once 'PostmanState.php';
 
 
28
  }
29
 
30
  /**
@@ -95,7 +97,7 @@ if ( ! class_exists( 'PostmanWpMail' ) ) {
95
  * @param mixed $headers
96
  * @param mixed $attachments
97
  */
98
- private function processWpMailCall( $to, $subject, $message, $headers, $attachments ) {
99
  $this->logger->trace( 'wp_mail parameters before applying WordPress wp_mail filter:' );
100
  $this->traceParameters( $to, $subject, $message, $headers, $attachments );
101
 
@@ -199,7 +201,6 @@ if ( ! class_exists( 'PostmanWpMail' ) ) {
199
  // apply the WordPress filters
200
  // may impact the from address, from email, charset and content-type
201
  $message->applyFilters();
202
- //do_action_ref_array( 'phpmailer_init', array( &$message ) );
203
 
204
  // create the body parts (if they are both missing)
205
  if ( $message->isBodyPartsEmpty() ) {
@@ -229,8 +230,13 @@ if ( ! class_exists( 'PostmanWpMail' ) ) {
229
  }
230
 
231
  $this->logger->debug( 'Sending mail' );
 
232
  // may throw an exception attempting to contact the SMTP server
233
- $engine->send( $message );
 
 
 
 
234
 
235
  // increment the success counter, unless we are just tesitng
236
  if ( ! $testMode ) {
@@ -241,10 +247,10 @@ if ( ! class_exists( 'PostmanWpMail' ) ) {
241
  // clean up
242
  $this->postSend( $engine, $startTime, $options, $transport );
243
 
244
- if ( $options->getRunMode() == PostmanOptions::RUN_MODE_PRODUCTION || $options->getRunMode() == PostmanOptions::RUN_MODE_LOG_ONLY ) {
245
- // log the successful delivery
246
- PostmanEmailLogService::getInstance()->writeSuccessLog( $log, $message, $engine->getTranscript(), $transport );
247
- }
248
 
249
  // return successful
250
  return true;
@@ -263,10 +269,11 @@ if ( ! class_exists( 'PostmanWpMail' ) ) {
263
  // clean up
264
  $this->postSend( $engine, $startTime, $options, $transport );
265
 
266
- if ( $options->getRunMode() == PostmanOptions::RUN_MODE_PRODUCTION || $options->getRunMode() == PostmanOptions::RUN_MODE_LOG_ONLY ) {
267
- // log the failed delivery
268
- PostmanEmailLogService::getInstance()->writeFailureLog( $log, $message, $engine->getTranscript(), $transport, $e->getMessage() );
269
- }
 
270
 
271
  // Fallback
272
  if ( $this->fallback( $log, $message, $options ) ) {
25
  require_once 'Postman-Mail/PostmanMailEngine.php';
26
  require_once 'Postman-Auth/PostmanAuthenticationManagerFactory.php';
27
  require_once 'PostmanState.php';
28
+
29
+ PostmanEmailLogService::getInstance();
30
  }
31
 
32
  /**
97
  * @param mixed $headers
98
  * @param mixed $attachments
99
  */
100
+ public function processWpMailCall( $to, $subject, $message, $headers, $attachments ) {
101
  $this->logger->trace( 'wp_mail parameters before applying WordPress wp_mail filter:' );
102
  $this->traceParameters( $to, $subject, $message, $headers, $attachments );
103
 
201
  // apply the WordPress filters
202
  // may impact the from address, from email, charset and content-type
203
  $message->applyFilters();
 
204
 
205
  // create the body parts (if they are both missing)
206
  if ( $message->isBodyPartsEmpty() ) {
230
  }
231
 
232
  $this->logger->debug( 'Sending mail' );
233
+
234
  // may throw an exception attempting to contact the SMTP server
235
+ if ( $send_email = apply_filters( 'post_smtp_do_send_email', true ) ) {
236
+ $engine->send($message);
237
+ } else {
238
+ $this->transcript = 'Bypassed By MailControl For Post SMTP';
239
+ }
240
 
241
  // increment the success counter, unless we are just tesitng
242
  if ( ! $testMode ) {
247
  // clean up
248
  $this->postSend( $engine, $startTime, $options, $transport );
249
 
250
+ /**
251
+ * Do stuff after successful delivery
252
+ */
253
+ do_action( 'post_smtp_on_success', $log, $message, $engine->getTranscript(), $transport );
254
 
255
  // return successful
256
  return true;
269
  // clean up
270
  $this->postSend( $engine, $startTime, $options, $transport );
271
 
272
+
273
+ /**
274
+ * Do stuff after failed delivery
275
+ */
276
+ do_action( 'post_smtp_on_failed', $log, $message, $engine->getTranscript(), $transport, $e->getMessage() );
277
 
278
  // Fallback
279
  if ( $this->fallback( $log, $message, $options ) ) {
Postman/notifications/PostmanNotify.php DELETED
@@ -1,42 +0,0 @@
1
- <?php
2
- if ( ! defined( 'ABSPATH' ) ) {
3
- exit; // Exit if accessed directly
4
- }
5
- require_once 'INotify.php';
6
- require_once 'PostmanMailNotify.php';
7
- require_once 'PostmanPushoverNotify.php';
8
- require_once 'PostmanSlackNotify.php';
9
-
10
- class PostmanNotify {
11
- private $notify;
12
-
13
- public function __construct( Postman_Notify $notify ) {
14
- $this->notify = $notify;
15
- }
16
-
17
- public function send( $message, $log ) {
18
- $this->notify->send_message( $message );
19
- }
20
-
21
- public function push_to_chrome($message) {
22
- $push_chrome = PostmanOptions::getInstance()->useChromeExtension();
23
-
24
- if ( $push_chrome ) {
25
- $uid = PostmanOptions::getInstance()->getNotificationChromeUid();
26
-
27
- if ( empty( $uid ) ) {
28
- return;
29
- }
30
-
31
- $url = 'https://postmansmtp.com/chrome/' . $uid;
32
-
33
- $args = array(
34
- 'body' => array(
35
- 'message' => $message
36
- )
37
- );
38
-
39
- $response = wp_remote_post( $url , $args );
40
- }
41
- }
42
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
postman-smtp.php CHANGED
@@ -6,7 +6,7 @@ if ( ! defined( 'ABSPATH' ) ) {
6
  * Plugin Name: Post SMTP
7
  * Plugin URI: https://wordpress.org/plugins/post-smtp/
8
  * Description: Email not reliable? Post SMTP is the first and only WordPress SMTP plugin to implement OAuth 2.0 for Gmail, Hotmail and Yahoo Mail. Setup is a breeze with the Configuration Wizard and integrated Port Tester. Enjoy worry-free delivery even if your password changes!
9
- * Version: 2.0.6
10
  * Author: Yehuda Hassine
11
  * Text Domain: post-smtp
12
  * Author URI: https://postmansmtp.com
@@ -44,10 +44,10 @@ if ( ! defined( 'ABSPATH' ) ) {
44
  define( 'POST_SMTP_BASE', __FILE__ );
45
  define( 'POST_SMTP_PATH', __DIR__ );
46
  define( 'POST_SMTP_URL', plugins_url('', POST_SMTP_BASE ) );
47
- define( 'POST_SMTP_VER', '2.0.6' );
48
  define( 'POST_SMTP_SHOW_RELEASE_MESSAGE', true );
49
- define( 'POST_SMTP_RELEASE_MESSAGE', 'If you see the implode error please read' );
50
- define( 'POST_SMTP_RELEASE_URL', 'https://postmansmtp.com/post-smtp-2-0-6-bug-fixes/' );
51
 
52
  $postman_smtp_exist = in_array( 'postman-smtp/postman-smtp.php', (array) get_option( 'active_plugins', array() ) );
53
  $required_php_version = version_compare( PHP_VERSION, '5.6.0', '<' );
6
  * Plugin Name: Post SMTP
7
  * Plugin URI: https://wordpress.org/plugins/post-smtp/
8
  * Description: Email not reliable? Post SMTP is the first and only WordPress SMTP plugin to implement OAuth 2.0 for Gmail, Hotmail and Yahoo Mail. Setup is a breeze with the Configuration Wizard and integrated Port Tester. Enjoy worry-free delivery even if your password changes!
9
+ * Version: 2.0.7
10
  * Author: Yehuda Hassine
11
  * Text Domain: post-smtp
12
  * Author URI: https://postmansmtp.com
44
  define( 'POST_SMTP_BASE', __FILE__ );
45
  define( 'POST_SMTP_PATH', __DIR__ );
46
  define( 'POST_SMTP_URL', plugins_url('', POST_SMTP_BASE ) );
47
+ define( 'POST_SMTP_VER', '2.0.7' );
48
  define( 'POST_SMTP_SHOW_RELEASE_MESSAGE', true );
49
+ define( 'POST_SMTP_RELEASE_MESSAGE', 'We have a new Facebook group, feel free to join.' );
50
+ define( 'POST_SMTP_RELEASE_URL', 'https://www.facebook.com/groups/post.smtp' );
51
 
52
  $postman_smtp_exist = in_array( 'postman-smtp/postman-smtp.php', (array) get_option( 'active_plugins', array() ) );
53
  $required_php_version = version_compare( PHP_VERSION, '5.6.0', '<' );
readme.txt CHANGED
@@ -3,8 +3,8 @@ Contributors: yehudah
3
  Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=yehuda@myinbox.in&item_name=Donation+for+PostSMTP
4
  Tags: postman smtp, postman, smtp, email, mail, mailer, email log, oauth2, gmail, google apps, hotmail, yahoo, mandrill api, sendgrid api, elastic email, office365, mailgun
5
  Requires at least: 3.9
6
- Tested up to: 5.2.2
7
- Stable tag: 2.0.6
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
@@ -12,9 +12,6 @@ Send, log and troubleshoot your Outgoing Email easily. Supports everything: SMTP
12
 
13
  == Description ==
14
 
15
- = Version 2.0.6 released - bug fixes. =
16
- [See here](https://postmansmtp.com/post-smtp-2-0-6-bug-fixes/)
17
-
18
  = The Only SMTP plugin with chrome Notifications =
19
  Get notified if your emails are failing inside your Chrome browser. [Download here](https://chrome.google.com/webstore/detail/post-smtp-notifications/npklmbkpbknkmbohdbpikeidiaekjoch?hl=en-US)
20
 
@@ -289,6 +286,10 @@ To avoid being flagged as spam, you need to prove your email isn't forged. On a
289
 
290
  == Changelog ==
291
 
 
 
 
 
292
  = 2.0.6 - 2019-10-08
293
  * Updated: Bug fixes.
294
 
3
  Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=yehuda@myinbox.in&item_name=Donation+for+PostSMTP
4
  Tags: postman smtp, postman, smtp, email, mail, mailer, email log, oauth2, gmail, google apps, hotmail, yahoo, mandrill api, sendgrid api, elastic email, office365, mailgun
5
  Requires at least: 3.9
6
+ Tested up to: 5.3.2
7
+ Stable tag: 2.0.7
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
12
 
13
  == Description ==
14
 
 
 
 
15
  = The Only SMTP plugin with chrome Notifications =
16
  Get notified if your emails are failing inside your Chrome browser. [Download here](https://chrome.google.com/webstore/detail/post-smtp-notifications/npklmbkpbknkmbohdbpikeidiaekjoch?hl=en-US)
17
 
286
 
287
  == Changelog ==
288
 
289
+ = 2.0.7 - 2020-01-12
290
+ * Updated: Improve PHPMailer method.
291
+ * Updated: Bug fixes.
292
+
293
  = 2.0.6 - 2019-10-08
294
  * Updated: Bug fixes.
295