<?php

/**
 * @package WP Encryption
 *
 * @author     WP Encryption
 * @copyright  Copyright (C) 2019-2024, WP Encryption. All Rights Reserved.
 * @link       https://wpencryption.com
 * @since      Class available since Release 5.7.0
 *
 */
class WPLE_DeepScanner {
    private $permalinks_list = [];

    private $insecure_links_within_posts = [];

    private $permalink_vs_mxresource = [];

    private $permalink_vs_inlinemx = [];

    private $widget_issues = [];

    private $merged_widget_issues = [];

    public function __construct() {
        $this->get_all_permalinks();
        if ( !empty( $this->permalinks_list ) ) {
            foreach ( $this->permalinks_list as $PID => $link ) {
                $excludes = [
                    'shop',
                    'cart',
                    'checkout',
                    'my-account'
                ];
                foreach ( $excludes as $exclude ) {
                    if ( stripos( $link, $exclude ) !== false ) {
                        continue 2;
                    }
                }
                $webpage = $this->retrieve_content( $link );
                $this->parse_content_for_http_links( $PID, $link, $webpage );
                $this->find_inline_insecure_items( $PID, $link, $webpage );
            }
        }
        //$this->p($this->insecure_links_within_posts);
        $this->find_widgets_insecure_items();
        //$this->p($this->widget_issues);
        ///$this->p($this->merged_widget_issues);
        //$this->p($this->permalink_vs_mxresource);
        //$this->p($this->permalink_vs_inlinemx);
        //Table starts
        if ( empty( $this->permalink_vs_mxresource ) ) {
            delete_option( 'wple_mixed_issues' );
            echo 'success';
            exit;
        }
        //have issues
        update_option( 'wple_mixed_issues', 1 );
        $table = '<table id="wple-advanced-scanner">
    <th>Type</th>
    <th>Insecure URL<br><small>URLs that needs updating to <strong>https://</strong></small></th>
    <th>Source File<br><small>Where it\'s coming from?</small></th>';
        foreach ( $this->permalink_vs_mxresource as $ID => $research ) {
            if ( empty( $research['mx_resources'] ) ) {
                continue;
            }
            $table .= '<tr>
      <td colspan="3" class="wple-scan-head">Analyzed Page URL: ' . esc_url( $research['webpage'] ) . ' (ID=' . (int) $ID . ')</td>      
      </tr>';
            $issue_found = false;
            foreach ( $research['mx_resources'] as $key => $data ) {
                if ( empty( $data ) || count( $data ) == 1 && false !== stripos( $data[0], '/svg' ) ) {
                    continue;
                }
                if ( $key == 'secure_css' || $key == 'secure_js' ) {
                    //insecure items within secure files
                    $issue_found = true;
                    foreach ( $data as $key => $files_w_issue ) {
                        $table .= '<tr>
          <td class="issue_type">' . esc_html__( 'Insecure links within css/js files', 'wp-letsencrypt-ssl' ) . '</td>';
                        $rcount = 1;
                        foreach ( $files_w_issue['issues'] as $key => $items ) {
                            $table .= '<td>http://' . implode( "<br>http://", $items ) . '</td>';
                            $rcount++;
                        }
                        $table .= '<td class="wple-tooltip" data-tippy="Find & fix these insecure urls via Appearance ~ Theme Editor">' . esc_url( $files_w_issue['resource'] ) . '</td>';
                        $table .= '</tr>';
                    }
                } else {
                    $tds = '';
                    $tdcount = 1;
                    foreach ( $data as $resource ) {
                        if ( stripos( $resource, '/svg' ) ) {
                            continue;
                        }
                        $issue_found = true;
                        $issue_location = '';
                        $issue_tooltip = 'Try updating Site & WordPress urls to https:// protocol in Settings > General to resolve this issue. Likewise, this could be coming from your active theme files or active plugin files.';
                        if ( in_array( $resource, $this->merged_widget_issues ) ) {
                            $issue_location = '<a href="' . admin_url( "widgets.php" ) . '" target="_blank">WIDGET</a>';
                            $issue_tooltip = 'Update this insecure url via Appearance > Widgets on left sidebar';
                        }
                        if ( isset( $this->insecure_links_within_posts[$ID] ) ) {
                            foreach ( $this->insecure_links_within_posts[$ID] as $type => $items ) {
                                if ( in_array( $resource, $items ) ) {
                                    $ID = (int) $ID;
                                    $issue_location = '<a href="' . admin_url( "post.php?post={$ID}&action=edit" ) . '" target="_blank">POST CONTENT</a>';
                                    $issue_tooltip = 'Update this insecure url via Edit Post';
                                }
                            }
                        }
                        if ( isset( $this->permalink_vs_inlinemx[$ID] ) ) {
                            foreach ( $this->permalink_vs_inlinemx[$ID]['mx_resources'] as $indx => $arr ) {
                                if ( in_array( $resource, $arr ) ) {
                                    $issue_location = 'INLINE STYLE / SCRIPT';
                                    $issue_tooltip = 'This issue is found within webpage html and might be coming from custom css / js section of your active theme or plugins.';
                                }
                            }
                        }
                        $tds .= '<tr><td>' . esc_url( $resource ) . '</td><td class="wple-tooltip" data-tippy="' . esc_attr( $issue_tooltip ) . '">' . $issue_location . '</td></tr>';
                        $tdcount++;
                    }
                    if ( $tdcount > 1 ) {
                        $table .= '<tr>
            <td rowspan="' . $tdcount . '" class="issue_type">' . esc_html( $key ) . '</td>
            ' . $tds . '          
            </tr>';
                    }
                }
            }
            if ( !$issue_found ) {
                $table .= '<tr>
            <td colspan="3" class="issue_type">Great!.. No mixed content issues found.</td>       
            </tr>';
            }
        }
        $table .= '</table>';
        echo wp_kses_post( $table );
    }

