both_hooks(); /** Define public hooks. */ $this->public_hooks(); /** Define admin hooks. */ $this->admin_hooks(); } /** * Define hooks that runs on both the front-end and the dashboard. * * @since 1.0.0 * @access private * * @return void **/ private function both_hooks() { } /** * Register all the hooks related to the public-facing functionality. * * @since 1.0.0 * @access private * * @return void **/ private function public_hooks() { /** Work only on frontend area. */ if ( is_admin() ) { return; } /** Load CSS Styles for Frontend Area. */ add_action( 'wp_enqueue_scripts', [$this, 'styles' ] ); // CSS. /** Load JavaScript for Frontend Area. */ add_action( 'wp_enqueue_scripts', [$this, 'scripts'] ); // JS. /** JavaScript Required. */ add_action( 'wp_footer', [$this, 'javascript_required'] ); /** We need Sessions */ add_action( 'init', [ Helper::class, 'start_session' ], 1 ); add_action( 'wp_logout', [ Helper::class, 'end_session' ] ); add_action( 'wp_login', [ Helper::class, 'end_session' ] ); /** Run one of selected algorithm. */ $this->run_algorithm(); } /** * Register all the hooks related to the admin area functionality. * * @since 1.0.0 * @access private * * @return void **/ private function admin_hooks() { /** Work only in admin area. */ if ( ! is_admin() ) { return; } /** * The adBlock extensions blocks us in admin area too, so we add scripts and styles inline. **/ /** Remove unity css and JS. */ add_action( 'admin_enqueue_scripts', [$this, 'dequeue_unity_assets'], 1000 ); /** Add inline JS and CSS for Backend Area. */ add_action( 'admin_footer', [ $this, 'admin_styles' ], 1000 ); add_action( 'admin_footer', [ $this, 'admin_scripts' ], 1000 ); } /** * Protect site if JavaScript is Disabled. * * @since 2.0.0 * @access public **/ public function javascript_required() { /** Custom JavaScript is not allowed in AMP. */ if ( function_exists( 'is_amp_endpoint' ) && is_amp_endpoint() ) { return; } /** Checks if plugin should work on this page. */ if ( ! TabAssignments::get_instance()->display() ) { return; } /** Get plugin settings */ $options = Settings::get_instance()->options; if ( 'on' !== $options['javascript'] ) { return; } ?> display() ) { return; } /** Add custom CSS. */ echo sprintf( '', Settings::get_instance()->options['custom_css'] ); } /** * Add JavaScript for the public-facing side of the site. * * @return void * @since 1.0.0 **/ public function scripts() { /** Checks if plugin should work on this page. */ if ( ! TabAssignments::get_instance()->display() ) { return; } wp_enqueue_script( 'pp-AdUnlocker-ads', Plugin::get_url() . 'js/ads' . Plugin::get_suffix() . '.js', [], Plugin::get_version(), true ); } /** * Remove unity css and JS. * * @since 3.0.0 * @access private * * @return void **/ public function dequeue_unity_assets() { /** Dequeue CSS. */ wp_dequeue_style( 'pp-plugins' ); wp_dequeue_style( 'pp-AdUnlocker-ui' ); wp_dequeue_style( 'pp-AdUnlocker-unity-admin' ); wp_dequeue_style( 'pp-AdUnlocker-admin' ); wp_dequeue_style( 'pp-AdUnlocker-plugin-install' ); /** Dequeue JS. */ wp_dequeue_script( 'pp-AdUnlocker-ui' ); wp_dequeue_script( 'pp-AdUnlocker-unity-admin' ); wp_dequeue_script( 'pp-AdUnlocker-admin' ); wp_dequeue_script( 'pp-plugins' ); wp_dequeue_script( 'pp-AdUnlocker-assignments' ); } /** * Add JS for admin area. * * @since 3.0.0 * @access public * * @return void **/ public function admin_scripts() { /** Get current screen to add styles on specific pages. */ $screen = get_current_screen(); if ( null === $screen ) { return; } /** Plugin Settings Page. */ if ( in_array( $screen->base, Plugin::get_menu_bases(), true ) ) { ?> base ) { ?> base, Plugin::get_menu_bases(), true ) ) { ?> base ) && isset( $_GET[ 'plugin' ] ) && $_GET[ 'plugin' ] === 'AdUnlocker' ) { ?>base ) { ?>options['algorithm']; if ( 'inline' === $algorithm ) { $this->inline_algorithm(); } elseif ( 'random-folder' === $algorithm ) { $this->random_folder_algorithm(); /** Proxies all scripts. */ } elseif ( 'proxy' === $algorithm ) { $this->proxy_algorithm(); } } /** * Pass variables to JS. * * @return void * @since 2.0.2 **/ public function localize_AdUnlocker() { /** Get plugin settings. */ $options = Settings::get_instance()->options; wp_localize_script( 'pp-AdUnlocker', 'ppAdUnlocker', [ 'style' => $options['style'], 'timeout' => $options['timeout'], 'closeable' => $options['closeable'], 'title' => $options['title'], 'content' => $options['content'], 'bg_color' => $options['bg_color'], 'modal_color' => $options['modal_color'], 'text_color' => $options['text_color'], 'blur' => $options['blur'], 'prefix' => $this->generate_random_name(), ] ); } /** * Add obfuscated inline script on page. * Very fast. But low reliability. * * @since 2.0.1 * @access private * @return void **/ private function inline_algorithm() { /** Load inline JavaScript for Frontend Area. */ add_action( 'wp_footer', [$this, 'footer_scripts'], mt_rand( 1, 60 ) ); // JS. } /** * Load inline JavaScript for Frontend Area. * * @return void * @since 1.0.0 **/ public function footer_scripts() { /** Checks if plugin should work on this page. */ if ( ! TabAssignments::get_instance()->display() ) { return; } /** * Get Randomized Script. * @noinspection PhpIncludeInspection **/ $js = require Plugin::get_path() . 'src/Core/AdUnlocker/AdUnlockerJS.php'; ?>inline_algorithm(); return; } /** Is it time to generate a new folder? */ $opt_name = 'pp_AdUnlocker_random_folder_generated'; $generated = get_transient( $opt_name ); $uploads_dir = wp_upload_dir(); /** Generate new random folder. */ if ( false === $generated ) { $options = Settings::get_instance()->options; /** Get the name of the old folder. */ $fake_folder = get_option( 'pp_AdUnlocker_random_folder_fake_folder' ); /** Remove old folder. */ if ( $fake_folder ) { Helper::get_instance()->remove_directory( $uploads_dir['basedir'] . '/' . $fake_folder ); } /** Create new folder with scripts. */ $this->random_folder_create_fake_folder(); /** Regenerate after 24 hours. */ set_transient( $opt_name, 'true', intval( $options[ 'lifetime' ] ) * DAY_IN_SECONDS ); // 24 hours } /** Load JavaScript for Frontend Area. */ add_action( 'wp_enqueue_scripts', [$this, 'random_folder_algorithm_scripts'] ); // JS. } /** * Add JavaScript for the public-facing side of the site. * For random folder algorithm. * * @return void * @since 2.0.1 **/ public function random_folder_algorithm_scripts() { /** Checks if plugin should work on this page. */ if ( ! TabAssignments::get_instance()->display() ) { return; } $folder_name = get_option( 'pp_AdUnlocker_random_folder_fake_folder' ); $file_name = get_option( 'pp_AdUnlocker_random_folder_fake_file' ); $uploads_dir = wp_upload_dir(); /** If script file not exist. */ if ( ! file_exists( $uploads_dir['basedir'] . '/' . $folder_name . '/' . $file_name ) ) { /** Create new folder with scripts. */ $this->random_folder_create_fake_folder(); return; } wp_enqueue_script( 'pp-AdUnlocker', $uploads_dir['baseurl'] . '/' . $folder_name . '/' . $file_name, [], Plugin::get_version(), true ); $this->localize_AdUnlocker(); } /** * Create random folder and random script file. * * @since 2.0.1 * @access private * @return void **/ private function random_folder_create_fake_folder() { /** Create Random Names. */ $folder = $this->generate_random_name(); $file = $this->generate_random_name(); $file .= '.js'; $uploads_dir = wp_upload_dir(); /** Create Folder. */ if ( ! wp_mkdir_p($concurrent_directory = $uploads_dir['basedir'] . '/' . $folder ) && ! is_dir( $concurrent_directory ) ) { throw new RuntimeException( sprintf( 'Directory "%s" was not created', $concurrent_directory ) ); } /** Create script min File. */ /** * Get Randomized Script. * @noinspection PhpIncludeInspection **/ $AdUnlocker_script = require Plugin::get_path() . 'src/Core/AdUnlocker/AdUnlockerJS.php'; file_put_contents( $uploads_dir['basedir'] . '/' . $folder . '/' . $file, $AdUnlocker_script ); /** Remember folder and script name. */ update_option( 'pp_AdUnlocker_random_folder_fake_folder', $folder ); update_option( 'pp_AdUnlocker_random_folder_fake_file', $file ); } /** * Return random alphanumeric name. * * @since 2.0.1 * @access private * @return string **/ public function generate_random_name(): string { $permitted_chars = 'abcdefghijklmnopqrstuvwxyz'; /** Prepare random parts. */ $part_1 = substr( str_shuffle( $permitted_chars ), 0, mt_rand( 4, 8 ) ); $part_2 = substr( str_shuffle( $permitted_chars ), 0, mt_rand( 4, 8 ) ); /** Add random dash. */ $dash = ''; if ( mt_rand( 0, 1 ) ) { $dash = '-'; } /** Add random wp. */ $wp = ''; if ( mt_rand( 0, 1 ) ) { $wp = 'wp-'; } return $wp . $part_1 . $dash . $part_2; } /** * Most powerful algorithm. Proxies all scripts and is randomly added self to the end of some one. * The disadvantages include a slight slowdown in loading and unstable operation with some caching systems. * * @since 2.0.1 * @access private * @return void **/ private function proxy_algorithm() { /** Let's try to saddle any other script to avoid blocking. */ add_action( 'wp_print_scripts', [$this, 'list_scripts'], PHP_INT_MAX ); /** Return proxied scripts. */ add_action( 'template_redirect', [$this, 'do_stuff_on_404'] ); } /** * Let's try to saddle any other script to avoid blocking. * * @since 2.0.0 * @access private * @return void **/ public function list_scripts() { global $wp_scripts; /** Checks if plugin should work on this page. */ if ( ! TabAssignments::get_instance()->display() ) { return; } /** Prepare relative paths to plugins and themes. */ $rel_plugin_path = str_replace( ABSPATH, '', WP_PLUGIN_DIR ); $rel_theme_path = str_replace( ABSPATH, '', get_theme_root() ); /** Create MD5 hashes. */ $md5_plugin_path = md5( $rel_plugin_path ); $md5_theme_path = md5( $rel_theme_path ); /** Select random script. */ $victim = $this->get_random_script_number( $wp_scripts->queue ); /** Replace paths to MD5 hashes. */ foreach( $wp_scripts->queue as $key => $handle ) { /** Remember victims. */ if ( $victim === $key ) { $_SESSION['pp_AdUnlocker_victim'] = $wp_scripts->registered[$handle]->src; } /** We send all scripts through our handler. */ $wp_scripts->registered[$handle]->src = str_replace( $rel_plugin_path, $md5_plugin_path, $wp_scripts->registered[$handle]->src ); $wp_scripts->registered[$handle]->src = str_replace( $rel_theme_path, $md5_theme_path, $wp_scripts->registered[$handle]->src ); } } private function get_random_script_number( $queue ) { global $wp_scripts; if ( empty( $queue ) ) { return 0; } /** Let's remove our own script. */ if ( ( $key = array_search( 'pp-AdUnlocker-ads', $queue, true ) ) !== false ) { unset( $queue[ $key ] ); } /** Name of victim. */ $victim_name = $queue[array_rand( $queue )]; /** Key of victim. */ $key = array_search( $victim_name, $wp_scripts->queue, true ); return $key; } /** * Return proxied scripts. * * @since 2.0.0 * @access private * * @return void **/ public function do_stuff_on_404() { global $wp; /** We are interested in requests for nonexistent files. */ if ( ! is_404() ) { return; } /** We are interested in js files. */ if ( 'js' !== strtolower( pathinfo( $wp->request, PATHINFO_EXTENSION ) ) ) { return; } /** Prepare relative paths to plugins and themes. */ $rel_plugin_path = str_replace( ABSPATH, '', WP_PLUGIN_DIR ); $rel_theme_path = str_replace( ABSPATH, '', get_theme_root() ); /** Create MD5 hashes. */ $md5_plugin_path = md5( $rel_plugin_path ); $md5_theme_path = md5( $rel_theme_path ); /** Reverse replace MD5 to path. */ $url = $wp->request; $url = str_replace( [$md5_plugin_path, $md5_theme_path], [$rel_plugin_path, $rel_theme_path], $url ); /** Path to script. */ $script_path = ABSPATH . $url; $add = false; if ( isset( $_SESSION['pp_AdUnlocker_victim'] ) ) { $victim = strval($_SESSION['pp_AdUnlocker_victim']); if ( strpos( $victim, $url ) !== false ) { $add = true; } } /** Return script. */ if ( file_exists( $script_path ) ) { header( 'HTTP/1.1 200 OK' ); header( 'Content-Type: application/javascript' ); echo self::get_js_contents( $script_path, $add ); die(); } } /** * Return js file and add AdUnlocker scripts. * * @param $path * @param bool $add * * @return false|string **@since 2.0.0 * @access public */ public static function get_js_contents( $path, bool $add = false ) { $js = file_get_contents( $path ); if ( $add ) { /** * Get Randomized Script. * @noinspection PhpIncludeInspection **/ $d_js = require Plugin::get_path() . 'src/Core/AdUnlocker/AdUnlockerJS.php'; $js .= $d_js; } return $js; } /** * This method used in register_activation_hook * Everything written here will be done during plugin activation. * * @since 1.0.0 * @access public */ public function activation_hook() { /** Activation hook */ } /** * Main Caster Instance. * Insures that only one instance of Caster exists in memory at any one time. * * @static * @since 1.0.0 * @access public * * @return Caster **/ public static function get_instance(): Caster { if ( ! isset( self::$instance ) && ! ( self::$instance instanceof self ) ) { self::$instance = new self; } return self::$instance; } }