summaryrefslogtreecommitdiff
path: root/Postman/Extensions
diff options
context:
space:
mode:
authoryehudah <yehudah@b8457f37-d9ea-0310-8a92-e5e31aec5664>2019-11-25 09:25:43 +0000
committeryehudah <yehudah@b8457f37-d9ea-0310-8a92-e5e31aec5664>2019-11-25 09:25:43 +0000
commitb5fd728e37aa2a9c7a5f37e8dead0a95117d541b (patch)
treea7158edc5e4e576e0377f078dabc87c699315c62 /Postman/Extensions
parentc61784411988d36d9bbd93cd3a97e773990af342 (diff)
downloadPost-SMTP-b5fd728e37aa2a9c7a5f37e8dead0a95117d541b.zip
phpmailer delivery improvments
bug fixes add option to disable notifications fix Invalid “Reply-To” e-mail address
Diffstat (limited to 'Postman/Extensions')
-rw-r--r--Postman/Extensions/Admin/PostmanAdmin.php31
-rw-r--r--Postman/Extensions/Admin/PostmanAdminView.php86
-rw-r--r--Postman/Extensions/Core/Notifications/INotify.php7
-rw-r--r--Postman/Extensions/Core/Notifications/PostmanMailNotify.php14
-rw-r--r--Postman/Extensions/Core/Notifications/PostmanNotify.php241
-rw-r--r--Postman/Extensions/Core/Notifications/PostmanNotifyOptions.php57
-rw-r--r--Postman/Extensions/Core/Notifications/PostmanPushoverNotify.php34
-rw-r--r--Postman/Extensions/Core/Notifications/PostmanSlackNotify.php39
-rw-r--r--Postman/Extensions/License/EDD_SL_Plugin_Updater.php585
-rw-r--r--Postman/Extensions/License/PostmanLicenseHandler.php422
-rw-r--r--Postman/Extensions/License/PostmanLicenseManager.php102
11 files changed, 1618 insertions, 0 deletions
diff --git a/Postman/Extensions/Admin/PostmanAdmin.php b/Postman/Extensions/Admin/PostmanAdmin.php
new file mode 100644
index 0000000..3b61a0a
--- /dev/null
+++ b/Postman/Extensions/Admin/PostmanAdmin.php
@@ -0,0 +1,31 @@
+<?php
+if ( ! defined( 'ABSPATH' ) ) exit;
+
+class PostmanAdmin {
+
+ public function __construct()
+ {
+ $PostmanLicenseManager = PostmanLicenseManager::get_instance();
+ $extensions = $PostmanLicenseManager->get_extensions();
+
+ if ( count( $extensions ) > 0 ) {
+ add_action('admin_menu', [ $this, 'add_menu' ], 20 );
+ }
+
+ }
+
+ public function add_menu() {
+ add_submenu_page(
+ PostmanViewController::POSTMAN_MENU_SLUG,
+ __('Extensions', 'post-smtp'),
+ __('Extensions', 'post-smtp'),
+ 'manage_options',
+ 'post-smtp-extensions',
+ [ $this, 'render_menu' ]
+ );
+ }
+
+ public function render_menu() {
+ include_once 'PostmanAdminView.php';
+ }
+}
diff --git a/Postman/Extensions/Admin/PostmanAdminView.php b/Postman/Extensions/Admin/PostmanAdminView.php
new file mode 100644
index 0000000..c9f0509
--- /dev/null
+++ b/Postman/Extensions/Admin/PostmanAdminView.php
@@ -0,0 +1,86 @@
+<?php if ( ! defined( 'ABSPATH' ) ) exit; ?>
+
+<style>
+ .form-table .row {
+ display: flex;
+ }
+
+ .form-table .row .flex > *:not(:last-child) {
+ margin-right: 5px;
+ }
+
+ .form-table .label {
+ align-self: center;
+ font-weight: bold;
+ }
+
+ .form-table .flex {
+ display: flex;
+ }
+
+ .form-table .flex input {
+ border-radius: 3px;
+ height: 30px;
+ margin: 0;
+ margin-left: 5px;
+ }
+
+ .form-table .flex button {
+ box-shadow: none;
+ height: 100%;
+ }
+</style>
+
+<div class="wrap">
+ <h1>Post SMTP Installed Extensions</h1>
+ <form action="" method="post">
+ <div class="form-table">
+ <?php
+ $PostmanLicenseManager = PostmanLicenseManager::get_instance();
+ $extensions = $PostmanLicenseManager->get_extensions();
+
+ foreach ( $extensions as $slug => $extension) :
+ $short_name = $extension['license_manager']->get_slug( $extension['plugin_data']['Name'] );
+ $nonce = $short_name . '_license_key-nonce';
+
+ $license_data = get_option( $short_name . '_license_active' );
+ $license_key = get_option( $short_name . '_license_key' );
+
+ $license_valid = is_object( $license_data ) && $license_data->license === 'valid';
+ $license_field_class = $license_valid ? 'readonly' : '';
+ $license_field_value = $license_valid ? base64_encode($license_key) : '';
+
+ wp_nonce_field( $nonce, $nonce );
+ ?>
+
+ <div class="row">
+ <div class="label">
+ <?php echo esc_html( $extension['plugin_data']['Name'] ); ?>
+ </div>
+
+ <div class="flex">
+ <div class="input">
+ <input <?php echo $license_field_class; ?>
+ type="password"
+ name="post_smtp_extension[<?php echo $short_name . '_license_key'; ?>]"
+ class="regular-text"
+ value="<?php echo $license_field_value; ?>"
+ placeholder="Serial Key">
+ </div>
+
+ <div class="buttons">
+ <?php if ( ! $license_valid ) :?>
+ <button type="submit" name="post_smtp_extension[<?php echo $short_name; ?>_activate]" class="button button-primary">Activate</button>
+ <?php endif; ?>
+
+ <button type="submit" name="post_smtp_extension[<?php echo $short_name; ?>_deactivate]" class="button button-secondary">Deactivate</button>
+ </div>
+ </div>
+
+ </div>
+
+ <?php endforeach; ?>
+
+ </div>
+ </form>
+</div>
diff --git a/Postman/Extensions/Core/Notifications/INotify.php b/Postman/Extensions/Core/Notifications/INotify.php
new file mode 100644
index 0000000..f40548d
--- /dev/null
+++ b/Postman/Extensions/Core/Notifications/INotify.php
@@ -0,0 +1,7 @@
+<?php
+if ( ! defined( 'ABSPATH' ) ) {
+ exit; // Exit if accessed directly
+}
+interface Postman_Notify {
+ public function send_message( $message );
+} \ No newline at end of file
diff --git a/Postman/Extensions/Core/Notifications/PostmanMailNotify.php b/Postman/Extensions/Core/Notifications/PostmanMailNotify.php
new file mode 100644
index 0000000..922c304
--- /dev/null
+++ b/Postman/Extensions/Core/Notifications/PostmanMailNotify.php
@@ -0,0 +1,14 @@
+<?php
+if ( ! defined( 'ABSPATH' ) ) {
+ exit; // Exit if accessed directly
+}
+class PostmanMailNotify implements Postman_Notify {
+
+ public function send_message($message)
+ {
+ $to_email = apply_filters( 'post_smtp_notify_email',get_bloginfo( 'admin_email' ) );
+ $domain = get_bloginfo( 'url' );
+
+ mail( $to_email, "{$domain}: " . __( 'Post SMTP email error', 'post-smtp' ), $message , '', "-f{$to_email}" );
+ }
+} \ No newline at end of file
diff --git a/Postman/Extensions/Core/Notifications/PostmanNotify.php b/Postman/Extensions/Core/Notifications/PostmanNotify.php
new file mode 100644
index 0000000..d9f6a58
--- /dev/null
+++ b/Postman/Extensions/Core/Notifications/PostmanNotify.php
@@ -0,0 +1,241 @@
+<?php
+if ( ! defined( 'ABSPATH' ) ) {
+ exit; // Exit if accessed directly
+}
+require_once 'INotify.php';
+require_once 'PostmanMailNotify.php';
+require_once 'PostmanPushoverNotify.php';
+require_once 'PostmanSlackNotify.php';
+require_once 'PostmanNotifyOptions.php';
+
+class PostmanNotify {
+
+ const NOTIFICATIONS_OPTIONS = 'postman_notifications_options';
+ const NOTIFICATIONS_SECTION = 'postman_notifications_section';
+ const NOTIFICATIONS_PUSHOVER_CRED = 'postman_pushover_cred';
+ const NOTIFICATIONS_SLACK_CRED = 'postman_slack_cred';
+
+ public function __construct() {
+
+ $this->options = new PostmanNotifyOptions();
+
+ add_filter( 'post_smtp_admin_tabs', array( $this, 'tabs' ) );
+ add_action( 'post_smtp_settings_menu', array( $this, 'menu' ) );
+ add_action( 'post_smtp_settings_fields', array( $this, 'settings' ) );
+ add_action( 'post_smtp_on_failed', array( $this, 'notify' ), 10, 5 );
+ add_filter( 'post_smtp_sanitize', array( $this, 'sanitize' ), 10, 3 );
+ }
+
+ public function menu() {
+ print '<section id="notifications">';
+ do_settings_sections( self::NOTIFICATIONS_OPTIONS );
+
+ $currentKey = $this->options->getNotificationService();
+ $pushover = $currentKey == 'pushover' ? 'block' : 'none';
+ $slack = $currentKey == 'slack' ? 'block' : 'none';
+
+ echo '<div id="pushover_cred" style="display: ' . $pushover . ';">';
+ do_settings_sections( self::NOTIFICATIONS_PUSHOVER_CRED );
+ echo '</div>';
+
+ echo '<div id="slack_cred" style="display: ' . $slack . ';">';
+ do_settings_sections( self::NOTIFICATIONS_SLACK_CRED );
+ echo '</div>';
+
+ do_action( 'post_smtp_notification_settings' );
+
+ print '</section>';
+ }
+
+ public function sanitize($new_input, $input, $sanitizer) {
+ // Notifications
+ $sanitizer->sanitizeString( 'Pushover Service', PostmanNotifyOptions::NOTIFICATION_SERVICE, $input, $new_input, $this->options->getNotificationService() );
+ $sanitizer->sanitizePassword( 'Pushover Username', PostmanNotifyOptions::PUSHOVER_USER, $input, $new_input, $this->options->getPushoverUser() );
+ $sanitizer->sanitizePassword( 'Pushover Token', PostmanNotifyOptions::PUSHOVER_TOKEN, $input, $new_input, $this->options->getPushoverToken() );
+ $sanitizer->sanitizePassword( 'Slack Token', PostmanNotifyOptions::SLACK_TOKEN, $input, $new_input, $this->options->getSlackToken() );
+
+ // Chrome extension
+ $sanitizer->sanitizeString( 'Push Chrome Extension', PostmanNotifyOptions::NOTIFICATION_USE_CHROME, $input, $new_input );
+ $sanitizer->sanitizePassword( 'Push Chrome Extension UID', PostmanNotifyOptions::NOTIFICATION_CHROME_UID, $input, $new_input, $this->options->getNotificationChromeUid() );
+
+ return $new_input;
+ }
+
+ public function tabs($tabs) {
+ $tabs['notifications'] = __( 'Notifications', 'post-smtp' );
+
+ return $tabs;
+ }
+
+ public function settings() {
+ // Notifications
+ add_settings_section( self::NOTIFICATIONS_SECTION, _x( 'Notifications Settings', 'Configuration Section Title', 'post-smtp' ), array(
+ $this,
+ 'printNotificationsSectionInfo',
+ ), self::NOTIFICATIONS_OPTIONS );
+
+ add_settings_field( PostmanNotifyOptions::NOTIFICATION_SERVICE, _x( 'Notification Service', 'Configuration Input Field', 'post-smtp' ), array(
+ $this,
+ 'notification_service_callback',
+ ), self::NOTIFICATIONS_OPTIONS, self::NOTIFICATIONS_SECTION );
+
+ // Pushover
+ add_settings_section( 'pushover_credentials', _x( 'Pushover Credentials', 'Configuration Section Title', 'post-smtp' ), array(
+ $this,
+ 'printNotificationsSectionInfo',
+ ), self::NOTIFICATIONS_PUSHOVER_CRED );
+
+ add_settings_field( PostmanNotifyOptions::PUSHOVER_USER, _x( 'Pushover User Key', 'Configuration Input Field', 'post-smtp' ), array(
+ $this,
+ 'pushover_user_callback',
+ ), self::NOTIFICATIONS_PUSHOVER_CRED, 'pushover_credentials' );
+
+ add_settings_field( PostmanNotifyOptions::PUSHOVER_TOKEN, _x( 'Pushover App Token', 'Configuration Input Field', 'post-smtp' ), array(
+ $this,
+ 'pushover_token_callback',
+ ), self::NOTIFICATIONS_PUSHOVER_CRED, 'pushover_credentials' );
+
+ // Slack
+ add_settings_section( 'slack_credentials', _x( 'Slack Credentials', 'Configuration Section Title', 'post-smtp' ), array(
+ $this,
+ 'printNotificationsSectionInfo',
+ ), self::NOTIFICATIONS_SLACK_CRED );
+
+ add_settings_field( PostmanNotifyOptions::SLACK_TOKEN, _x( 'Slack Webhook', 'Configuration Input Field', 'post-smtp' ), array(
+ $this,
+ 'slack_token_callback',
+ ), self::NOTIFICATIONS_SLACK_CRED, 'slack_credentials' );
+
+ add_settings_field( PostmanNotifyOptions::NOTIFICATION_USE_CHROME, _x( 'Push to chrome extension', 'Configuration Input Field', 'post-smtp' ), array(
+ $this,
+ 'notification_use_chrome_callback',
+ ), self::NOTIFICATIONS_OPTIONS, self::NOTIFICATIONS_SECTION );
+
+ add_settings_field( 'notification_chrome_uid', _x( 'Chrome Extension UID', 'Configuration Input Field', 'post-smtp' ), array(
+ $this,
+ 'notification_chrome_uid_callback',
+ ), self::NOTIFICATIONS_OPTIONS, self::NOTIFICATIONS_SECTION );
+ }
+
+ /**
+ * Print the Section text
+ */
+ public function printNotificationsSectionInfo() {
+ }
+
+ public function notification_service_callback() {
+ $inputDescription = __( 'Select the notification service you want to recieve alerts about failed emails.' );
+
+ $options = apply_filters('post_smtp_notification_service', array(
+ 'none' => __( 'None', 'post-smtp' ),
+ 'default' => __( 'WP Admin Email', 'post-smtp' ),
+ 'pushover' => __( 'Pushover', 'post-smtp' ),
+ 'slack' => __( 'Slack', 'post-smtp' ),
+ ));
+
+ printf( '<select id="input_%2$s" class="input_%2$s" name="%1$s[%2$s]">', 'postman_options', PostmanNotifyOptions::NOTIFICATION_SERVICE );
+ $currentKey = $this->options->getNotificationService();
+
+ foreach ( $options as $key => $label ) {
+ $this->printSelectOption( $label, $key, $currentKey );
+ }
+
+ printf( '</select><br/><span class="postman_input_description">%s</span>', $inputDescription );
+ }
+
+ public function notification_use_chrome_callback() {
+ $value = $this->options->useChromeExtension();
+ 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"' : '' );
+ }
+
+ public function notification_chrome_uid_callback() {
+ 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() ) );
+ }
+
+ public function pushover_user_callback() {
+ printf( '<input type="password" id="pushover_user" name="%s[%s]" value="%s" />', 'postman_options', PostmanNotifyOptions::PUSHOVER_USER, $this->options->getPushoverUser() );
+ }
+
+ public function pushover_token_callback() {
+ printf( '<input type="password" id="pushover_token" name="%s[%s]" value="%s" />', 'postman_options', PostmanNotifyOptions::PUSHOVER_TOKEN, $this->options->getPushoverToken() );
+ }
+
+ public function slack_token_callback() {
+ printf( '<input type="password" id="slack_token" name="%s[%s]" value="%s" />', 'postman_options', PostmanNotifyOptions::SLACK_TOKEN, $this->options->getSlackToken() );
+ echo '<a target="_blank" href="https://slack.postmansmtp.com/">' . __( 'Get your webhook URL here', 'post-smtp' ) . '</a>';
+
+ }
+
+ /**
+ * @param PostmanEmailLog $log
+ * @param PostmanMessage $message
+ * @param string $transcript
+ * @param PostmanTransport $transport
+ * @param string $errorMessage
+ */
+ public function notify ($log, $postmanMessage, $transcript, $transport, $errorMessage ) {
+ $message = __( 'You getting this message because an error detected while delivered your email.', 'post-smtp' );
+ $message .= "\r\n" . sprintf( __( 'For the domain: %1$s','post-smtp' ), get_bloginfo('url') );
+ $message .= "\r\n" . __( 'The log to paste when you open a support issue:', 'post-smtp' ) . "\r\n";
+
+ if ( $errorMessage && ! empty( $errorMessage ) ) {
+
+ $message = $message . $errorMessage;
+
+ $notification_service = PostmanNotifyOptions::getInstance()->getNotificationService();
+ switch ($notification_service) {
+ case 'none':
+ $notifyer = false;
+ break;
+ case 'default':
+ $notifyer = new PostmanMailNotify;
+ break;
+ case 'pushover':
+ $notifyer = new PostmanPushoverNotify;
+ break;
+ case 'slack':
+ $notifyer = new PostmanSlackNotify;
+ break;
+ default:
+ $notifyer = new PostmanMailNotify;
+ }
+
+ $notifyer = apply_filters('post_smtp_notifier', $notifyer, $notification_service);
+
+ // Notifications
+ if ( $notifyer ) {
+ $notifyer->send_message($message, $log);
+ }
+
+ $this->push_to_chrome($errorMessage);
+ }
+ }
+
+ public function push_to_chrome($message) {
+ $push_chrome = PostmanNotifyOptions::getInstance()->useChromeExtension();
+
+ if ( $push_chrome ) {
+ $uid = PostmanNotifyOptions::getInstance()->getNotificationChromeUid();
+
+ if ( empty( $uid ) ) {
+ return;
+ }
+
+ $url = 'https://postmansmtp.com/chrome/' . $uid;
+
+ $args = array(
+ 'body' => array(
+ 'message' => $message
+ )
+ );
+
+ $response = wp_remote_post( $url , $args );
+ }
+ }
+
+ private function printSelectOption( $label, $optionKey, $currentKey ) {
+ $optionPattern = '<option value="%1$s" %2$s>%3$s</option>';
+ printf( $optionPattern, $optionKey, $optionKey == $currentKey ? 'selected="selected"' : '', $label );
+ }
+}
+new PostmanNotify(); \ No newline at end of file
diff --git a/Postman/Extensions/Core/Notifications/PostmanNotifyOptions.php b/Postman/Extensions/Core/Notifications/PostmanNotifyOptions.php
new file mode 100644
index 0000000..08c27db
--- /dev/null
+++ b/Postman/Extensions/Core/Notifications/PostmanNotifyOptions.php
@@ -0,0 +1,57 @@
+<?php
+
+class PostmanNotifyOptions {
+
+ const DEFAULT_NOTIFICATION_SERVICE = 'default';
+ const NOTIFICATION_SERVICE = 'notification_service';
+ const NOTIFICATION_USE_CHROME = 'notification_use_chrome';
+ const NOTIFICATION_CHROME_UID = 'notification_chrome_uid';
+ const PUSHOVER_USER = 'pushover_user';
+ const PUSHOVER_TOKEN = 'pushover_token';
+ const SLACK_TOKEN = 'slack_token';
+
+ private $options;
+
+ public function __construct()
+ {
+ $this->options = get_option( 'postman_options' );
+ }
+
+ public function getNotificationService() {
+ if ( isset( $this->options [ self::NOTIFICATION_SERVICE ] ) ) {
+ return $this->options [ self::NOTIFICATION_SERVICE ];
+ } else {
+ return self::DEFAULT_NOTIFICATION_SERVICE;
+ }
+ }
+
+ public function getPushoverUser() {
+ if ( isset( $this->options [ self::PUSHOVER_USER ] ) ) {
+ return base64_decode( $this->options [ self::PUSHOVER_USER ] );
+ }
+ }
+
+ public function getPushoverToken() {
+ if ( isset( $this->options [ self::PUSHOVER_TOKEN ] ) ) {
+ return base64_decode( $this->options [ self::PUSHOVER_TOKEN ] );
+ }
+ }
+
+ public function getSlackToken() {
+ if ( isset( $this->options [ self::SLACK_TOKEN ] ) ) {
+ return base64_decode( $this->options [ self::SLACK_TOKEN ] );
+ }
+ }
+
+ public function useChromeExtension() {
+ if ( isset( $this->options [ self::NOTIFICATION_USE_CHROME ] ) ) {
+ return $this->options [ self::NOTIFICATION_USE_CHROME ];
+ }
+ }
+
+ public function getNotificationChromeUid() {
+ if ( isset( $this->options [ self::NOTIFICATION_CHROME_UID ] ) ) {
+ return base64_decode( $this->options [ self::NOTIFICATION_CHROME_UID ] );
+ }
+ }
+} \ No newline at end of file
diff --git a/Postman/Extensions/Core/Notifications/PostmanPushoverNotify.php b/Postman/Extensions/Core/Notifications/PostmanPushoverNotify.php
new file mode 100644
index 0000000..14ef7d2
--- /dev/null
+++ b/Postman/Extensions/Core/Notifications/PostmanPushoverNotify.php
@@ -0,0 +1,34 @@
+<?php
+if ( ! defined( 'ABSPATH' ) ) {
+ exit; // Exit if accessed directly
+}
+class PostmanPushoverNotify implements Postman_Notify {
+
+ public function send_message($message)
+ {
+ $options = PostmanOptions::getInstance();
+
+ $api_url = "https://api.pushover.net/1/messages.json";
+ $app_token = $options->getPushoverToken();
+ $user_key = $options->getPushoverUser();
+
+ $args = array(
+ 'body' => array(
+ "token" => $app_token,
+ "user" => $user_key,
+ "message" => $message,
+ )
+ );
+
+ $result = wp_remote_post( $api_url, $args );
+
+ if ( is_wp_error($result) ) {
+ error_log( __CLASS__ . ': ' . $result->get_error_message() );
+ }
+
+ $body = json_decode( wp_remote_retrieve_body( $result ), true );
+ if ( $body['status'] == 0 ) {
+ error_log( __CLASS__ . ': ' . print_r( $body, true ) );
+ }
+ }
+} \ No newline at end of file
diff --git a/Postman/Extensions/Core/Notifications/PostmanSlackNotify.php b/Postman/Extensions/Core/Notifications/PostmanSlackNotify.php
new file mode 100644
index 0000000..5b6fae3
--- /dev/null
+++ b/Postman/Extensions/Core/Notifications/PostmanSlackNotify.php
@@ -0,0 +1,39 @@
+<?php
+if ( ! defined( 'ABSPATH' ) ) {
+ exit; // Exit if accessed directly
+}
+class PostmanSlackNotify implements Postman_Notify {
+
+ public function send_message($message)
+ {
+ $options = PostmanOptions::getInstance();
+
+ $api_url = $options->getSlackToken();
+
+ $headers = array(
+ 'content-type' => 'application/json'
+ );
+
+ $body = array(
+ 'text' => $message
+ );
+
+ $args = array(
+ 'headers' => $headers,
+ 'body' => json_encode($body)
+ );
+
+ $result = wp_remote_post( $api_url, $args );
+
+ if ( is_wp_error($result) ) {
+ error_log( __CLASS__ . ': ' . $result->get_error_message() );
+ }
+
+ $code = wp_remote_retrieve_response_code( $result );
+ $message = wp_remote_retrieve_response_message( $result );
+
+ if ( $code != 200 && $message !== 'OK' ) {
+ error_log( __CLASS__ . ': ' . $message );
+ }
+ }
+} \ No newline at end of file
diff --git a/Postman/Extensions/License/EDD_SL_Plugin_Updater.php b/Postman/Extensions/License/EDD_SL_Plugin_Updater.php
new file mode 100644
index 0000000..0673834
--- /dev/null
+++ b/Postman/Extensions/License/EDD_SL_Plugin_Updater.php
@@ -0,0 +1,585 @@
+<?php
+
+// Exit if accessed directly
+if ( ! defined( 'ABSPATH' ) ) exit;
+
+/**
+ * Allows plugins to use their own update API.
+ *
+ * @author Easy Digital Downloads
+ * @version 1.6.19
+ */
+class EDD_SL_Plugin_Updater {
+
+ private $api_url = '';
+ private $api_data = array();
+ private $name = '';
+ private $slug = '';
+ private $version = '';
+ private $wp_override = false;
+ private $cache_key = '';
+
+ private $health_check_timeout = 5;
+
+ /**
+ * Class constructor.
+ *
+ * @uses plugin_basename()
+ * @uses hook()
+ *
+ * @param string $_api_url The URL pointing to the custom API endpoint.
+ * @param string $_plugin_file Path to the plugin file.
+ * @param array $_api_data Optional data to send with API calls.
+ */
+ public function __construct( $_api_url, $_plugin_file, $_api_data = null ) {
+
+ global $edd_plugin_data;
+
+ $this->api_url = trailingslashit( $_api_url );
+ $this->api_data = $_api_data;
+ $this->name = plugin_basename( $_plugin_file );
+ $this->slug = basename( $_plugin_file, '.php' );
+ $this->version = $_api_data['version'];
+ $this->wp_override = isset( $_api_data['wp_override'] ) ? (bool) $_api_data['wp_override'] : false;
+ $this->beta = ! empty( $this->api_data['beta'] ) ? true : false;
+ $this->cache_key = 'edd_sl_' . md5( serialize( $this->slug . $this->api_data['license'] . $this->beta ) );
+
+ $edd_plugin_data[ $this->slug ] = $this->api_data;
+
+ /**
+ * Fires after the $edd_plugin_data is setup.
+ *
+ * @since x.x.x
+ *
+ * @param array $edd_plugin_data Array of EDD SL plugin data.
+ */
+ do_action( 'post_edd_sl_plugin_updater_setup', $edd_plugin_data );
+
+ // Set up hooks.
+ $this->init();
+
+ }
+
+ /**
+ * Set up WordPress filters to hook into WP's update process.
+ *
+ * @uses add_filter()
+ *
+ * @return void
+ */
+ public function init() {
+
+ add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ) );
+ add_filter( 'plugins_api', array( $this, 'plugins_api_filter' ), 10, 3 );
+ remove_action( 'after_plugin_row_' . $this->name, 'wp_plugin_update_row', 10 );
+ add_action( 'after_plugin_row_' . $this->name, array( $this, 'show_update_notification' ), 10, 2 );
+ add_action( 'admin_init', array( $this, 'show_changelog' ) );
+
+ }
+
+ /**
+ * Check for Updates at the defined API endpoint and modify the update array.
+ *
+ * This function dives into the update API just when WordPress creates its update array,
+ * then adds a custom API call and injects the custom plugin data retrieved from the API.
+ * It is reassembled from parts of the native WordPress plugin update code.
+ * See wp-includes/update.php line 121 for the original wp_update_plugins() function.
+ *
+ * @uses api_request()
+ *
+ * @param array $_transient_data Update array build by WordPress.
+ * @return array Modified update array with custom plugin data.
+ */
+ public function check_update( $_transient_data ) {
+
+ global $pagenow;
+
+ if ( ! is_object( $_transient_data ) ) {
+ $_transient_data = new stdClass;
+ }
+
+ if ( 'plugins.php' == $pagenow && is_multisite() ) {
+ return $_transient_data;
+ }
+
+ if ( ! empty( $_transient_data->response ) && ! empty( $_transient_data->response[ $this->name ] ) && false === $this->wp_override ) {
+ return $_transient_data;
+ }
+
+ $version_info = $this->get_cached_version_info();
+
+ if ( false === $version_info ) {
+ $version_info = $this->api_request( 'plugin_latest_version', array( 'slug' => $this->slug, 'beta' => $this->beta ) );
+
+ $this->set_version_info_cache( $version_info );
+
+ }
+
+ if ( false !== $version_info && is_object( $version_info ) && isset( $version_info->new_version ) ) {
+
+ if ( version_compare( $this->version, $version_info->new_version, '<' ) ) {
+
+ $_transient_data->response[ $this->name ] = $version_info;
+
+ // Make sure the plugin property is set to the plugin's name/location. See issue 1463 on Software Licensing's GitHub repo.
+ $_transient_data->response[ $this->name ]->plugin = $this->name;
+
+ }
+
+ $_transient_data->last_checked = time();
+ $_transient_data->checked[ $this->name ] = $this->version;
+
+ }
+
+ return $_transient_data;
+ }
+
+ /**
+ * show update nofication row -- needed for multisite subsites, because WP won't tell you otherwise!
+ *
+ * @param string $file
+ * @param array $plugin
+ */
+ public function show_update_notification( $file, $plugin ) {
+
+ if ( is_network_admin() ) {
+ return;
+ }
+
+ if( ! current_user_can( 'update_plugins' ) ) {
+ return;
+ }
+
+ if( ! is_multisite() ) {
+ return;
+ }
+
+ if ( $this->name != $file ) {
+ return;
+ }
+
+ // Remove our filter on the site transient
+ remove_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ), 10 );
+
+ $update_cache = get_site_transient( 'update_plugins' );
+
+ $update_cache = is_object( $update_cache ) ? $update_cache : new stdClass();
+
+ if ( empty( $update_cache->response ) || empty( $update_cache->response[ $this->name ] ) ) {
+
+ $version_info = $this->get_cached_version_info();
+
+ if ( false === $version_info ) {
+ $version_info = $this->api_request( 'plugin_latest_version', array( 'slug' => $this->slug, 'beta' => $this->beta ) );
+
+ // Since we disabled our filter for the transient, we aren't running our object conversion on banners, sections, or icons. Do this now:
+ if ( isset( $version_info->banners ) && ! is_array( $version_info->banners ) ) {
+ $version_info->banners = $this->convert_object_to_array( $version_info->banners );
+ }
+
+ if ( isset( $version_info->sections ) && ! is_array( $version_info->sections ) ) {
+ $version_info->sections = $this->convert_object_to_array( $version_info->sections );
+ }
+
+ if ( isset( $version_info->icons ) && ! is_array( $version_info->icons ) ) {
+ $version_info->icons = $this->convert_object_to_array( $version_info->icons );
+ }
+
+ if ( isset( $version_info->icons ) && ! is_array( $version_info->icons ) ) {
+ $version_info->icons = $this->convert_object_to_array( $version_info->icons );
+ }
+
+ if ( isset( $version_info->contributors ) && ! is_array( $version_info->contributors ) ) {
+ $version_info->contributors = $this->convert_object_to_array( $version_info->contributors );
+ }
+
+ $this->set_version_info_cache( $version_info );
+ }
+
+ if ( ! is_object( $version_info ) ) {
+ return;
+ }
+
+ if ( version_compare( $this->version, $version_info->new_version, '<' ) ) {
+
+ $update_cache->response[ $this->name ] = $version_info;
+
+ }
+
+ $update_cache->last_checked = time();
+ $update_cache->checked[ $this->name ] = $this->version;
+
+ set_site_transient( 'update_plugins', $update_cache );
+
+ } else {
+
+ $version_info = $update_cache->response[ $this->name ];
+
+ }
+
+ // Restore our filter
+ add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ) );
+
+ if ( ! empty( $update_cache->response[ $this->name ] ) && version_compare( $this->version, $version_info->new_version, '<' ) ) {
+
+ // build a plugin list row, with update notification
+ $wp_list_table = _get_list_table( 'WP_Plugins_List_Table' );
+ # <tr class="plugin-update-tr"><td colspan="' . $wp_list_table->get_column_count() . '" class="plugin-update colspanchange">
+ echo '<tr class="plugin-update-tr" id="' . $this->slug . '-update" data-slug="' . $this->slug . '" data-plugin="' . $this->slug . '/' . $file . '">';
+ echo '<td colspan="3" class="plugin-update colspanchange">';
+ echo '<div class="update-message notice inline notice-warning notice-alt">';
+
+ $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' );
+
+ if ( empty( $version_info->download_link ) ) {
+ printf(
+ __( 'There is a new version of %1$s available. %2$sView version %3$s details%4$s.', 'easy-digital-downloads' ),
+ esc_html( $version_info->name ),
+ '<a target="_blank" class="thickbox" href="' . esc_url( $changelog_link ) . '">',
+ esc_html( $version_info->new_version ),
+ '</a>'
+ );
+ } else {
+ printf(
+ __( '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' ),
+ esc_html( $version_info->name ),
+ '<a target="_blank" class="thickbox" href="' . esc_url( $changelog_link ) . '">',
+ esc_html( $version_info->new_version ),
+ '</a>',
+ '<a href="' . esc_url( wp_nonce_url( self_admin_url( 'update.php?action=upgrade-plugin&plugin=' ) . $this->name, 'upgrade-plugin_' . $this->name ) ) .'">',
+ '</a>'
+ );
+ }
+
+ do_action( "in_plugin_update_message-{$file}", $plugin, $version_info );
+
+ echo '</div></td></tr>';
+ }
+ }
+
+ /**
+ * Updates information on the "View version x.x details" page with custom data.
+ *
+ * @uses api_request()
+ *
+ * @param mixed $_data
+ * @param string $_action
+ * @param object $_args
+ * @return object $_data
+ */
+ public function plugins_api_filter( $_data, $_action = '', $_args = null ) {
+
+ if ( $_action != 'plugin_information' ) {
+
+ return $_data;
+
+ }
+
+ if ( ! isset( $_args->slug ) || ( $_args->slug != $this->slug ) ) {
+
+ return $_data;
+
+ }
+
+ $to_send = array(
+ 'slug' => $this->slug,
+ 'is_ssl' => is_ssl(),
+ 'fields' => array(
+ 'banners' => array(),
+ 'reviews' => false,
+ 'icons' => array(),
+ )
+ );
+
+ $cache_key = 'edd_api_request_' . md5( serialize( $this->slug . $this->api_data['license'] . $this->beta ) );
+
+ // Get the transient where we store the api request for this plugin for 24 hours
+ $edd_api_request_transient = $this->get_cached_version_info( $cache_key );
+
+ //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.
+ if ( empty( $edd_api_request_transient ) ) {
+
+ $api_response = $this->api_request( 'plugin_information', $to_send );
+
+ // Expires in 3 hours
+ $this->set_version_info_cache( $api_response, $cache_key );
+
+ if ( false !== $api_response ) {
+ $_data = $api_response;
+ }
+
+ } else {
+ $_data = $edd_api_request_transient;
+ }
+
+ // Convert sections into an associative array, since we're getting an object, but Core expects an array.
+ if ( isset( $_data->sections ) && ! is_array( $_data->sections ) ) {
+ $_data->sections = $this->convert_object_to_array( $_data->sections );
+ }
+
+ // Convert banners into an associative array, since we're getting an object, but Core expects an array.
+ if ( isset( $_data->banners ) && ! is_array( $_data->banners ) ) {
+ $_data->banners = $this->convert_object_to_array( $_data->banners );
+ }
+
+ // Convert icons into an associative array, since we're getting an object, but Core expects an array.
+ if ( isset( $_data->icons ) && ! is_array( $_data->icons ) ) {
+ $_data->icons = $this->convert_object_to_array( $_data->icons );
+ }
+
+ // Convert contributors into an associative array, since we're getting an object, but Core expects an array.
+ if ( isset( $_data->contributors ) && ! is_array( $_data->contributors ) ) {
+ $_data->contributors = $this->convert_object_to_array( $_data->contributors );
+ }
+
+ if( ! isset( $_data->plugin ) ) {
+ $_data->plugin = $this->name;
+ }
+
+ return $_data;
+ }
+
+ /**
+ * Convert some objects to arrays when injecting data into the update API
+ *
+ * Some data like sections, banners, and icons are expected to be an associative array, however due to the JSON
+ * decoding, they are objects. This method allows us to pass in the object and return an associative array.
+ *
+ * @since 3.6.5
+ *
+ * @param stdClass $data
+ *
+ * @return array
+ */
+ private function convert_object_to_array( $data ) {
+ $new_data = array();
+ foreach ( $data as $key => $value ) {
+ $new_data[ $key ] = is_object( $value ) ? $this->convert_object_to_array( $value ) : $value;
+ }
+
+ return $new_data;
+ }
+
+ /**
+ * Disable SSL verification in order to prevent download update failures
+ *
+ * @param array $args
+ * @param string $url
+ * @return object $array
+ */
+ public function http_request_args( $args, $url ) {
+
+ $verify_ssl = $this->verify_ssl();
+ if ( strpos( $url, 'https://' ) !== false && strpos( $url, 'edd_action=package_download' ) ) {
+ $args['sslverify'] = $verify_ssl;
+ }
+ return $args;
+
+ }
+
+ /**
+ * Calls the API and, if successfull, returns the object delivered by the API.
+ *
+ * @uses get_bloginfo()
+ * @uses wp_remote_post()
+ * @uses is_wp_error()
+ *
+ * @param string $_action The requested action.
+ * @param array $_data Parameters for the API action.
+ * @return false|object
+ */
+ private function api_request( $_action, $_data ) {
+
+ global $wp_version, $edd_plugin_url_available;
+
+ $verify_ssl = $this->verify_ssl();
+
+ // Do a quick status check on this domain if we haven't already checked it.
+ $store_hash = md5( $this->api_url );
+ if ( ! is_array( $edd_plugin_url_available ) || ! isset( $edd_plugin_url_available[ $store_hash ] ) ) {
+ $test_url_parts = parse_url( $this->api_url );
+
+ $scheme = ! empty( $test_url_parts['scheme'] ) ? $test_url_parts['scheme'] : 'http';
+ $host = ! empty( $test_url_parts['host'] ) ? $test_url_parts['host'] : '';
+ $port = ! empty( $test_url_parts['port'] ) ? ':' . $test_url_parts['port'] : '';
+
+ if ( empty( $host ) ) {
+ $edd_plugin_url_available[ $store_hash ] = false;
+ } else {
+ $test_url = $scheme . '://' . $host . $port;
+ $response = wp_remote_get( $test_url, array( 'timeout' => $this->health_check_timeout, 'sslverify' => $verify_ssl ) );
+ $edd_plugin_url_available[ $store_hash ] = is_wp_error( $response ) ? false : true;
+ }
+ }
+
+ if ( false === $edd_plugin_url_available[ $store_hash ] ) {
+ return;
+ }
+
+ $data = array_merge( $this->api_data, $_data );
+
+ if ( $data['slug'] != $this->slug ) {
+ return;
+ }
+
+ if( $this->api_url == trailingslashit ( home_url() ) ) {
+ return false; // Don't allow a plugin to ping itself
+ }
+
+ $api_params = array(
+ 'edd_action' => 'get_version',
+ 'license' => ! empty( $data['license'] ) ? $data['license'] : '',
+ 'item_name' => isset( $data['item_name'] ) ? $data['item_name'] : false,
+ 'item_id' => isset( $data['item_id'] ) ? $data['item_id'] : false,
+ 'version' => isset( $data['version'] ) ? $data['version'] : false,
+ 'slug' => $data['slug'],
+ 'author' => $data['author'],
+ 'url' => home_url(),
+ 'beta' => ! empty( $data['beta'] ),
+ );
+
+ $request = wp_remote_post( $this->api_url, array( 'timeout' => 15, 'sslverify' => $verify_ssl, 'body' => $api_params ) );
+
+ if ( ! is_wp_error( $request ) ) {
+ $request = json_decode( wp_remote_retrieve_body( $request ) );
+ }
+
+ if ( $request && isset( $request->sections ) ) {
+ $request->sections = maybe_unserialize( $request->sections );
+ } else {
+ $request = false;
+ }
+
+ if ( $request && isset( $request->banners ) ) {
+ $request->banners = maybe_unserialize( $request->banners );
+ }
+
+ if ( $request && isset( $request->icons ) ) {
+ $request->icons = maybe_unserialize( $request->icons );
+ }
+
+ if( ! empty( $request->sections ) ) {
+ foreach( $request->sections as $key => $section ) {
+ $request->$key = (array) $section;
+ }
+ }
+
+ return $request;
+ }
+
+ public function show_changelog() {
+
+ global $edd_plugin_data;
+
+ if( empty( $_REQUEST['edd_sl_action'] ) || 'view_plugin_changelog' != $_REQUEST['edd_sl_action'] ) {
+ return;
+ }
+
+ if( empty( $_REQUEST['plugin'] ) ) {
+ return;
+ }
+
+ if( empty( $_REQUEST['slug'] ) ) {
+ return;
+ }
+
+ if( ! current_user_can( 'update_plugins' ) ) {
+ wp_die( __( 'You do not have permission to install plugin updates', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
+ }
+
+ $data = $edd_plugin_data[ $_REQUEST['slug'] ];
+ $beta = ! empty( $data['beta'] ) ? true : false;
+ $cache_key = md5( 'edd_plugin_' . sanitize_key( $_REQUEST['plugin'] ) . '_' . $beta . '_version_info' );
+ $version_info = $this->get_cached_version_info( $cache_key );
+
+ if( false === $version_info ) {
+
+ $api_params = array(
+ 'edd_action' => 'get_version',
+ 'item_name' => isset( $data['item_name'] ) ? $data['item_name'] : false,
+ 'item_id' => isset( $data['item_id'] ) ? $data['item_id'] : false,
+ 'slug' => $_REQUEST['slug'],
+ 'author' => $data['author'],
+ 'url' => home_url(),
+ 'beta' => ! empty( $data['beta'] )
+ );
+
+ $verify_ssl = $this->verify_ssl();
+ $request = wp_remote_post( $this->api_url, array( 'timeout' => 15, 'sslverify' => $verify_ssl, 'body' => $api_params ) );
+
+ if ( ! is_wp_error( $request ) ) {
+ $version_info = json_decode( wp_remote_retrieve_body( $request ) );
+ }
+
+
+ if ( ! empty( $version_info ) && isset( $version_info->sections ) ) {
+ $version_info->sections = maybe_unserialize( $version_info->sections );
+ } else {
+ $version_info = false;
+ }
+
+ if( ! empty( $version_info ) ) {
+ foreach( $version_info->sections as $key => $section ) {
+ $version_info->$key = (array) $section;
+ }
+ }
+
+ $this->set_version_info_cache( $version_info, $cache_key );
+
+ }
+
+ if( ! empty( $version_info ) && isset( $version_info->sections['changelog'] ) ) {
+ echo '<div style="background:#fff;padding:10px;">' . $version_info->sections['changelog'] . '</div>';
+ }
+
+ exit;
+ }
+
+ public function get_cached_version_info( $cache_key = '' ) {
+
+ if( empty( $cache_key ) ) {
+ $cache_key = $this->cache_key;
+ }
+
+ $cache = get_option( $cache_key );
+
+ if( empty( $cache['timeout'] ) || time() > $cache['timeout'] ) {
+ return false; // Cache is expired
+ }
+
+ // We need to turn the icons into an array, thanks to WP Core forcing these into an object at some point.
+ $cache['value'] = json_decode( $cache['value'] );
+ if ( ! empty( $cache['value']->icons ) ) {
+ $cache['value']->icons = (array) $cache['value']->icons;
+ }
+
+ return $cache['value'];
+
+ }
+
+ public function set_version_info_cache( $value = '', $cache_key = '' ) {
+
+ if( empty( $cache_key ) ) {
+ $cache_key = $this->cache_key;
+ }
+
+ $data = array(
+ 'timeout' => strtotime( '+3 hours', time() ),
+ 'value' => json_encode( $value )
+ );
+
+ update_option( $cache_key, $data, 'no' );
+
+ }
+
+ /**
+ * Returns if the SSL of the store should be verified.
+ *
+ * @since 1.6.13
+ * @return bool
+ */
+ private function verify_ssl() {
+ return (bool) apply_filters( 'edd_sl_api_request_verify_ssl', true, $this );
+ }
+
+}
diff --git a/Postman/Extensions/License/PostmanLicenseHandler.php b/Postman/Extensions/License/PostmanLicenseHandler.php
new file mode 100644
index 0000000..b24aa14
--- /dev/null
+++ b/Postman/Extensions/License/PostmanLicenseHandler.php
@@ -0,0 +1,422 @@
+<?php
+
+// Exit if accessed directly
+if ( ! defined( 'ABSPATH' ) ) exit;
+
+if ( ! class_exists( 'PostmanLicenseHandler' ) ) :
+
+
+class PostmanLicenseHandler {
+ private $file;
+ private $license;
+ private $license_data;
+ private $item_name;
+ private $item_id;
+ private $item_shortname;
+ private $version;
+ private $author;
+ private $api_url = 'http://localhost/psp/';
+
+
+ function __construct( $_file, $_item_name, $_version, $_author, $_optname = null, $_api_url = null, $_item_id = null ) {
+ $this->file = $_file;
+ $this->item_name = $_item_name;
+
+ if ( is_numeric( $_item_id ) ) {
+ $this->item_id = absint( $_item_id );
+ }
+
+ $this->item_shortname = $this->get_slug();
+ $this->version = $_version;
+ $this->license = trim( get_option( $this->item_shortname . '_license_key', '' ) );
+ $this->license_data = get_option( $this->item_shortname . '_license_active', '' );
+ $this->author = $_author;
+ $this->api_url = is_null( $_api_url ) ? $this->api_url : $_api_url;
+
+ /**
+ * Allows for backwards compatibility with old license options,
+ * i.e. if the plugins had license key fields previously, the license
+ * handler will automatically pick these up and use those in lieu of the
+ * user having to reactive their license.
+ */
+ if ( ! empty( $_optname ) ) {
+ $opt = get_option( $_optname, false );
+
+ if( isset( $opt ) && empty( $this->license ) ) {
+ $this->license = trim( $opt );
+ }
+ }
+
+ // Setup hooks
+ $this->includes();
+ $this->hooks();
+
+ }
+
+ /**
+ * Include the updater class
+ *
+ * @access private
+ * @return void
+ */
+ private function includes() {
+ if ( ! class_exists( 'EDD_SL_Plugin_Updater' ) ) {
+ require_once 'EDD_SL_Plugin_Updater.php';
+ }
+ }
+
+ /**
+ * Setup hooks
+ *
+ * @access private
+ * @return void
+ */
+ public function hooks() {
+
+ // Activate license key on settings save
+ add_action( 'admin_init', array( $this, 'activate_license' ) );
+
+ // Deactivate license key
+ add_action( 'admin_init', array( $this, 'deactivate_license' ) );
+
+ add_action( 'init', array( $this, 'cron' ), 20 );
+
+ // Check that license is valid once per week
+ add_action( 'admin_init', array( $this, 'validate_license' ) );
+
+ // Updater
+ add_action( 'admin_init', array( $this, 'auto_updater' ), 0 );
+
+ // Display notices to admins
+ add_action( 'admin_notices', array( $this, 'notices' ) );
+
+ add_action( 'in_plugin_update_message-' . plugin_basename( $this->file ), array( $this, 'plugin_row_license_missing' ), 10, 2 );
+ }
+
+ /**
+ * Auto updater
+ *
+ * @access private
+ * @return void
+ */
+ public function auto_updater() {
+
+ $args = array(
+ 'version' => $this->version,
+ 'license' => $this->license,
+ 'author' => $this->author,
+ 'beta' => function_exists( 'edd_extension_has_beta_support' ) && edd_extension_has_beta_support( $this->item_shortname ),
+ );
+
+ if( ! empty( $this->item_id ) ) {
+ $args['item_id'] = $this->item_id;
+ } else {
+ $args['item_name'] = $this->item_name;
+ }
+
+ // Setup the updater
+ $edd_updater = new EDD_SL_Plugin_Updater(
+ $this->api_url,
+ $this->file,
+ $args
+ );
+ }
+
+ public function cron() {
+ if ( ! wp_next_scheduled( $this->item_shortname . '_scheduled_events' ) ) {
+ wp_schedule_event( current_time( 'timestamp', true ), 'daily', $this->item_shortname . '_scheduled_events' );
+ }
+ }
+
+ public function get_slug() {
+ return preg_replace( '/[^a-zA-Z0-9_\s]/', '', str_replace( ' ', '_', strtolower( $this->item_name ) ) );
+ }
+
+ /**
+ * Display help text at the top of the Licenses tag
+ *
+ * @since 2.5
+ * @param string $active_tab
+ * @return void
+ */
+ public function license_help_text( $active_tab = '' ) {
+
+ static $has_ran;
+
+ if( 'licenses' !== $active_tab ) {
+ return;
+ }
+
+ if( ! empty( $has_ran ) ) {
+ return;
+ }
+
+ echo '<p>' . sprintf(
+ __( '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' ),
+ 'http://docs.easydigitaldownloads.com/article/1000-license-renewal'
+ ) . '</p>';
+
+ $has_ran = true;
+
+ }
+
+
+ /**
+ * Activate the license key
+ *
+ * @return void
+ */
+ public function activate_license() {
+
+ if ( ! isset( $_POST['post_smtp_extension'][ $this->item_shortname . '_activate'] ) ) {
+ return;
+ }
+
+ 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' ) ) {
+
+ return;
+
+ }
+
+ if ( ! current_user_can( 'manage_options' ) ) {
+ return;
+ }
+
+ if ( empty( $_POST['post_smtp_extension'][ $this->item_shortname . '_license_key'] ) ) {
+
+ delete_option( $this->item_shortname . '_license_active' );
+ delete_option( $this->item_shortname . '_license_key' );
+
+ return;
+
+ }
+
+ foreach ( $_POST as $key => $value ) {
+ if( false !== strpos( $key, 'license_key_deactivate' ) ) {
+ // Don't activate a key when deactivating a different key
+ return;
+ }
+ }
+
+ $details = get_option( $this->item_shortname . '_license_active' );
+
+ if ( is_object( $details ) && 'valid' === $details->license ) {
+ return;
+ }
+
+ $license = sanitize_text_field( $_POST['post_smtp_extension'][ $this->item_shortname . '_license_key'] );
+
+ if( empty( $license ) ) {
+ return;
+ }
+
+ // Data to send to the API
+ $api_params = array(
+ 'edd_action' => 'activate_license',
+ 'license' => $license,
+ 'item_name' => urlencode( $this->item_name ),
+ 'url' => home_url()
+ );
+
+ if ( ! empty( $this->item_id ) ) {
+ $api_params['item_id'] = $this->item_id;
+ }
+
+ // Call the API
+ $response = wp_remote_post(
+ $this->api_url,
+ array(
+ 'timeout' => 15,
+ 'sslverify' => false,
+ 'body' => $api_params
+ )
+ );
+
+ // Make sure there are no errors
+ if ( is_wp_error( $response ) ) {
+ return;
+ }
+
+ // Tell WordPress to look for updates
+ set_site_transient( 'update_plugins', null );
+
+ // Decode license data
+ $license_data = json_decode( wp_remote_retrieve_body( $response ) );
+
+ update_option( $this->item_shortname . '_license_active', $license_data );
+ update_option( $this->item_shortname . '_license_key', $license );
+
+ $slug = plugin_basename($this->file);
+ PostmanLicenseManager::get_instance()->add_extension($slug);
+
+ }
+
+
+ /**
+ * Deactivate the license key
+ *
+ * @return void
+ */
+ public function deactivate_license() {
+
+ if ( ! isset( $_POST['post_smtp_extension'][ $this->item_shortname . '_deactivate'] ) ) {
+ return;
+ }
+
+ if ( ! isset( $_POST['post_smtp_extension'][ $this->item_shortname . '_license_key'] ) )
+ return;
+
+ if( ! wp_verify_nonce( $_REQUEST[ $this->item_shortname . '_license_key-nonce'], $this->item_shortname . '_license_key-nonce' ) ) {
+
+ wp_die( __( 'Nonce verification failed', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
+
+ }
+
+ if( ! current_user_can( 'manage_options' ) ) {
+ return;
+ }
+
+ $license_key = sanitize_text_field( base64_decode( $_POST['post_smtp_extension'][ $this->item_shortname . '_license_key'] ) );
+
+ // Run on deactivate button press
+ // Data to send to the API
+ $api_params = array(
+ 'edd_action' => 'deactivate_license',
+ 'license' => $license_key,
+ 'item_name' => urlencode( $this->item_name ),
+ 'url' => home_url()
+ );
+
+ if ( ! empty( $this->item_id ) ) {
+ $api_params['item_id'] = $this->item_id;
+ }
+
+ // Call the API
+ $response = wp_remote_post(
+ $this->api_url,
+ array(
+ 'timeout' => 15,
+ 'sslverify' => false,
+ 'body' => $api_params
+ )
+ );
+
+ // Make sure there are no errors
+ if ( is_wp_error( $response ) ) {
+ return;
+ }
+
+ // Decode the license data
+ $license_data = json_decode( wp_remote_retrieve_body( $response ) );
+
+ delete_option( $this->item_shortname . '_license_active' );
+ delete_option( $this->item_shortname . '_license_key' );
+
+ $slug = plugin_basename($this->file);
+ PostmanLicenseManager::get_instance()->remove_extension($slug);
+
+ }
+
+ public function validate_license() {
+ if ( false === ( $cron_data = get_transient( $this->item_shortname . '_cron' ) ) ) {
+ $this->license_check();
+
+ set_transient( $this->item_shortname . '_cron', true, rand( 12, 48 ) * HOUR_IN_SECONDS );
+ }
+ }
+
+
+ /**
+ * Check if license key is valid once per week
+ *
+ * @since 2.5
+ * @return void
+ */
+ public function license_check() {
+
+ // data to send in our API request
+ $api_params = array(
+ 'edd_action'=> 'check_license',
+ 'license' => $this->license,
+ 'item_name' => urlencode( $this->item_name ),
+ 'url' => home_url()
+ );
+
+ // Call the API
+ $response = wp_remote_post(
+ $this->api_url,
+ array(
+ 'timeout' => 15,
+ 'sslverify' => false,
+ 'body' => $api_params
+ )
+ );
+
+ // make sure the response came back okay
+ if ( is_wp_error( $response ) ) {
+ return false;
+ }
+
+ $license_data = json_decode( wp_remote_retrieve_body( $response ) );
+
+ update_option( $this->item_shortname . '_license_active', $license_data );
+
+ }
+
+
+ /**
+ * Admin notices for errors
+ *
+ * @return void
+ */
+ public function notices() {
+
+ $showed_invalid_message = null;
+
+ if( empty( $this->license ) ) {
+ return;
+ }
+
+ if( ! current_user_can( 'manage_options' ) ) {
+ return;
+ }
+
+ $messages = array();
+
+ $license = get_option( $this->item_shortname . '_license_active' );
+
+ if( is_object( $license ) && 'valid' !== $license->license && empty( $showed_invalid_message ) ) {
+
+ if( isset( $_GET['page'] ) && 'post-smtp-extensions' === $_GET['page'] ) {
+
+ $messages[] = sprintf(
+ __( '%s has invalid or expired license key for Post SMTP.'),
+ '<strong>' . $this->item_name . '</strong>'
+ );
+
+ $showed_invalid_message = true;
+
+ }
+
+ }
+
+ if( ! empty( $messages ) ) {
+
+ foreach( $messages as $message ) {
+
+ echo '<div class="error">';
+ echo '<p>' . $message . '</p>';
+ echo '</div>';
+
+ }
+
+ }
+
+ }
+
+ public function is_licensed() {
+ return is_object($this->license_data) && 'valid' === $this->license_data->license;
+ }
+}
+
+endif; // end class_exists check
diff --git a/Postman/Extensions/License/PostmanLicenseManager.php b/Postman/Extensions/License/PostmanLicenseManager.php
new file mode 100644
index 0000000..280b564
--- /dev/null
+++ b/Postman/Extensions/License/PostmanLicenseManager.php
@@ -0,0 +1,102 @@
+<?php
+if ( ! defined( 'ABSPATH' ) ) exit;
+
+class PostmanLicenseManager {
+
+ const ENDPOINT = 'https://postmansmtp.com';
+
+ const CORE_EXTENSIONS = [ 'gmail_api', 'sendgrid_api', 'mandrill_api', 'mailgun_api' ];
+
+ private $extensions;
+
+ private $rand_cache_interval = 12;
+
+ private static $instance;
+
+ public static function get_instance() {
+ if ( ! self::$instance ) {
+ self::$instance = new static();
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * PostmanLicenseManager constructor.
+ */
+ private function __construct()
+ {
+ $this->includes();
+ $this->rand_cache_interval = rand( 1, 24 );
+
+ add_filter( 'extra_plugin_headers', [ $this, 'add_extension_headers' ] );
+ }
+
+ public function includes() {
+ include_once 'PostmanLicenseHandler.php';
+
+ include_once ABSPATH . '/wp-admin/includes/plugin.php';
+
+ }
+
+
+ function add_extension_headers($headers) {
+ $headers[] = 'Class';
+ $headers[] = 'Slug';
+
+ return $headers;
+ }
+
+ /**
+ * Init
+ */
+ public function init() {
+
+ $plugins = get_plugins();
+ foreach ( $plugins as $plugin_dir_and_filename => $plugin_data ) {
+
+ if ( ! is_plugin_active( $plugin_dir_and_filename ) ) {
+ continue;
+ }
+
+ if ( false !== strpos( $plugin_dir_and_filename, 'post-smtp-extension' ) ) {
+ $slug = $plugin_dir_and_filename;
+ $class = $plugin_data['Class'];
+ $plugin_path = WP_CONTENT_DIR . '/plugins/' . $plugin_dir_and_filename;
+
+ $this->extensions[$slug]['plugin_data'] = $plugin_data;
+ $this->extensions[$slug]['plugin_dir_and_filename'] = $plugin_dir_and_filename;
+ $this->extensions[$slug]['license_manager'] = new PostmanLicenseHandler(
+ $plugin_path, $plugin_data['Name'],
+ $plugin_data['Version'], $plugin_data['Author']
+ );
+ if ( $this->extensions[$slug]['license_manager']->is_licensed() ) {
+ include_once $plugin_path;
+
+ $this->extensions[$slug]['instance'] = new $class;
+ }
+ }
+ }
+
+ if ( ! empty( $this->extensions ) ) {
+ new PostmanAdmin();
+ }
+ }
+
+ public function add_extension($slug) {
+ $plugin_path = WP_CONTENT_DIR . '/plugins/' . $this->extensions[$slug]['plugin_dir_and_filename'];
+ $class = $this->extensions[$slug]['plugin_data']['Class'];
+
+ include_once $plugin_path;
+ $this->extensions[$slug]['instance'] = new $class;
+ }
+
+ public function remove_extension($slug) {
+ $this->extensions[$slug]['instance'] = null;
+ unset($this->extensions[$slug]['instance']);
+ }
+
+ public function get_extensions() {
+ return $this->extensions;
+ }
+} \ No newline at end of file