    private function get_all_permalinks() {
        global $wpdb;
        $ptypes_query = array();
        $args = array(
            'public' => true,
        );
        $ptypes = get_post_types( $args );
        foreach ( $ptypes as $post_type ) {
            if ( $post_type == 'product' ) {
                //dont include products
                continue;
            }
            $ptypes_query[] = " post_type = '" . $post_type . "'";
        }
        $sql = implode( " OR ", $ptypes_query );
        $sql = "SELECT ID, post_content FROM {$wpdb->posts} where post_status='publish' and (" . $sql . ") LIMIT 25";
        $res = $wpdb->get_results( $sql );
        if ( !empty( $res ) ) {
            foreach ( $res as $item ) {
                if ( @(!in_array( get_permalink( $item->ID ), $this->permalinks_list )) ) {
                    $this->permalinks_list[$item->ID] = get_permalink( $item->ID );
                }
            }
            $this->locate_insecure_items_in_posts( $res );
        }
    }

    private function locate_insecure_items_in_posts( $results ) {
        $url_pattern = '([\\w.,@?^=%&:\\/~+#-]*[\\w@?^=%&\\/~+#-]?)(?:[\'|\\"])';
        $image_pattern = '([\\w.,@?^=%&:\\/~+#-]*[\\w@?^=%&\\/~+#-]?[.jpg|.gif|.jpeg|.png|.svg])(?:((\\?.*[\'|"])|[\'|"]))';
        $script_pattern = '([\\w.,@?^=%&:\\/~+#-]*[\\w@?^=%&\\/~+#-]?[.js])(?:((\\?.*[\'|\\"])|[\'|\\"]))';
        $style_pattern = '([\\w.,@?^=%&:\\/~+#-]*[\\w@?^=%&\\/~+#-]?[.css])(?:((\\?.*[\'|\\"])|[\'|\\"]))';
        $patterns = array(
            'inline_css' => '/url\\([\'"]?\\K(http:\\/\\/)()' . $image_pattern . '/i',
            'link'       => '/<link[^>].*?href=[\'"]\\K(http:\\/\\/)()' . $style_pattern . '/i',
            'meta'       => '/<meta property="og:image" .*?content=[\'"]\\K(http:\\/\\/)()' . $image_pattern . '/i',
            'img'        => '/<(?:img)[^>].*?src=[\'"]\\K(http:\\/\\/)()' . $image_pattern . '/i',
            'iframe'     => '/<(?:iframe)[^>].*?src=[\'"]\\K(http:\\/\\/)()' . $url_pattern . '/i',
            'script'     => '/<script[^>]*?src=[\'"]\\K(http:\\/\\/)()' . $script_pattern . '/i',
            'form'       => '/<form[^>]*?action=[\'"]\\K(http:\\/\\/)()' . $url_pattern . '/i',
            'inline_js'  => '/"url":"\\K(http:\\/\\/)()' . $image_pattern . '/i',
        );
        foreach ( $results as $res ) {
            foreach ( $patterns as $key => $pattern ) {
                $matches = [];
                if ( preg_match_all(
                    $pattern,
                    $res->post_content,
                    $matches,
                    PREG_PATTERN_ORDER
                ) ) {
                    $this->insecure_links_within_posts[$res->ID][$key] = $matches[3];
                }
            }
        }
    }

