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 '#