loadOptions();
$this->proxyFix();
$this->configureSiteOnly();
add_action('init', array($this, 'loadTranslations'));
if ($this->allowFixer()) {
add_action('init', array($this, 'runFilters'), 4);
// filter script and stylesheet links
add_filter('script_loader_src', 'ssl_insecure_content_fix_url');
add_filter('style_loader_src', 'ssl_insecure_content_fix_url');
// filter uploads dir so that plugins using it to determine upload URL also work
add_filter('upload_dir', array(__CLASS__, 'uploadDir'));
// catch plugins / themes overriding the user's avatar and breaking it
add_filter('get_avatar', array($this, 'fixContent'), 9999);
// filter image links on front end e.g. in calls to wp_get_attachment_image(), wp_get_attachment_image_src(), etc.
if (!is_admin() || $this->isAjax()) {
add_filter('wp_get_attachment_url', 'ssl_insecure_content_fix_url', 100);
}
switch ($this->options['fix_level']) {
// handle Content fix level
case 'content':
add_filter('the_content', array($this, 'fixContent'), 9999); // also for fix_level 'widget'
add_filter('widget_text', array($this, 'fixContent'), 9999); // not for fix_level 'widget' (no need to duplicate effort)
break;
// handle Widget fix level
case 'widgets':
add_filter('the_content', array($this, 'fixContent'), 9999); // also for fix_level 'content'
add_action('dynamic_sidebar_before', array($this, 'fixWidgetsStart'), 9999, 2);
add_action('dynamic_sidebar_after', array($this, 'fixWidgetsEnd'), 9999, 2);
break;
// handle Capture fix level (excludes AJAX calls)
case 'capture':
if (!is_admin() && !$this->isAjax()) {
add_action('init', array($this, 'fixCaptureStart'), 5);
}
break;
// handle Capture All fix level (even AJAX calls)
case 'capture_all':
if (!is_admin() || $this->isAjaxNotExcluded()) {
add_action('init', array($this, 'fixCaptureStart'), 5);
}
break;
}
// handle some specific plugins
if (!empty($this->options['fix_specific'])) {
add_action('wp_print_styles', array($this, 'fixSpecific'), 100);
}
// filter WooCommerce cached widget ID if base site is not https
if (stripos(get_option('home'), 'http://') === 0) {
add_filter('woocommerce_cached_widget_id', array(__CLASS__, 'woocommerceWidgetID'));
}
// filter Gravity Forms confirmation content
add_filter('gform_confirmation', array($this, 'fixContent'));
// filter plugin Image Widget old-style image links
add_filter('image_widget_image_url', 'ssl_insecure_content_fix_url');
}
if (is_admin()) {
require SSLFIX_PLUGIN_ROOT . 'includes/class.SSLInsecureContentFixerAdmin.php';
new SSLInsecureContentFixerAdmin();
}
}
/**
* see whether the fixer should be run for this request
* @return bool
*/
protected function allowFixer() {
// do nothing if fixer is turned off
if ($this->options['fix_level'] === 'off') {
return false;
}
// don't mess with WooCommerce downloads
if (isset($_GET['download_file']) && isset($_GET['order']) && (isset($_GET['email']) || isset($_GET['uid']))) {
// but ensure that WooCommerce is active and will handle this request
if ($this->isPluginActive('woocommerce/woocommerce.php')) {
return false;
}
}
return is_ssl();
}
/**
* test whether a plugin is active
* @param string $plugin
* @return bool
*/
protected function isPluginActive($plugin) {
if (is_multisite()) {
$plugins = (array) get_site_option('active_sitewide_plugins', array());
if (isset($plugins[$plugin])) {
return true;
}
}
return in_array($plugin, (array) get_option('active_plugins', array()));
}
/**
* detect AJAX call
* @return bool
*/
protected function isAjax() {
if (function_exists('wp_doing_ajax')) {
$is_ajax = wp_doing_ajax();
}
else {
$is_ajax = defined('DOING_AJAX') && DOING_AJAX;
}
return $is_ajax;
}
/**
* exclude certain AJAX calls from capture_all
* @return bool
*/
protected function isAjaxNotExcluded() {
$is_ajax = $this->isAjax();
if ($is_ajax) {
$exclude = false;
if (!empty($_REQUEST['action'])) {
$exclude = in_array($_REQUEST['action'], array(
// some standard WordPress actions
'heartbeat',
// this plugin
'sslfix-test-https',
));
}
$is_ajax = !apply_filters('ssl_insecure_content_ajax_exclude', $exclude);
}
return $is_ajax;
}
/**
* run filters for plugins / themes that register domain exclusions
*/
public function runFilters() {
$domains = apply_filters('ssl_insecure_content_domain_exclusions', array());
if (!empty($domains) && is_array($domains)) {
$this->domain_exclusions = $domains;
}
}
/**
* load options for plugin
*/
protected function loadOptions() {
$defaults = array(
'fix_level' => 'simple',
'proxy_fix' => 'normal',
'fix_specific' => array(
'woo_https' => 1
),
);
if (is_multisite()) {
$this->network_options = get_site_option(SSLFIX_PLUGIN_OPTIONS, $defaults);
// use network-wide settings as default for individual sites
$defaults = $this->network_options;
}
$this->options = get_option(SSLFIX_PLUGIN_OPTIONS, $defaults);
}
/**
* check options for required proxy fix
*/
protected function proxyFix() {
// failsafe: allow website owners to force the proxy fix off, in case of conflicts
if (defined('SSLFIX_PLUGIN_NO_HTTPS_DETECT') && SSLFIX_PLUGIN_NO_HTTPS_DETECT) {
return;
}
if (!empty($this->options['proxy_fix'])) {
switch ($this->options['proxy_fix']) {
case 'HTTP_X_FORWARDED_PROTO':
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) === 'https') {
$_SERVER['HTTPS'] = 'on';
}
break;
case 'HTTP_X_FORWARDED_SSL':
if (isset($_SERVER['HTTP_X_FORWARDED_SSL']) && (strtolower($_SERVER['HTTP_X_FORWARDED_SSL']) === 'on' || $_SERVER['HTTP_X_FORWARDED_SSL'] === '1')) {
$_SERVER['HTTPS'] = 'on';
}
break;
case 'HTTP_CLOUDFRONT_FORWARDED_PROTO':
if (isset($_SERVER['HTTP_CLOUDFRONT_FORWARDED_PROTO']) && strtolower($_SERVER['HTTP_CLOUDFRONT_FORWARDED_PROTO']) === 'https') {
$_SERVER['HTTPS'] = 'on';
}
break;
case 'HTTP_CF_VISITOR':
if (isset($_SERVER['HTTP_CF_VISITOR']) && strpos($_SERVER['HTTP_CF_VISITOR'], 'https') !== false) {
$_SERVER['HTTPS'] = 'on';
}
break;
case 'HTTP_X_ARR_SSL':
if (!empty($_SERVER['HTTP_X_ARR_SSL'])) {
$_SERVER['HTTPS'] = 'on';
}
break;
case 'HTTP_X_FORWARDED_SCHEME':
if (!empty($_SERVER['HTTP_X_FORWARDED_SCHEME']) && strtolower($_SERVER['HTTP_X_FORWARDED_SCHEME']) === 'https') {
$_SERVER['HTTPS'] = 'on';
}
break;
case 'detect_fail':
// only force-enable https if site is set to run fully on https
if (stripos(get_option('siteurl'), 'https://') === 0) {
$_SERVER['HTTPS'] = 'on';
// add JavaScript detection of page protocol, and pray!
add_action('wp_print_scripts', array($this, 'scriptForceHTTPS'));
}
break;
}
}
if (!empty($this->options['fix_specific']['woo_https'])) {
// stop old WooCommerce versions from falsely detecting HTTPS from Google Chrome/Chromium
// @link https://woocommerce.wordpress.com/2015/07/07/woocommerce-2-3-13-security-and-maintenance-release/
// @link https://github.com/woothemes/woocommerce/issues/8479
// @link https://superuser.com/a/943989/473190
unset($_SERVER['HTTP_HTTPS']);
}
}
/**
* if Ignore External Sites is selected, record the http site URL for this website
*/
protected function configureSiteOnly() {
if (!empty($this->options['site_only'])) {
$this->process_only_site = site_url('', 'http');
}
}
/**
* load text translations
*/
public function loadTranslations() {
load_plugin_textdomain('ssl-insecure-content-fixer');
}
/**
* fix images, embeds, iframes in content
* @param string $content
* @return string
*/
public function fixContent($content) {
static $searches = array(
'#<(?:img|iframe) .*?src=[\'"]\Khttp://[^\'"]+#i', // fix image and iframe elements
'#]*href=[\'"]\Khttp://[^\'"]+#i', // fix link elements
'#