    private function retrieve_content( $url ) {
        if ( strpos( $url, "//" ) === 0 ) {
            $url = "https:" . $url;
        }
        $home = WPLE_Trait::get_root_domain( false );
        if ( strpos( $url, $home ) !== FALSE ) {
            $url = add_query_arg( 'wpen_scan', time(), $url );
        }
        $res = wp_remote_get( $url );
        $maincontent = "";
        if ( is_array( $res ) ) {
            $maincontent = wp_remote_retrieve_body( $res );
        }
        if ( is_wp_error( $res ) ) {
            return '';
        }
        return $maincontent;
    }

    private function parse_content_for_http_links( $pid, $link, $content ) {
        $patterns = array(
            "/(http:\\/\\/)([\\w.,@?^=%&:\\/~+#-]*[\\w@?^=%&\\/~+#-]?[.jpg|.gif|.jpeg|.png])(?:((\\?.*[\\'|\"])|['|\"]))/",
            "/(http:\\/\\/)([\\w.,@?^=%&:\\/~+#-]*[\\w@?^=%&\\/~+#-]?\\.mp4)(?:((\\?.*[\\'|\"])|['|\"]))/",
            "/(http:\\/\\/)([\\w.,@?^=%&:\\/~+#-]*[\\w@?^=%&\\/~+#-]?\\.js)(?:((\\?.*[\\'|\"])|['|\"]))/",
            "/(http:\\/\\/)([\\w.,@?^=%&:\\/~+#-]*[\\w@?^=%&\\/~+#-]?\\.css)(?:((\\?.*[\\'|\"])|['|\"]))/"
        );
        $mx_resources = [];
        $matches = [];
        $count = 0;
        foreach ( $patterns as $pattern ) {
            $key = ( $count == 2 ? 'insecure_js' : (( $count == 3 ? 'insecure_css' : 'insecure_images' )) );
            if ( preg_match_all(
                $pattern,
                $content,
                $matches,
                PREG_PATTERN_ORDER
            ) ) {
                $mx_resources[$key] = $matches[2];
            }
            $count++;
        }
        $patterns = array("/(http:\\/\\/|https:\\/\\/|\\/\\/)([\\w.,@?^=%&:\\/~+#-]*[\\w@?^=%&\\/~+#-]?\\.js)(?:((\\?.*[\\'|\"])|['|\"]))/", "/(http:\\/\\/|https:\\/\\/|\\/\\/)([\\w.,@?^=%&:\\/~+#-]*[\\w@?^=%&\\/~+#-]?\\.css)(?:((\\?.*[\\'|\"])|['|\"]))/");
        $all_cssjs_files = [];
        foreach ( $patterns as $index => $pattern ) {
            $key = ( $index == 0 ? 'secure_js' : 'secure_css' );
            if ( preg_match_all(
                $pattern,
                $content,
                $matches,
                PREG_PATTERN_ORDER
            ) ) {
                $all_cssjs_files[$key] = $matches[2];
            }
        }
        foreach ( $all_cssjs_files as $key => $items ) {
            foreach ( $items as $index => $cssjsfile ) {
                $foundissues = $this->check_mxissues_within_cssjs( 'https://' . $cssjsfile );
                if ( !empty( $foundissues ) ) {
                    $mx_resources[$key][$index] = [
                        'resource' => $cssjsfile,
                        'issues'   => $foundissues,
                    ];
                }
            }
        }
        if ( !empty( $mx_resources ) ) {
            $this->permalink_vs_mxresource[$pid] = [
                'webpage'      => $link,
                'mx_resources' => $mx_resources,
            ];
        }
    }

    private function check_mxissues_within_cssjs( $cssjs_url ) {
        $url_pattern = '([\\w.,@?^=%&:\\/~+#-]*[\\w@?^=%&\\/~+#-]?)';
        $patterns = array(
            '/url\\([\'"]?\\K(http:\\/\\/)' . $url_pattern . '/i',
            '/<script [^>]*?src=[\'"]\\K(http:\\/\\/)' . $url_pattern . '/i',
            '/<meta property="og:image" .*?content=[\'"]\\K(http:\\/\\/)' . $url_pattern . '/i',
            '/<(?:img|iframe)[^>].*?src=[\'"]\\K(http:\\/\\/)' . $url_pattern . '/i',
            '/<link [^>].*?href=[\'"]\\K(http:\\/\\/)' . $url_pattern . '/i'
        );
        $filestr = file_get_contents( $cssjs_url );
        $totalmatches = [];
        foreach ( $patterns as $pattern ) {
            $matches = [];
            if ( preg_match_all(
                $pattern,
                $filestr,
                $matches,
                PREG_PATTERN_ORDER
            ) ) {
                $totalmatches[] = $matches[2];
            }
        }
        return $totalmatches;
    }

    private function find_inline_insecure_items( $pid, $link, $webpage ) {
        $url_pattern = '([\\w.,@?^=%&:\\/~+#-]*[\\w@?^=%&\\/~+#-]?)(?:[\'|\\"])';
        $image_pattern = '([\\w.,@?^=%&:\\/~+#-]*[\\w@?^=%&\\/~+#-]?[.jpg|.gif|.jpeg|.png|.svg])(?:((\\?.*[\'|"])|[\'|"]))';
        $patterns = array('/url\\([\'"]?\\K(http:\\/\\/)' . $image_pattern . '/i', '/<(?:iframe)[^>].*?src=[\'"]\\K(http:\\/\\/)' . $url_pattern . '/i', '/<form[^>]*?action=[\'"]\\K(http:\\/\\/)' . $url_pattern . '/i');
        $type = 'inline_style';
        $count = 0;
        $inline_issues = [];
        foreach ( $patterns as $pattern ) {
            $type = ( $count == 1 ? 'inline_iframe' : (( $count == 2 ? 'inline_form' : '' )) );
            $matches = [];
            if ( preg_match_all(
                $pattern,
                $webpage,
                $matches,
                PREG_PATTERN_ORDER
            ) ) {
                $inline_issues[$type] = $matches[2];
            }
            $count++;
        }
        if ( !empty( $inline_issues ) ) {
            $this->permalink_vs_inlinemx[$pid] = [
                'webpage'      => $link,
                'mx_resources' => $inline_issues,
            ];
        }
    }

    private function find_widgets_insecure_items() {
        $widget_areas = wp_get_sidebars_widgets();
        foreach ( $widget_areas as $widgets ) {
            foreach ( $widgets as $widget_title ) {
                $widget_data = $this->get_widget_data( $widget_title );
                if ( $widget_data ) {
                    $patterns = array(
                        "images" => "/(http:\\/\\/)([\\w.,@?^=%&:\\/~+#-]*[\\w@?^=%&\\/~+#-]?[.jpg|.gif|.jpeg|.png|.svg])(?:((\\?.*[\\'|\"])|['|\"]))/",
                        "video"  => "/(http:\\/\\/)([\\w.,@?^=%&:\\/~+#-]*[\\w@?^=%&\\/~+#-]?\\.mp4)(?:((\\?.*[\\'|\"])|['|\"]))/",
                        "js"     => "/(http:\\/\\/)([\\w.,@?^=%&:\\/~+#-]*[\\w@?^=%&\\/~+#-]?\\.js)(?:((\\?.*[\\'|\"])|['|\"]))/",
                        "css"    => "/(http:\\/\\/)([\\w.,@?^=%&:\\/~+#-]*[\\w@?^=%&\\/~+#-]?\\.css)(?:((\\?.*[\\'|\"])|['|\"]))/",
                    );
                    foreach ( $patterns as $type => $pattern ) {
                        $matches = [];
                        if ( preg_match_all(
                            $pattern,
                            $widget_data['html'],
                            $matches,
                            PREG_PATTERN_ORDER
                        ) ) {
                            $this->widget_issues[$widget_data['type'] . '-' . $widget_data['id']][] = $matches[2];
                            $this->merged_widget_issues = array_merge( $this->merged_widget_issues, $matches[2] );
                        }
                    }
                }
            }
        }
    }

    public function get_widget_data( $title ) {
        $type = substr( $title, 0, strpos( $title, '-' ) );
        $id = substr( $title, strpos( $title, '-' ) + 1 );
        $widget_array = get_option( "widget_" . $type );
        $widget_html = "";
        $widget_title = "";
        $type_found = false;
        if ( isset( $widget_array[$id]["content"] ) ) {
            $type_found = true;
            $widget_html = $widget_array[$id]["content"];
        }
        if ( isset( $widget_array[$id]["url"] ) ) {
            $type_found = true;
            $widget_html = $widget_array[$id]["url"];
        }
        if ( isset( $widget_array[$id]["text"] ) ) {
            $type_found = true;
            $widget_html = $widget_array[$id]["text"];
        }
        if ( isset( $widget_array[$id]["title"] ) ) {
            $widget_title = $widget_array[$id]["title"];
        }
        if ( isset( $widget_array[$id]["html"] ) ) {
            $type_found = true;
            $widget_html = $widget_array[$id]["html"];
        }
        if ( $type_found ) {
            return array(
                "type"  => $type,
                "id"    => $id,
                "html"  => $widget_html,
                "title" => $widget_title,
            );
        } else {
            return false;
        }
    }

}
