Your IP : 3.145.88.233


Current Path : /var/www/mainsite/
Upload File :
Current File : /var/www/mainsite/sucuri-version-check.php

<?php

/* Function requested by Giannis to test if tool was uploaded correctly */
if ( isset( $_GET['test'] ) ) exit;

@set_time_limit(0);
@ini_set("max_execution_time", 0);
@set_time_limit(0);
@ignore_user_abort(true);
if (extension_loaded('xdebug') && !isset($_GET['robot'])) {
    echo 'Xdebug detected - exitting...';
    die();
}

// Version to be updated on every build
$myversion='20200817_1106';

function isTerminal()
{
    return !isset($_SERVER['REMOTE_ADDR']) && isset($_SERVER['SHELL']);
}

if (!isTerminal()){
    echo "<head><style>.noselect {
        -webkit-touch-callout: none; /* iOS Safari */
        -webkit-user-select: none;   /* Chrome/Safari/Opera */
        -khtml-user-select: none;    /* Konqueror */
        -moz-user-select: none;      /* Firefox */
        -ms-user-select: none;       /* IE/Edge */
        user-select: none;           /* non-prefixed version, currently
                                  not supported by any browser */
    }</style></head>";
}

/* If running via terminal. */
if (isTerminal())  parse_str(implode('&', array_slice($argv, 1)), $_GET);

/**
 * Prevents the non-supervised execution of the cleanup script.
 *
 * The main idea of this condition is to automatically delete this and other
 * scripts that were uploaded by one of our security analysts to a customer's
 * website during a regular malware cleanup.
 *
 * If the analyst forgets to delete these files and a web crawler finds them,
 * the direct execution of the script without the required parameters will
 * trigger the deletion of the cleanup suite.
 *
 * @option srun Stop the automatic deletion of the unsupervised scripts.
 */

if (!isset($_GET['srun'])) {
    @unlink('sucuri-cleanup.php');
    @unlink('sucuri-version-check.php');
    @unlink('sucuri-wpdb-clean.php');
    @unlink('sucuri-db-cleanup.php');
    @unlink('sucuri-db-clean.php');
    @unlink('sucuri_listcleaned.php');
    @unlink('sucuri-filemanager.php');
    @unlink('sucuri-toolbox.php');
    @unlink('sucuri-toolbox-client.php');
    @unlink('google936278b9bb435851.html');
    @unlink('google79c13c5605e6f406.html');
    @unlink('googlee6cb14d10ed8d0d2.html');
    @unlink('googlec55310faa35e04c1.html');
    @unlink(__FILE__);
    exit(0);
}


$VULN_SOFTWARE = array("wordpress" => array('1.2'=>array(0,1,2,3,4,5,6,),'1.2.1'=>array(0,2,3,4,5,6,),'1.5'=>array(7,8,9,10,2,3,4,5,6,11,),'1.5.1'=>array(12,13,14,2,3,4,5,6,11,),'1.5.1.1'=>array(12,15,16,13,14,10,2,3,4,5,6,11,),'1.5.1.2'=>array(12,17,13,14,2,3,4,5,6,11,),'1.5.1.3'=>array(12,18,13,14,2,3,11,),'1.5.2'=>array(12,13,14,2,3,11,),'2.0'=>array(12,19,13,14,20,21,22,23,24,2,3,11,),'2.0.1'=>array(12,19,13,14,20,21,22,23,24,2,3,11,),'2.0.2'=>array(19,25,13,14,26,20,21,22,23,24,2,3,11,),'2.0.3'=>array(19,13,14,26,20,21,22,23,24,27,2,3,11,),'2.0.4'=>array(19,13,14,26,20,21,22,23,24,27,2,3,11,),'2.0.5'=>array(19,28,13,14,20,21,22,23,24,27,2,3,11,),'2.0.6'=>array(19,29,13,14,20,21,22,23,24,27,2,3,11,),'2.0.7'=>array(19,13,14,20,21,22,23,24,27,2,3,11,),'2.0.8'=>array(13,14,20,21,22,23,24,2,3,11,),'2.0.9'=>array(13,14,20,21,22,23,24,27,2,3,11,),'2.0.10'=>array(19,13,14,20,21,22,23,24,27,2,3,11,),'2.0.11'=>array(19,13,14,20,21,22,23,24,27,2,3,11,),'2.1'=>array(13,14,20,21,22,23,24,27,2,3,11,),'2.1.1'=>array(19,30,13,14,20,21,22,23,24,27,2,3,31,11,),'2.1.2'=>array(19,32,33,13,14,20,21,22,23,24,27,2,3,11,),'2.1.3'=>array(19,34,13,14,20,21,22,23,24,27,2,3,11,),'2.2'=>array(19,35,36,13,14,20,21,22,23,24,27,2,3,11,),'2.2.1'=>array(19,13,14,20,21,22,23,24,27,2,3,11,),'2.2.2'=>array(19,13,14,20,21,22,23,24,27,2,3,11,),'2.2.3'=>array(19,13,14,20,21,22,23,24,27,2,3,11,),'2.3'=>array(13,14,20,21,22,23,24,27,2,3,11,),'2.3.1'=>array(19,37,13,14,20,21,22,23,24,27,2,3,11,),'2.3.2'=>array(19,13,14,20,21,22,23,24,27,2,3,11,),'2.3.3'=>array(19,13,14,20,21,22,23,24,2,3,11,),'2.5'=>array(38,39,13,14,20,21,22,23,24,27,2,3,40,11,),'2.5.1'=>array(19,39,13,14,20,21,22,23,24,27,2,3,40,11,),'2.6'=>array(39,13,14,20,21,22,23,24,27,2,3,41,40,11,),'2.6.1'=>array(19,42,39,13,14,20,21,22,23,24,27,2,3,41,40,11,),'2.6.2'=>array(19,39,13,14,20,21,22,23,24,27,2,3,41,40,11,),'2.6.3'=>array(39,13,14,20,21,22,23,24,27,2,3,41,40,11,),'2.6.5'=>array(19,39,13,14,20,21,22,23,24,2,3,41,40,11,),'2.7'=>array(19,39,13,14,20,21,22,23,24,27,2,3,43,41,40,11,),'2.7.1'=>array(19,39,13,14,20,21,22,23,24,27,2,3,43,41,40,11,),'2.8'=>array(39,13,14,20,21,22,23,24,27,2,3,43,41,40,44,11,45,),'2.8.1'=>array(46,39,13,14,20,21,22,23,24,27,2,3,43,41,40,44,11,45,),'2.8.2'=>array(39,13,14,20,21,22,23,24,27,2,3,43,41,40,44,11,45,),'2.8.3'=>array(47,39,13,14,20,21,22,23,24,27,2,3,43,41,40,44,11,45,),'2.8.4'=>array(39,13,14,20,21,22,23,24,27,2,3,43,41,40,44,11,45,),'2.8.5'=>array(48,39,13,14,20,21,22,23,24,27,2,3,43,41,40,44,11,45,),'2.8.6'=>array(39,13,14,20,21,22,23,24,27,2,3,43,41,40,44,11,45,),'2.9'=>array(49,50,39,13,14,20,21,22,23,24,27,2,3,43,41,40,44,51,11,45,),'2.9.1'=>array(39,13,14,20,21,22,23,24,27,2,3,43,41,40,44,51,11,45,),'2.9.2'=>array(39,13,14,20,21,22,23,24,27,2,3,43,41,40,44,51,11,45,),'3.0'=>array(39,13,14,52,53,54,55,20,21,22,23,24,56,27,57,58,2,3,59,43,60,61,41,40,44,51,11,45,62,),'3.8.5'=>array(59,63,64,65,66,67,68,69,70,71,72,73,43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'3.8.6'=>array(59,63,64,65,66,67,68,69,70,71,72,73,43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'3.8.7'=>array(59,63,64,65,66,67,68,69,70,71,72,73,43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'3.8.8'=>array(59,63,64,65,66,67,68,69,70,71,72,73,43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'3.8.9'=>array(63,64,65,66,67,68,69,70,71,72,73,43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'3.8.10'=>array(68,69,70,71,72,73,43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'3.8.11'=>array(71,72,73,43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'3.8.12'=>array(72,73,43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'3.8.13'=>array(43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'3.8.14'=>array(75,41,40,44,51,76,11,45,62,77,),'3.8.15'=>array(40,44,51,76,11,45,62,77,),'3.8.16'=>array(51,76,11,45,62,77,),'3.8.17'=>array(77,),'3.9'=>array(78,27,57,79,80,58,2,3,81,82,83,59,63,64,65,66,67,68,69,70,71,72,73,43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'3.9.1'=>array(78,27,57,79,80,58,2,3,81,82,59,63,64,65,66,67,68,69,70,71,72,73,43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'3.9.2'=>array(80,58,2,3,81,82,84,59,63,64,65,66,67,68,69,70,71,72,73,43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'3.9.3'=>array(85,59,63,64,65,66,67,68,69,70,71,72,73,43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'3.9.4'=>array(59,63,64,65,66,67,68,69,70,71,72,73,43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'3.9.5'=>array(59,63,64,65,66,67,68,69,70,71,72,73,43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'3.9.6'=>array(59,63,64,65,66,67,68,69,70,71,72,73,43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'3.9.7'=>array(63,64,65,66,67,68,69,70,71,72,73,43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'3.9.8'=>array(68,69,70,71,72,73,43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'3.9.9'=>array(71,71,72,73,43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'3.9.10'=>array(72,73,43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'3.9.11'=>array(43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'3.9.12'=>array(75,41,40,44,51,76,11,45,62,77,),'3.9.13'=>array(40,44,51,76,11,45,62,77,),'3.9.14'=>array(51,76,11,45,62,77,),'3.9.15'=>array(77,),'4.0'=>array(2,3,81,82,83,84,59,63,64,65,66,67,68,69,70,71,72,73,43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'4.0.1'=>array(83,82,59,63,64,65,66,67,68,69,70,71,72,73,43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'4.0.2'=>array(59,63,64,65,66,67,68,69,70,71,72,73,43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'4.0.3'=>array(59,63,64,65,66,67,68,69,70,71,72,73,43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'4.0.4'=>array(59,63,64,65,66,67,68,69,70,71,72,73,43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'4.0.5'=>array(59,63,64,65,66,67,68,69,70,71,72,73,43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'4.0.6'=>array(63,64,65,66,67,68,69,70,71,72,73,43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'4.0.7'=>array(68,69,70,71,72,73,43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'4.0.8'=>array(71,71,72,73,43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'4.0.9'=>array(72,73,43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'4.0.10'=>array(43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'4.0.11'=>array(75,41,40,44,51,76,11,45,62,77,),'4.0.12'=>array(40,44,51,76,11,45,62,77,),'4.0.13'=>array(51,76,11,45,62,77,),'4.0.14'=>array(77,),'4.1'=>array(82,83,86,87,59,63,64,65,66,67,68,69,70,71,72,73,43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'4.1.1'=>array(82,83,85,86,87,59,63,64,65,66,67,68,69,70,71,72,73,43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'4.1.2'=>array(85,86,59,63,64,65,66,67,68,69,70,71,72,73,43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'4.1.3'=>array(86,59,63,64,65,66,67,68,69,70,71,72,73,43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'4.1.4'=>array(86,59,63,64,65,66,67,68,69,70,71,72,73,43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'4.1.5'=>array(59,63,64,65,66,67,68,69,70,71,72,73,43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'4.1.6'=>array(63,64,65,66,67,68,69,70,71,72,73,43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'4.1.7'=>array(68,69,70,71,72,73,43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'4.1.8'=>array(71,71,72,73,43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'4.1.9'=>array(72,73,43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'4.1.10'=>array(43,60,61,74,75,41,40,44,51,76,11,45,62,77,),'4.1.11'=>array(75,41,40,44,51,76,11,45,62,77,),'4.1.12'=>array(40,44,51,76,11,45,62,77,),'4.5.1'=>array(88,74,89,75,41,40,44,90,51,76,11,45,62,91,77,92,),'4.5.2'=>array(89,75,41,93,94,95,40,44,90,51,76,11,45,62,91,77,92,),'4.5.3'=>array(96,40,44,90,51,76,11,45,62,91,77,92,),'4.5.4'=>array(90,51,76,11,45,62,91,77,92,),'4.5.5'=>array(90,91,77,92,),'4.6'=>array(40,44,90,51,76,11,45,62,91,77,92,),'4.6.1'=>array(90,51,76,11,45,62,91,77,92,),'4.6.2'=>array(91,77,92,),'4.7'=>array(90,97,51,98,76,11,45,62,91,77,92,99,),'4.7.1'=>array(91,77,92,99,),));


$VULNERABILITIES = array('WordPress 1.2-1.2.1 - Multiple Cross-Site Scripting (XSS)','WordPress 1.2 - HTTP Response Splitting','WordPress <= 4.0 - Long Password Denial of Service (DoS)','WordPress <= 4.0 - Server Side Request Forgery (SSRF)','WordPress <= 1.5.1.2 - XMLRPC Eval Injection ','WordPress <= 1.5.1.2 - Multiple Cross-Site Scripting (XSS)','WordPress <= 1.5.1.2 - Email Spoofing','WordPress 1.5 wp-trackback.php tb_id Parameter SQL Injection','WordPress <= 1.5 Multiple Vulnerabilities (XSS, SQLi)','WordPress 1.5 template-functions-post.php Multiple Field XSS','WordPress 1.5 & 1.5.1.1 - SQL Injection','WordPress <= 4.7 - Post via Email Checks mail.example.com by Default','Wordpress 1.5.1 - 2.0.2 wp-register.php Multiple Parameter XSS','WordPress 1.5.1 - 3.5 XMLRPC Pingback API Internal/External Port Scanning','WordPress 1.5.1 - 3.5 XMLRPC pingback additional issues','WordPress <= 1.5.1.1 "add new admin" SQL Injection Exploit','WordPress <= 1.5.1.1 SQL Injection Exploit','WordPress <= 1.5.1.2 - XMLRPC SQL Injection','Wordpress <= 1.5.1.3 Remote Code Execution eXploit (metasploit)','WordPress 2.0 - 2.7.1 admin.php Module Configuration Security Bypass ','WordPress 2.0 - 3.0.1 wp-includes/comment.php Bypass Spam Restrictions','WordPress 2.0 - 3.0.1 Multiple Cross-Site Scripting (XSS) in request_filesystem_credentials()','WordPress 2.0 - 3.0.1 Cross-Site Scripting (XSS) in wp-admin/plugins.php','WordPress 2.0 - 3.0.1 wp-includes/capabilities.php Remote Authenticated Administrator Delete Action Bypass','WordPress 2.0 - 3.0 Remote Authenticated Administrator Add Action Bypass','WordPress <= 2.0.2 (cache) Remote Shell Injection Exploit','WordPress 2.0.2 - 2.0.4 Paged Parameter SQL Injection ','WordPress 2.0.3 - 3.9.1 (except 3.7.4 / 3.8.4) CSRF Token Brute Forcing','Wordpress 2.0.5 Trackback UTF-7 Remote SQL Injection Exploit','Wordpress <= 2.0.6 wp-trackback.php Remote SQL Injection Exploit','WordPress 2.1.1 - Comm& Execution Backdoor','WordPress 2.1.1 - RCE Backdoor','WordPress \'year\' Cross-Site Scripting (XSS)','WordPress 2.1.2 Authenticated XMLRPC SQL Injection','Wordpress 2.1.3 admin-ajax.php SQL Injection Blind Fishing Exploit','WordPress 2.2 (wp-app.php) Arbitrary File Upload Exploit','Wordpress 2.2 (xmlrpc.php) Remote SQL Injection Exploit','Wordpress <= 2.3.1 Charset Remote SQL Injection ','Wordpress 2.5 Cookie Integrity Protection ','WordPress 2.5 - 3.3.1 XSS in swfupload','WordPress 2.5-4.6 - Authenticated Stored Cross-Site Scripting via Image Filename','WordPress 2.6.0-4.5.2 - Unauthorized Category Removal from Post','Wordpress 2.6.1 (SQL Column Truncation) Admin Takeover Exploit','WordPress <= 4.4.2 - SSRF Bypass using Octal & Hexedecimal IP addresses','WordPress 2.8-4.6 - Path Traversal in Upgrade Package Uploader','WordPress 2.8-4.7 - Accessibility Mode Cross-Site Request Forgery (CSRF)','Wordpress 2.8.1 (url) Remote Cross Site Scripting Exploit','Wordpress <= 2.8.3 Remote Admin Reset Password ','WordPress <= 2.8.5 Unrestricted File Upload Arbitrary PHP Code Execution','WordPress 2.9 Failure to Restrict URL Access','WordPress 2.9 - Failure to Restrict URL Access','WordPress 2.9-4.7 - Authenticated Cross-Site scripting (XSS) in update-core.php','WordPress <= 3.0.5 wp-admin/press-this.php Privilege Escalation','WordPress <= 3.3.2 Cross-Site Scripting (XSS) in wp-includes/default-filters.php','WordPress <= 3.3.2 wp-admin/media-upload.php sensitive information disclosure or bypass','WordPress <= 3.3.2 wp-admin/includes/class-wp-posts-list-table.php sensitive information disclosure by visiting a draft','WordPress 3.0 - 3.6 Crafted String URL Redirect Restriction Bypass','WordPress 3.0 - 3.9.1 Authenticated Cross-Site Scripting (XSS) in Multisite','WordPress 3.0-3.9.2 - Unauthenticated Stored Cross-Site Scripting (XSS)','WordPress <= 4.2.2 - Authenticated Stored Cross-Site Scripting (XSS)','WordPress <= 4.4.2 - Reflected XSS in Network Settings','WordPress <= 4.4.2 - Script Compression Option CSRF','WordPress 3.0-4.7 - Cryptographically Weak Pseudo-Random Number Generator (PRNG)','WordPress <= 4.2.3 - wp_untrash_post_comments SQL Injection ','WordPress <= 4.2.3 - Timing Side Channel Attack','WordPress <= 4.2.3 - Widgets Title Cross-Site Scripting (XSS)','WordPress <= 4.2.3 - Nav Menu Title Cross-Site Scripting (XSS)','WordPress <= 4.2.3 - Legacy Theme Preview Cross-Site Scripting (XSS)','WordPress <= 4.3 - Authenticated Shortcode Tags Cross-Site Scripting (XSS)','WordPress <= 4.3 - User List Table Cross-Site Scripting (XSS)','WordPress <= 4.3 - Publish Post & Mark as Sticky Permission Issue','WordPress  3.7-4.4 - Authenticated Cross-Site Scripting (XSS)','WordPress 3.7-4.4.1 - Local URIs Server Side Request Forgery (SSRF)','WordPress 3.7-4.4.1 - Open Redirect','WordPress <= 4.5.1 - Pupload Same Origin Method Execution (SOME)','WordPress 3.6-4.5.2 - Authenticated Revision History Information Disclosure','WordPress 3.4-4.7 - Stored Cross-Site Scripting (XSS) via Theme Name fallback','WordPress 3.5-4.7.1 - WP_Query SQL Injection',' WordPress 3.9 & 3.9.1 Unlikely Code Execution','WordPress 3.6 - 3.9.1 XXE in GetID3 Library','WordPress 3.4.2 - 3.9.2 Does Not Invalidate Sessions Upon Logout','WordPress 3.9, 3.9.1, 3.9.2, 4.0 - XSS in Media Playlists','WordPress <= 4.1.1 - Unauthenticated Stored Cross-Site Scripting (XSS)','WordPress 3.9-4.1.1 - Same-Origin Method Execution','WordPress <= 4.0 - CSRF in wp-login.php Password Reset','WordPress <= 4.2 - Unauthenticated Stored Cross-Site Scripting (XSS)','WordPress 4.1-4.2.1 - Genericons Cross-Site Scripting (XSS)','WordPress 4.1 - 4.1.1 - Arbitrary File Upload','WordPress 4.2-4.5.1 - MediaElement.js Reflected Cross-Site Scripting (XSS)','WordPress 4.2-4.5.2 - Authenticated Attachment Name Stored XSS','WordPress 4.3-4.7 - Potential Remote Command Execution (RCE) in PHPMailer','WordPress 4.2.0-4.7.1 - Press This UI Available to Unauthorised Users','WordPress 4.3.0-4.7.1 - Cross-Site Scripting (XSS) in posts list table','WordPress 4.5.2 - Redirect Bypass','WordPress 4.5.2 - oEmbed Denial of Service (DoS)','WordPress 4.5.2 - Password Change via Stolen Cookie','WordPress 4.5.3 - Authenticated Denial of Service (DoS)','WordPress 4.7 - User Information Disclosure via REST API','WordPress 4.7 - Cross-Site Request Forgery (CSRF) via Flash Upload','WordPress 4.7.0-4.7.1 - Unauthenticated Page/Post Content Modification via REST API',);

if ($VULN_SOFTWARE == null) {
    echo "ERROR: Unable to get current versions. Please contact support@sucuri.net for guidance.\n";
    exit(1);
}

// Get file content and find the line with content
function findLineInFile($file, $content)
{
    $fh = @fopen($file, "r");
    if (!$fh) {
        echo "DEBUG: UNABLE TO CHECK " . escapeHtml($file) . "\n";
        return null;
    }
    while (($buffer = fgets($fh, 4096)) !== false) {
        if (strpos($buffer, $content) !== false) {
            fclose($fh);
            return $buffer;
        }
    }
    fclose($fh);
    return null;
}

if (!function_exists('file_get_contents')) { // below PHP 4.3
    function file_get_contents($fileName) {
        $fh = @fopen($fileName);
        if ($fh === false) {
            return false;
        }
        $res = fread($fh, 1048576);
        fclose($fh);
        return $res;
    }
}

define('OPT_TYPE_CONST',      1);
define('OPT_TYPE_VAR',        2);
define('OPT_TYPE_ASSOC',      4);
define('OPT_TYPE_ENV',        8);
define('OPT_TYPE_XML',       16);
define('OPT_TYPE_FIRST',     32);
define('OPT_TYPE_CONST_NUM', 64);

function getOptMatch($m, $type)
{
    return ($type & OPT_TYPE_FIRST) ? $m[0] : $m[count($m) - 1];
}

function getOption($option, $config, $type)
{
    $option = preg_quote($option);

    // String constants (WordPress)
    if (($type & OPT_TYPE_CONST) &&
        preg_match_all('@^\s*define\(\s*([\'"])' . $option . '\1\s*,\s*([\'"])(.*)\2\s*\)\s*;@m', $config, $m)) {
            return stripslashes(getOptMatch($m[3], $type));
    }

    // Numeric constants
    if (($type & OPT_TYPE_CONST_NUM) &&
        preg_match_all('@^\s*define\(\s*([\'"])' . $option . '\1\s*,\s*(\d*)\s*\)\s*;@m', $config, $m)) {
            return stripslashes(getOptMatch($m[2], $type));
    }

    // wp-config.php trick, see
    // http://www.wpbeginner.com/wp-tutorials/useful-wordpress-configuration-tricks-that-you-may-not-know/
    if (($type & OPT_TYPE_ENV) && preg_match('@^\s*define\(\s*([\'"])' . $option . '\1\s*,\s*\$_ENV\s*[[{]\s*' .
        '([\'"]?)DATABASE_SERVER\2\s*[}\]]\s*\)\s*;@m', $config, $m)) {
            return $_ENV['DATABASE_SERVER'];
    }

    // Variables (Joomla, WordPress prefix)
    if (($type & OPT_TYPE_VAR) &&
        preg_match_all('@^\s*(?:public|var)?\s*' . $option . '\s*=\s*(?:([\'"])(.*)\1|([0-9]+))\s*;@m', $config, $m)) {
            $str = getOptMatch($m[2], $type); // Return the string m[2] or the number m[3] if the string was not found
            return $str ? stripslashes($str) : getOptMatch($m[3], $type);
    }

    // Associative arrays (Drupal)
    if (($type & OPT_TYPE_ASSOC) &&
        preg_match_all('@(?:^|,|\()\s*([\'"])' . $option . '\1\s*=>\s*([\'"])(.*?)\2\s*[,)]@m', $config, $m)) {
            return stripslashes(getOptMatch($m[3], $type));
    }

    // Magento XML config
    if (($type & OPT_TYPE_XML) && preg_match_all('@^\s*<' . $option . '>(?:<!\[CDATA\[|\{\{)(.*)(?:}}|\]\]>)</' .
        $option . '>\s*$@m', $config, $m)) {
            return getOptMatch($m[1], $type);
    }

    return '';
}


// Properly escape HTML content
function escapeHTML($string)
{
    global $isPlainText;

    if ($isPlainText) {
        return $string;
    }

    if (!defined('ENT_SUBSTITUTE')) {
        define('ENT_SUBSTITUTE', 0);
    }

    return htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}

$VULNERABLE_PLUGINS = array(
    'fancybox.php' => array('3.0.2', 'https://blog.sucuri.net/2015/02/zero-day-in-the-fancybox-for-wordpress-plugin.html', 'Fancybox'),
    'gravityforms.php' => array('1.8.20', 'https://blog.sucuri.net/2015/02/malware-cleanup-to-arbitrary-file-upload-in-gravity-forms.html', 'Gravity Forms'),
    'hdflvvideoshare.php' => array('2.8', '', 'HDFLV Videoshare'),
    'grpdocsassembly.php' => array('2.0.5', '', 'Group Docs Assembly'),
    'wp-seo.php' => array('3.4.2', '', 'Yoast SEO'),
    'blogvault.php' => array('1.45', 'https://blogvault.net/security-notification/', 'BlogVault'),
    'ultimate-member.php' => array('2.0.23', '', 'BlogVault'),
    'duplicator.php' => array('1.2.29', '', 'SnapCreek'),
    'wp-gdpr-compliance.php' => array('1.4.3', '', 'WP GDPR Compliance'),
    'propeller-ads.php' => array('1.0.0', 'This plugin is known for having hardcoded links to ad-rotators involved in fraud.', 'PropellerAds'),
    'easy-wp-smtp.php' => array('1.3.9', 'https://blog.sucuri.net/2019/03/0day-vulnerability-in-easy-wp-smtp-affects-thousands-of-sites.html', 'Easy WP SMTP'),
    'social-warfare.php' => array('3.5.3', 'https://blog.sucuri.net/2019/03/zero-day-stored-xss-in-social-warfare.html', 'Social Warfare'),
    'migla-donation.php' => array('2.0.6', 'The plugin Total Donations has a 0day vulnerability, needs to be removed completely to prevent further attacks, not just disabled.', 'Total Donations'),
    'yuzo_related_post.php' => array('5.12.95', 'https://blog.sucuri.net/2019/04/attacks-on-closed-wordpress-plugins.html', 'Yuzo Related Post'),
    'advanced-cf7-db.php' => array('1.6.1', 'https://blog.sucuri.net/2019/04/sql-injection-in-advance-contact-form-7-db.html', 'Advanced Contact Form 7 DB'),
    'yellow-pencil.php' => array('7.2.0', 'https://yellowpencil.waspthemes.com/docs/important-security-update/', 'Yellow Pencil'),
    'woocommerce-checkout-manager.php' => array('4.3', 'https://blog.sucuri.net/2019/04/insufficient-privilege-validation-in-woocommerce-checkout-manager.html', 'WooCommerce Checkout Manager'),
    'blog-designer.php' => array('1.8.13', '', 'Blog Designer'),
    'wp-live-chat-support.php' => array('8.0.28', 'https://blog.sucuri.net/2019/05/persistent-cross-site-scripting-in-wp-live-chat-support-plugin.html', 'WP Live Chat Support'),
    'fb-messenger-live-chat.php' => array('1.4.8', '', 'Live Chat with Facebook Messenger'),
    'wp-slimstat.php' => array('4.8.1', 'https://blog.sucuri.net/2019/05/slimstat-stored-xss-from-visitors.html', 'Slimstat Analytics'),
    'wp-live-chat-support-pro.php' => array('8.0.12', '', 'WP Live Chat Support Pro'),
    'wp-database-backup.php' => array('5.3', '', 'WP Database Backup'),
    'wp_mail_smtp.php' => array('1.5.1', '', 'WP Mail SMTP by WPForms'),
    'wp-contact-form-7.php' => array('5.0.4', 'https://contactform7.com/2018/09/04/contact-form-7-504/', 'Contact Form 7'),
    'cpabc_appointments.php' => array('1.3.19', '', 'Appointment Booking Calendar'),
    'simple-301-bulk-uploader.php' => array('1.2.5', '', 'Simple 301 Redirects – Addon – Bulk Uploader'),
    'nd-shortcodes.php' => array('6.0', '', 'ND Shortcodes For Visual Composer'),
    'wp-private-content-plus.php' => array('1.32', '', 'WP Private Content Plus'),
    'woocommerce-filters.php' => array('1.3.7', 'https://labs.sucuri.net/unauthenticated-settings-update-in-woocommerce-ajax-filters/', 'Advanced AJAX Product Filters'),
    'file_folder_manager.php' => array('5.4', 'https://labs.sucuri.net/plugins-under-attack-june-2019/', 'File Manager'),
    'mystickymenu.php' => array('2.1.6', 'https://labs.sucuri.net/plugins-under-attack-june-2019/', 'Sticky Menu Plugin'),
    'wp-support-plus.php' => array('9.1.2', 'https://labs.sucuri.net/plugins-under-attack-june-2019/', 'WP Support Plus Responsive Ticket System'),
    'folders.php' => array('2.1.1', 'https://labs.sucuri.net/plugins-under-attack-june-2019/', 'Folders Plugins'),
    'supportcandy.php' => array('2.0.6', 'https://labs.sucuri.net/plugins-under-attack-june-2019/', 'SupportCandy'),
    'rich-reviews.php' => array('1.7.4', 'https://wordpress.org/plugins/rich-reviews/', 'Rich Reviews'),
    'wp-piwik.php' => array('1.0.19', 'https://labs.sucuri.net/plugins-under-attack-june-2019/', 'WP-Matomo (WP-Piwik)'),
    'wp-quick-booking-manager.php' => array('1.2', 'https://wordpress.org/plugins/wp-quick-booking-manager/', 'WP Quick Booking Manager (Closed - Plugin should be manually Removed)'),
    'simple_fields.php' => array('1.4.12', 'https://wordpress.org/support/topic/hack-related-to-simple-fields/', 'Simple Fields'),
    'popup-maker.php' => array('1.8.13', 'https://wpvulndb.com/vulnerabilities/9907', 'Popup Maker'),
    'premium-addons-for-elementor.php' => array('3.7.3', 'https://www.pluginvulnerabilities.com/2019/09/10/hackers-may-already-be-targeting-this-authenticated-persistent-xss-vulnerability-in-premium-addons-for-elementor/', 'Premium Addons for Elementor'),
    'simple-staff-list.php' => array('2.2.1', 'https://labs.sucuri.net/plugins-added-to-malware-campaign-july-2019/', 'Simple Staff List'),
    'otw_post_custom_templates_lite.php' => array('1.12', 'https://labs.sucuri.net/plugins-added-to-malware-campaign-july-2019/', 'Post Custom Templates Lite'),
    'wp-time-capsule.php' => array('1.21.16', 'https://wordpress.org/plugins/wp-time-capsule/', 'Backup and Staging by WP Time Capsule'),
    'popup-builder.php' => array('3.64.1', 'https://wordpress.org/plugins/popup-builder/', 'Popup Builder'),
    'brizy.php' => array('1.0.114', 'https://wpvulndb.com/vulnerabilities/10112', 'Brizy – Page Builder'),
    'flexible-checkout-fields.php' => array('2.3.4', 'https://www.wpdesk.net/blog/flexible-checkout-fields-vulnerability/', 'Flexible Checkout Fields for WooCommerce'),
    'rank-math.php' => array('1.0.41', 'https://wpvulndb.com/vulnerabilities/10157', 'WordPress SEO Plugin – Rank Math'),
    'elementor-pro.php' => array('2.9.4', 'https://wpvulndb.com/vulnerabilities/10214', 'Elementor Pro'),
    'ultimate-elementor.php' => array('1.24.2', 'https://wpvulndb.com/vulnerabilities/10215', 'Ultimate Elementor Pro'),
    'siteorigin-panels.php' => array('2.10.16', 'https://wpvulndb.com/vulnerabilities/10219', 'Page Builder by SiteOrigin'),
    'ninja-forms.php' => array('3.4.24.3', 'https://wpvulndb.com/vulnerabilities/10200', 'Ninja Forms'),
);

// This line is updated with the most recent versions in refresh-versions.php on every build
$VERSIONS = array('signature'=>'sucuri-current-versions','joomla'=>array('3.9.20',),'wordpress'=>array('5.5',),'drupal'=>array('7.70','8.7.14','8.8.6',),'magento'=>array('1.9.4.4','2.2.11','2.3.4',),'jce'=>array('2.8.12',),'phpbb'=>array('3.3.0',),'vbulletin'=>array('5.5.6',),'jetpack'=>array('8.6.1',),'zenphoto'=>array('1.5.7',),'prestashop'=>array('1.7.6.5',),'opencart'=>array('3.0.3.3',),'oscommerce'=>array('2.3.4.1',),'timthumb'=>array('2.8.13',),'modX'=>array('evolution'=>'1.4.11','revolution'=>'2.7.3',),'typo3'=>array('9.5.19','10.4.4',),'Newsmag'=>array('3',),'Newspaper'=>array('6.7.2',),'jQueryFileUpload'=>array('9.25.1',),);

$isNoise = false;
if (isset($_GET['noise'])) {
    $isNoise = true;
}

// =======================
// checkIsUpdated

function checkIsUpdated($version, $latestVersions)
{
    $version = standardizeVersion($version);

    if (is_array($latestVersions)) {
        $lv = $latestVersions[0];
    } else {
        $lv = $latestVersions;
    }

    $checkArrayCount = is_array($latestVersions) ? count($latestVersions) : 0;

    $latest = 1 < $checkArrayCount
        ? findMostSimilar($version, $latestVersions)
        : standardizeVersion($lv);

    return version_compare($version, $latest) >= 0;
}

// finds the most similar version using XOR bitwise operation
// (same chars at the beginning of the string result in lower values of the result,
// lowest result gives the most similar from the beginning)
function findMostSimilar($version, $versions)
{
    $mostSimilar = false;
    $mostSimilarValue = 0;

    foreach ($versions as $currentVersion) {
        $endResult = standardizeVersion($currentVersion);
        $endResult = str_pad($endResult, strlen($version), '0');
        $endResult = unpack('H*', $version ^ $endResult);
        $endResult = base_convert($endResult[1], 16, 10);

        if (!$mostSimilar || $endResult < $mostSimilarValue) {
            $mostSimilar = $currentVersion;
            $mostSimilarValue = $endResult;
        }
    }

    return $mostSimilar;
}

function standardizeVersion($version)
{
    $version = preg_replace('/ Patch Level (\d+)/i', 'pl$1', $version);

    return $version;
}

// ============================================
// checkPlugin

function checkWpPlugin($path, $fileName)
{
    global $VULNERABLE_PLUGINS;

    list($safeVersion, $url, $pluginName) = $VULNERABLE_PLUGINS[$fileName];

    $pluginVersion = getWpPluginVersion("$path/$fileName");

    if ($pluginVersion && !checkIsUpdated($pluginVersion, $safeVersion)) {
        $info = array('name' => $pluginName, 'version' => $pluginVersion, 'dir' => $path.'/'.$fileName,
            'oldplugin' => true, );

        if ($url) {
            $info['url'] = $url;
        }

        return $info;
    }

    return false;
}

function getWpPluginVersion($file)
{
    $version = findLineInFile($file, 'Version:');

    $version = explode(':', $version);

    if (isset($version[1])) {
        return trim($version[1]);
    }

    return false;
}

// ============================================
// checkVersionInFile and friends

function checkWordPressAndZenPhotoVersion($fileName, $path)
{
    global $VERSIONS, $CMS;

    if (strpos($path, 'administrator/components/com_jevents') !== false ||
        strpos($path, 'tiny-compress-images/test/wp-includes') !== false) {
        return false;
    }

    $version = findLineInFile($path.'/'.$fileName, 'wp_version = ');
    if ($version !== null) {
        // WordPress
        $realdir = dirname($path);

        $explosion = explode("'", $version);

        if (isset($explosion[1])) {
            $version = $explosion[1];
        }

        $CMS = '1';

        return array('name' => 'WordPress', 'version' => $version, 'dir' => $realdir,
            'updated' => checkIsUpdated($version, $VERSIONS['wordpress']),
            'config' => $realdir.'/wp-config.php', 'source' => $path . '/' . $fileName, );
    }

    // Zen Photo
    $version = findLineInFile($path.'/'.$fileName, 'ZENPHOTO_VERSION');
    if ($version == null) {
        return false;
    }

    $version = trim($version);
    $version = explode("'", $version);
    $version = $version[3];

    return array('name' => 'Zenphoto', 'version' => $version, 'dir' => $path.'/'.$fileName,
        'updated' => checkIsUpdated($version, $VERSIONS['zenphoto']), );
}

function checkJoomlaVersion($fileName, $path)
{
    global $VERSIONS, $CMS;

    $versionFile = '';
    if (file_exists("$path/includes/version.php")) {
        $versionFile = "$path/includes/version.php";
    } elseif (file_exists("$path/libraries/joomla/version.php")) {
        $versionFile = "$path/libraries/joomla/version.php";
    } elseif (file_exists("$path/libraries/cms/version.php")) {
        $versionFile = "$path/libraries/cms/version.php";
    } elseif (file_exists("$path/libraries/cms/version/version.php") && false === strpos($path, 'breezing-forms')) {
        $versionFile = "$path/libraries/cms/version/version.php";
    } elseif (file_exists("$path/libraries/src/Version.php")) {
        $versionFile = "$path/libraries/src/Version.php";
    } else {
        return false;
    }

    $version1 = findLineInFile($versionFile, 'RELEASE');
    if (!$version1) {
        return false;
    }

    $version1 = explode("'", $version1);
    $version2 = findLineInFile($versionFile, 'DEV_LEVEL');
    $version2 = explode("'", $version2);
    $version = $version1[1].'.'.$version2[1];

    $CMS = '1';

    return array('name' => 'Joomla', 'version' => $version, 'dir' => $path, 'source' => $versionFile,
        'updated' => checkIsUpdated($version, $VERSIONS['joomla']),
        'config' => $path.'/'.$fileName, );
}

function checkJceVersion($fileName, $path)
{
    global $VERSIONS;

    $firstLine = findLineInFile($path.'/'.$fileName, 'install');

    if (false === strpos($firstLine, 'component')) {
        return false;
    }

    $version = findLineInFile($path.'/'.$fileName, '<version>');
    $version = str_replace('<version>', '', $version);
    $version = str_replace('</version>', '', $version);
    $version = trim($version);
    $realdir = dirname($path);

    return array('name' => 'JCE component', 'version' => $version, 'dir' => $realdir,
        'updated' => checkIsUpdated($version, $VERSIONS['jce']), );
}

function checkjQueryFileUploadVersion($fileName, $path)
{
    /** Bring file to a string. */
    $fileContent = @file_get_contents("$path/$fileName");

    /** Exit in case the file does not contain the GitHub Address (not jQFU). */
    if (
        (empty($fileContent)) ||
        (false === strpos($fileContent, 'https://github.com/blueimp/jQuery-File-Upload'))
    ) {
        return false;
    }

    /**
     * Get current file hash, its version if possible and directory.
     * Set by default isUpdated to false until further check.
     */
    $fileHash = md5($fileContent);
    $version = findLineInFile($path.'/'.$fileName, '* jQuery File Upload Plugin');
    $version = trim($version);
    $version = preg_replace('@\*[\s]*jQuery File Upload Plugin[\s]*([\d\s]+)@', '$1', $version);
    if (preg_match('/[0-9]/', $version) === 0) {
        $version = 'Unknown.';
    }
    $realdir = dirname($path);
    $isUpdated = false;

    /**
     * Set isUpdated to true if the file has "IMAGETYPE_PNG" constant cause that
     * proves the file version has the known vulnerabilities patched.
     * @see https://github.com/blueimp/jQuery-File-Upload/blob/master/VULNERABILITIES.md
     * 
     * Otherwise, check if md5 hash matches the last version.
     */
    if (strpos($fileContent, 'IMAGETYPE_PNG') !== false) {
        $isUpdated = true;
    } else {
        /** Check the md5 hash of the last jQuery File Upload "UploadHandler.php". */
        $mostRecentUploadHandler = 'https://raw.githubusercontent.com/blueimp/jQuery-File-Upload/master/server/php/UploadHandler.php';
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $mostRecentUploadHandler);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, '20');
        curl_setopt($ch, CURLOPT_MAXREDIRS, '3');
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, '30');
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_USERAGENT, 'Sucuri Version Check '.$myversion);
        $mostRecentUploadHandlerContent = curl_exec($ch);
        curl_close($ch);

        /** Extract the md5 hash. */
        if (strpos($mostRecentUploadHandlerContent, 'jQuery-File-Upload') !== false) {
            $mostRecentUploadHandlerHash = md5($mostRecentUploadHandlerContent);
        } else {
            /** Set to true cause we were unable to verify the last version hash. */
            $isUpdated = true;
        }
    }

    /**
     * Set version to 9.25.1+ if file contains the "IMAGETYPE_PNG" constant
     * or to "Last available at GitHub" if md5 hash matches the latest commit.
     */
    if (empty($mostRecentUploadHandlerHash)) {
        if ($isUpdated) {
            $version = '9.25+';
        }
    } else {
        if ($fileHash === $mostRecentUploadHandlerHash) {
            $isUpdated = true;
            $version = 'Last available at GitHub.';
        }
    }

    /** Send result to main runtime. */
    return array(
        'name' => 'jQuery File Upload plugin',
        'version' => $version.' ',
        'dir' => $realdir,
        'updated' => $isUpdated,
    );
}

function checkDrupalVersion($fileName, $path)
{
    global $VERSIONS, $CMS;

    $version = findLineInFile($path.'/'.$fileName, "define('VERSION'");

    if (!$version) {
        return false;
    }

    $version = trim($version);
    $version = preg_replace("@^define\(\s*['\"]VERSION['\"]\s*,\s*['\"]([\d\.]+)['\"]\s*\);$@", '$1', $version);
    if ($fileName == 'bootstrap.inc') {
        $realdir = dirname($path);
    } else {
        $realdir = dirname(dirname($path));
    }

    $CMS = '1';

    return array('name' => 'Drupal', 'version' => $version, 'dir' => $realdir, 'source' => $path.'/'.$fileName,
        'updated' => checkIsUpdated($version, $VERSIONS['drupal']), );
}

function checkDrupal8Version($fileName, $path)
{
    global $VERSIONS, $CMS;
    
    $version = findLineInFile($path . '/' . $fileName, "const VERSION = '");

    if (!$version) {
        return false;
    }

    $version = trim($version);
    $version = preg_replace("@^const.VERSION.\=\s*\s*['\"]([\d\.]+)['\"]\s*;$@", '$1', $version);
    $realdir = dirname(dirname($path));

    $CMS = '1';
    return array('name' => 'Drupal', 'version' => $version, 'dir' => $realdir, 'source' => $path . '/' . $fileName,
        'updated' => checkIsUpdated($version, $VERSIONS['drupal']) );
}

function checkMagentoVersion($fileName, $path)
{
    global $VERSIONS, $CMS;

    $config = @file_get_contents("$path/$fileName");

    if ((false === $config) || (false === strpos($config, 'public static function getVersionInfo()'))) {
        return false;
    }

    $version = sprintf(
        '%s.%s.%s.%s',
        getOption('major', $config, OPT_TYPE_ASSOC),
        getOption('minor', $config, OPT_TYPE_ASSOC),
        getOption('revision', $config, OPT_TYPE_ASSOC),
        getOption('patch', $config, OPT_TYPE_ASSOC)
    );
    $realdir = dirname($path);

    $CMS = '1';

    return array('name' => 'Magento', 'version' => $version, 'dir' => $realdir, 'source' => $path.'/'.$fileName,
        'updated' => checkIsUpdated($version, $VERSIONS['magento']), );
}

//Magento 2 check
function checkMagento2Version($fileName, $path)
{
    global $VERSIONS, $CMS;
    if (strpos($path, 'dev/tests/integration/framework/Magento/TestFramework/Event') != false) {
        $originpath = $path;
        $path = str_replace('dev/tests/integration/framework/Magento/TestFramework/Event', '', $path);
        if (strpos($path, 'vendor/magento/magento2-base') != false) {
            if (file_exists($path.'composer.json')) {
                $fileName = 'composer.json';
                $config = file_get_contents($path.$fileName);
                preg_match('((?:\"version\"\: \"[0-9]+,)*[0-9]+(?:\.[0-9]+)(?:\.[0-9]+)":?)', $config, $version);
                $version = $version['0'];
                $version = str_replace('"', '', $version);
                $path = str_replace('vendor/magento', '', $path);
                $realdir = dirname($path);

                $CMS = '1';

                return array('name' => 'Magento 2', 'version' => $version, 'dir' => $realdir, 'source' => $originpath.'/'.$fileName,
                'updated' => checkIsUpdated($version, $VERSIONS['magento']), );
            }
        } else {
            return;
        }
    }
}

function checkModXVersion($fileName, $path)
{
    global $VERSIONS, $CMS;

    $config = @file_get_contents("$path/$fileName");
    if ($config === false) {
        return false;
    }

    $version = getOption('$modx_version', $config, OPT_TYPE_VAR);
    if ($version) {
        return array('name' => 'ModX Evolution', 'version' => $version, 'dir' => $path.'/'.$fileName,
            'updated' => checkIsUpdated($version, $VERSIONS['modX']['evolution']), );
    }

    if (!preg_match_all('@\$v\[\'([\S]+)\'\]\s*=\s*[\'](.*)[\'];@', $config, $m)) {
        return false;
    }
    if (!isset($m[2][0], $m[2][1], $m[2][2], $m[2][3])) {
        return false;
    }

    $realdir = dirname($path);
    if (substr($realdir, -5) == '/core') {
        $realdir = substr_replace($realdir, '', -5);
    }

    $CMS = '1';
    $version = sprintf('%s.%s.%s-%s', $m[2][0], $m[2][1], $m[2][2], $m[2][3]);

    return array('name' => 'ModX Revolution', 'version' => $version, 'dir' => $realdir, 'source' => $path.'/'.$fileName,
        'updated' => checkIsUpdated($version, $VERSIONS['modX']['revolution']), );
}

function checkPhpBbVersion($fileName, $path)
{
    global $VERSIONS, $CMS;

    if (!preg_match('~@?define\(.PHPBB_VERSION., +.(\S+).\);~', findLineInFile($path.'/'.$fileName, "'PHPBB_VERSION'"), $matches)) {
        return false;
    }
    $version = $matches[1];
    $realdir = dirname($path);

    $CMS = '1';

    return array('name' => 'PHPBB', 'version' => $version, 'dir' => $realdir,
        'updated' => checkIsUpdated($version, $VERSIONS['phpbb']), );
}

function checkVbulletinVersion($fileName, $path)
{
    global $VERSIONS, $CMS;

    $version = findLineInFile($path.'/'.$fileName, "'vbulletin'");

    if (!$version) {
        return false;
    }

    $version = str_replace("\t\t\$md5_sum_versions = array('vbulletin' => '", '', $version);
    $version = str_replace("');", '', $version);
    $realdir = dirname($path);

    $CMS = '1';

    return array('name' => 'vBulletin', 'version' => $version, 'dir' => $realdir, 'source' => $path.'/'.$fileName,
        'updated' => checkIsUpdated($version, $VERSIONS['vbulletin']), );
}

function checkOsCommerceVersion($fileName, $path)
{
    global $VERSIONS, $CMS;
    $verRegex = '[0-9.]+[ -]?[0-9a-zA-Z]{0,5}'; // Examples: "2.3.1"   "2.3.3.2"   "2.2-MS2"  "2.2 RC2a"  "2.2RC2a"

    $realdir = dirname($path);

    // See https://github.com/osCommerce/oscommerce2/commits/23/catalog/includes/version.php
    $versionFileNames = array("$path/includes/version.php", "$path/includes/version.txt",
        "$path/includes/OSC/version.txt", );

    $version = false;

    foreach ($versionFileNames as $fileName) {
        $config = @file_get_contents($fileName);
        if ($config && preg_match('/^'.$verRegex.'$/', $config)) {
            $version = $config;
            $versionFile = $fileName;
            break;
        }
    }

    if (!$version) {
        $versionFile = "$path/includes/application_top.php";
        $config = @file_get_contents($versionFile);
        $option = getOption('PROJECT_VERSION', $config, OPT_TYPE_CONST);
        // PROJECT_VERSION can be something like "osCommerce 2.2-MS2" or "osCommerce Online Merchant v2.2 RC1"
        if (preg_match('/('.$verRegex.')$/', $option, $m)) {
            $version = $m[1];
        }
    }

    if (!$version) {
        return false;
    }

    $CMS = '1';

    return array('name' => 'osCommerce', 'version' => $version, 'dir' => $realdir, 'source' => $versionFile,
        'updated' => checkIsUpdated($version, $VERSIONS['oscommerce']), );
}

function checkPrestaShopVersion($fileName, $path)
{
    global $VERSIONS, $CMS;

    $realdir = dirname($path);

    $version = null;

    if (file_exists("$realdir/config/autoload.php")) {
        $versionFile = "$realdir/config/autoload.php";
        $config = @file_get_contents($versionFile);
        $version = getOption('_PS_VERSION_', $config, OPT_TYPE_CONST);
    }

    if (!$version && file_exists("$realdir/docs/readme_en.txt")) {
        $versionFile = "$realdir/docs/readme_en.txt";
        $line = findLineInFile($versionFile, 'NAME: Prestashop ');
        $version = str_replace(array("\r", "\n", 'NAME: Prestashop '), '', $line);
    }

    if (!$version) {
        return false;
    }

    $CMS = '1';

    return array('name' => 'PrestaShop', 'version' => $version, 'dir' => $realdir, 'source' => $versionFile,
        'updated' => checkIsUpdated($version, $VERSIONS['prestashop']), );
}

function checkOpenCartVersion($fileName, $path)
{
    global $VERSIONS, $CMS;

    if (false === strpos($path, 'admin/controller/catalog')) {
        return false;
    }

    $realdir = dirname(dirname(dirname($path)));

    if (!file_exists("$realdir/admin/index.php")) {
        return false;
    }

    $config = @file_get_contents("$realdir/admin/index.php");
    $version = getOption('VERSION', $config, OPT_TYPE_CONST);

    if (!$version) {
        return false;
    }

    $CMS = '1';

    return array('name' => 'OpenCart', 'version' => $version, 'dir' => $realdir,
        'updated' => checkIsUpdated($version, $VERSIONS['opencart']), );
}

function checkTypo3Version($fileName, $path)
{
    global $VERSIONS;

    $realdir = $fileName === 'config_default.php' ? dirname($path) : dirname(dirname(dirname(dirname($path))));

    $config = @file_get_contents($path.'/'.$fileName);

    $version = getOption('TYPO3_version', $config, OPT_TYPE_CONST);
    if (!$version && $fileName === 'config_default.php') {
        // Before 4.8
        $version = getOption('$TYPO_VERSION', $config, OPT_TYPE_VAR);
    }

    if (!$version) {
        return false;
    }

    return array('name' => 'Typo3', 'version' => $version, 'dir' => $realdir, 'source' => $fileName,
        'updated' => checkIsUpdated($version, $VERSIONS['typo3']), );
}
function checkTDThemesVersion($fileName, $path)
{
    global $VERSIONS;

    $version = findLineInFile($path.'/'.$fileName, 'define("TD_THEME_VERSION", ');

    if (!$version) {
        return false;
    }

    $version = trim($version);
    $version = preg_replace("@define\(\"TD_THEME_VERSION\", \"(\S+)\"\);@", '$1', $version);
    $themename = findLineInFile($path.'/'.$fileName, 'define("TD_THEME_NAME", ');
    $themename = trim(preg_replace("@define\(\"TD_THEME_NAME\", \"(\S+)\"\);@", '$1', $themename));
    if (!$themename) {
        return false;
    }

    return array('name' => 'tagDiv Theme '.$themename, 'version' => $version, 'dir' => $path.'/'.$fileName, 'updated' => checkIsUpdated($version, $VERSIONS[$themename]));
}

function checkTimThumbVersion($fileName, $path)
{
    global $VERSIONS;

    /* Check for timthumb version */
    if (!findLineInFile($path.'/'.$fileName, 'TimThumb')) {
        return false;
    }

    $version = findLineInFile($path.'/'.$fileName, "'VERSION'");
    $rversion = explode("'", $version);

    if (!isset($rversion[3])) {
        return false;
    }

    return array('name' => 'TimThumb', 'version' => $rversion[3], 'dir' => $path.'/'.$fileName,
        'updated' => checkIsUpdated($rversion[3], $VERSIONS['timthumb']), );
}

function checkRevSliderVersion($fileName, $path)
{
    $config = @file_get_contents("$path/$fileName");
    if (false === $config) {
        return false;
    }

    $version = getOption('$revSliderVersion', $config, OPT_TYPE_VAR);
    if (!$version) {
        return false;
    }

    if (version_compare($version, '4.1.5') < 0) {
        return array('name' => 'Slider Revolution', 'version' => $version, 'dir' => $path.'/'.$fileName,
            'oldplugin' => true, 'url' => 'https://www.themepunch.com/faq/how-to-update-the-slider/', );
    }

    return false;
}

function checkShowBizVersion($fileName, $path)
{
    $config = @file_get_contents("$path/$fileName");
    if (false === $config) {
        return false;
    }

    $version = getOption('$showbizVersion', $config, OPT_TYPE_VAR);
    if (!$version) {
        return false;
    }

    if (version_compare($version, '1.7.2') < 0) {
        return array('name' => 'ShowBiz Plugin', 'version' => $version, 'dir' => $path.'/'.$fileName,
            'oldplugin' => true, );
    }

    return false;
}

function checkDzsVideoGalleryVersion($fileName, $path)
{
    if (false === strpos($path, '/wp-content/plugins/dzs-videogallery/')) {
        return false;
    }

    $version = findLineInFile($path.'/'.$fileName, 'DZS Upload');

    if (strpos($version, 'version: 0.') !== false || strpos($version, 'version: 1.0') !== false) {
        return array('name' => 'dzs-videogallery', 'version' => $version, 'dir' => $path.'/'.$fileName,
            'oldplugin' => true, );
    }

    return false;
}

function checkFreemiusVersion($fileName, $path)
{

    if (false === strpos($path, '/freemius')) {
        return false;
    }

    $version = findLineInFile($path.'/'.$fileName, '$this_sdk_version =');
    $patterns[0] = '/\$this_sdk_version = \'/';
    $patterns[1] = '/\';/';
    $replacement = '';
    $version = trim(preg_replace($patterns, $replacement, $version));

    if (version_compare($version, '2.2.3') <= 0) {
        return array('name' => 'Freemius Library', 'version' => $version, 'dir' => $path.'/'.$fileName, 'oldplugin' => true, 'url' => 'https://wpvulndb.com/vulnerabilities/9223', );
    }

    return false;
}

function checkPhpMailerVersion($fileName, $path)
{

    //confirming we're having right class
    if (!findLineInFile($path.'/'.$fileName, 'https://github.com/PHPMailer/')) {
        return false;
    }

    //extracting only the version string
    $version = findLineInFile($path.'/'.$fileName, "Version:");
    if (preg_match("/\d\.\d\.\d/", $version, $matches)) {
        $version = $matches[0];
    }
    else {
        return false;
    }

    // Compares if plugin version is 6.0.5 or lower (first safe version is 6.0.6)
    if (version_compare($version, '6.0.5') <= 0) {
        return array('name' => 'PHPMailer Class', 'version' => $version, 'dir' => $path.'/'.$fileName,
            'oldplugin' => true, 'url' => 'https://github.com/PHPMailer/PHPMailer/blob/master/SECURITY.md', );
    }

    return false;
}

function checkPluginIndexVersion($fileName, $path)
{
    // if there will be more cases of plugins using index.php as version file, we'll need to add new array of pairs
    // such as "/hybrid-composer" => "1.4.6"
    // and rework this whole function into using this new array

    if (false !== strpos($path, '/hybrid-composer')) {
        //getting the line with version number
        $version = findLineInFile($path.'/'.$fileName, '* Version:');

        // keeping just the version value
        $version = trim(preg_replace("/\* Version: /", '$1', $version));

        // Compares if plugin version is 1.4.6 or lower
        if (version_compare($version, '1.4.6') <= 0) {
            return array('name' => 'WPTF Hybrid Composer', 'version' => $version, 'dir' => $path.'/'.$fileName,
                'oldplugin' => true, 'url' => 'https://labs.sucuri.net/wptf-hybrid-composer-unauthenticated-arbitrary-options-update/', );
        }
    }

    // another else if could be added for other plugins with index.php as plugin version file
    // if there will be too many such plugins, this structure can be reworked into another array

    return false;
}

// The plugin is using the filename "Main.php" which is very generic.
// With that, we created a function to detect and alert for such cases.
    
function checkPluginMainVersion($fileName, $path)
{
    if (false !== strpos($path, '/ultimate-faqs')) {
        //getting the line with version number
        $version = findLineInFile($path.'/'.$fileName, 'Version: ');

        // keeping just the version value
        $version = trim(preg_replace("/Version: /", '$1', $version));

        // Compares if plugin version is 1.8.21 or lower
        if (version_compare($version, '1.8.21') <= 0) {
            return array('name' => 'Ultimate FAQ', 'version' => $version, 'dir' => $path.'/'.$fileName,
            'oldplugin' => true, 'url' => 'https://labs.sucuri.net/malware-campaign-evolves-to-target-new-plugins-may-2019/', );
        }
    }

    // another else if could be added for other plugins with Main.php as plugin version file
    // if there will be too many such plugins, this structure can be reworked into another array

    return false;

}

function checkPluginInitVersion($fileName, $path)
{
    // if there will be more cases of plugins using index.php as version file, we'll need to add new array of pairs
    // such as "/hybrid-composer" => "1.4.6"
    // and rework this whole function into using this new array

    //workaround to ensure we're checking init.php in the plugin root
    if (false !== strpos($path.$fileName, '/ithemes-syncinit.php')) {
        //getting the line with version number
        $version = findLineInFile($path.'/'.$fileName, 'Version:');

        // keeping just the version value
        $version = trim(preg_replace("/Version: /", '$1', $version));

        // Compares if plugin version is 1.4.6 or lower
        if (version_compare($version, '2.0.17') <= 0) {
            return array('name' => 'iThemes Sync', 'version' => $version, 'dir' => $path.'/'.$fileName,
                'oldplugin' => true, 'url' => 'https://ithemes.com/important-ithemes-sync-vulnerability-patched/', );
        }
    }

    // Added support to InfiniteWP Client since it uses init.php as the initializer for the plugin
    if (false !== strpos($path.$fileName, '/iwp-client')) {
        //getting the line with version number
        $version = findLineInFile($path.'/'.$fileName, 'Version:');

        // keeping just the version value
        $version = trim(preg_replace("/Version: /", '$1', $version));

        // Compares if plugin version is 1.9.4.4 or lower
        if (version_compare($version, '1.9.4.4') <= 0) {
            return array('name' => 'InfiniteWP Client', 'version' => $version, 'dir' => $path.'/'.$fileName,
                'oldplugin' => true, 'url' => 'https://blog.sucuri.net/2020/01/authentication-bypass-vulnerability-in-infinitewp-client.html', );
        }
    }

    // another else if could be added for other plugins with index.php as plugin version file
    // if there will be too many such plugins, this structure can be reworked into another array

    return false;
}

function checkThemeStyleVersion($fileName, $path)
{
    // if there will be more cases of themes using style.css as version file, we'll need to add new array of pairs
    // such as "/onetone/" => "3.0.6"
    // and rework this whole function into using this new array

    if (false !== strpos($path . '/' . $fileName, '/onetone/')) {
        //getting the line with version number
        $version = findLineInFile($path . '/' . $fileName, 'Version:');

        // keeping just the version value
        $version = trim(preg_replace("/Version: /", '$1', $version));

        // Compares if plugin version is 3.0.6 or lower
        if (version_compare($version, '3.0.6') <= 0) {
            return array('name' => 'OneTone Theme', 'version' => $version, 'dir' => $path.'/', 'updated' => false, 'url' => 'https://wpvulndb.com/themes/onetone', );
        }
    }

    return false;
}


function checkUploadifyVersion($fileName, $path)
{
    return array('name' => 'uploadify');
}

function checkPHPUnitVersion($fileName, $path)
{
    if (false === strpos($path . '/' . $fileName, '/Util/PHP/eval-stdin.php')) {
        return false;
    }
    
    // Check that the file contains the vulnerable code (reading from php://input)
    $contents = @file_get_contents($path . '/' . $fileName, false, null, 0, 1024);
    
    if (strpos($contents, "eval('?>' . file_get_contents('php://input'));") === false) {
        return false; // not vulnerable
    }
    
    // Find the PHPUnit version
    $realdir = dirname(dirname($path));
    
    $version = findLineInFile($realdir . '/Runner/Version.php', 'SebastianBergmann\\Version');
    
    if ($version !== null && preg_match("/SebastianBergmann\\\\Version\\('([0-9.]+)',/", $version, $m)) {   
        $version = $m[1];
    } else {
        $version = findLineInFile($realdir . '/Runner/Version.php', "const VERSION = '");
        if ($version !== null && preg_match("/const VERSION = '([0-9.]+)'/", $version, $m)) {
            $version = $m[1];
        } else {
            $version = 'unknown';
        }
    }

    return array('name' => 'phpunit', 'version' => $version, 'dir' => $path.'/', 'updated' => false);
}

function checkElementorVersion($fileName, $path)
{
    if (false === strpos($path . '/' . $fileName, 'plugins/elementor/elementor.php')) {
        return false;
    }
    
    $version = findLineInFile($path . '/' . $fileName, '* Version:');
    $version = trim(preg_replace("/ \* Version: /", '$1', $version));
    
    if (version_compare($version, '2.9.7') <= 0) {
        return array('name' => 'Elementor', 'version' => $version, 'dir' => $path.'/', 'updated' => false, 'url' => 'https://wpvulndb.com/vulnerabilities/10213', );
    }
}


function checkHtaccess($fileName, $path)
{
    return array('name' => 'htaccess');
}

// ==================================
// The main version check loop

$VERSION_CHECKS = array(
    'system.module' => 'checkDrupalVersion',
    'bootstrap.inc' => 'checkDrupalVersion',
    'Drupal.php' => 'checkDrupal8Version',
    'Mage.php' => 'checkMagentoVersion',
    'Magento.php' => 'checkMagento2Version',
    'version.inc.php' => 'checkModXVersion',
    'configuration.php' => 'checkJoomlaVersion',
    'jce.xml' => 'checkJceVersion',
    'constants.php' => 'checkPhpBbVersion',
    'diagnostic.php' => 'checkVBulletinVersion',
    'version.php' => 'checkWordPressAndZenPhotoVersion',
    'checkout_shipping_address.php' => 'checkOsCommerceVersion',
    'revslider.php' => 'checkRevSliderVersion',
    'showbiz.php' => 'checkShowBizVersion',
    'TranslatedConfiguration.php' => 'checkPrestaShopVersion',
    'manufacturer.php' => 'checkOpenCartVersion',
    'upload.php' => 'checkDzsVideoGalleryVersion',
    'uploadify.php' => 'checkUploadifyVersion',
    '.htaccess' => 'checkHtaccess',
    'config_default.php' => 'checkTypo3Version',
    'SystemEnvironmentBuilder.php' => 'checkTypo3Version',
    'td_config.php' => 'checkTDThemesVersion',
    'UploadHandler.php' => 'checkjQueryFileUploadVersion',
    'index.php' => 'checkPluginIndexVersion',
    'Main.php' => 'checkPluginMainVersion',
    'start.php' => 'checkFreemiusVersion',
    'init.php' => 'checkPluginInitVersion',
    'phpmailer.php' => 'checkPhpMailerVersion',
    'style.css' => 'checkThemeStyleVersion',
    'eval-stdin.php' => 'checkPHPUnitVersion',
    'elementor.php' => 'checkElementorVersion',
);

function runVersionCheck($path, $checkFound)
{
    global $VULNERABLE_PLUGINS, $VERSION_CHECKS, $isNoise, $CMS;

    $dh = @opendir($path);
    if (!$dh) {
        if ($isNoise) {
            echo 'Open directory failed: '.escapeHtml($path)."\n";
        }

        return;
    }

    while (($fileName = @readdir($dh)) !== false) {
        if ($fileName === '.' || $fileName == '..' || strpos($fileName, 'sucuribackup.') !== false) {
            continue;
        }

        $fullName = $path.'/'.$fileName;
        if (@is_link($fullName)) {
            if ($isNoise) {
                echo 'Skipping symlink directory: '.escapeHtml($fullName)."\n";
            }
            continue;
        }

        $res = false;
        if (isset($VERSION_CHECKS[$fileName])) {
            $CMS = '0';
            $res = call_user_func($VERSION_CHECKS[$fileName], $fileName, $path);
        } elseif (strpos($fileName, '.php') !== false &&
            (strpos($fileName, 'thumb') !== false ||
            strpos($fileName, 'Thumb') !== false ||
            strpos($fileName, 'crop') !== false)) {
            $res = checkTimThumbVersion($fileName, $path);
            $CMS = '0';
        } elseif (false !== strpos($path, '/wp-content/plugins/') && isset($VULNERABLE_PLUGINS[$fileName])) {
            $CMS = '0';
            $res = checkWpPlugin($path, $fileName);
        }
        if ($res) {
            call_user_func($checkFound, $fullName, $res);
        }

        if (@is_dir($fullName)) {
            if ($isNoise) {
                echo '    Reading Dir: '.escapeHtml($fullName)."\n";
            }
            runVersionCheck($fullName, $checkFound);
            @flush();
        }
    }
    closedir($dh);
}

// jsonDcode implementation that works with small jsons

class JSONError
{
}

$JSON_ESCAPE_CHARS = array('"' => '"', '\\' => '\\', '/' => '/', 'b' => "\10",
    'f' => "\f", 'n' => "\n", 'r' => "\r", 't' => "\t");

function jsonParseStr($json, &$pos)
{
    global $JSON_ESCAPE_CHARS;
    
    if ($pos >= strlen($json)) {
        return new JSONError;
    }
    
    $str = '';

    $start = $pos;
    
    while ($json[$pos] !== '"') {
        if ($json[$pos] !== '\\') {
            $pos++;
            if ($pos >= strlen($json)) {
                return new JSONError; // unterminated string
            }
            continue;
        }
        $str .= substr($json, $start, $pos - $start);

        $pos++;
        if ($pos >= strlen($json)) {
            return new JSONError; // unterminated escape sequence
        }
        
        if (isset($JSON_ESCAPE_CHARS[$json[$pos]])) {
            $str .= $JSON_ESCAPE_CHARS[$json[$pos]];
            $pos++;
            if ($pos >= strlen($json)) {
                return new JSONError; // unterminated string
            }
        } elseif ($json[$pos] === 'u' && $pos + 5 < strlen($json) &&
            ctype_xdigit(substr($json, $pos + 1, 4))) {
            $charCode = hexdec(substr($json, $pos + 1, 4));
            $pos += 5;
            // Encode UTF-8 (characters outside BMP are not supported in this version)
            if ($charCode < 0x80) {
                $str .= chr($charCode);
            } elseif ($charCode < 0x800) {
                $str .= chr(($charCode >> 6) | 0xC0) . chr(($charCode & 0x3F) | 0x80);
            } else {
                $str .= chr(($charCode >> 12) | 0xE0) . chr((($charCode >> 6) & 0x3F) | 0x80) .
                    chr(($charCode & 0x3F) | 0x80);
            }
        } else {
            return new JSONError; // invalid escape sequence
        }
        $start = $pos;
    }
    $str .= substr($json, $start, $pos - $start);
    
    $pos++;
    return $str;
}

function jsonSkipSpaces($json, &$pos)
{
    if ($pos >= strlen($json)) {
        return false;
    }
    
    while ($json[$pos] === ' ' || $json[$pos] === '\t' || $json[$pos] === '\n' || $json[$pos] === '\r') {
        $pos++;

        if ($pos >= strlen($json)) {
            return false;
        }
    }
    
    return true;
}

function jsonParseArray($json, &$pos, $isAssoc)
{
    $array = array();
    
    // Check for an empty array
    if (!jsonSkipSpaces($json, $pos)) {
        return new JSONError;
    }
    if ($json[$pos] === ']') {
        $pos++;
        return $array;
    }
    
    for (;;) {
        // Value
        $value = jsonParseAtom($json, $pos, $isAssoc);
        if (is_object($value) && get_class($value) === 'JSONError') {
            return $value;
        }
        $array[] = $value;

        // A comma or a closing bracket
        if (!jsonSkipSpaces($json, $pos)) {
            return new JSONError;
        }
    
        if ($json[$pos] === ']') {
            $pos++;
            return $array;
        } elseif ($json[$pos] !== ',') {
            return new JSONError; // expected bracket or comma
        }
        
        $pos++;
    }
}

function jsonParseObject($json, &$pos, $isAssoc)
{
    $object = $isAssoc ? array() : new stdClass;
    
    // Check for an empty object
    if (!jsonSkipSpaces($json, $pos)) {
        return new JSONError;
    }
    if ($json[$pos] === '}') {
        $pos++;
        return $object;
    }
    
    for (;;) {
        // Name
        if (!jsonSkipSpaces($json, $pos) || $json[$pos] !== '"') {
            return new JSONError;
        }
        $pos++;
        
        $name = jsonParseStr($json, $pos);
        if (is_object($name) && get_class($name) === 'JSONError') {
            return $name;
        }
    
        if (!jsonSkipSpaces($json, $pos) || $json[$pos] !== ':') {
            return new JSONError;
        }
        $pos++;
        
        // Value
        $value = jsonParseAtom($json, $pos, $isAssoc);
        if (is_object($value) && get_class($value) === 'JSONError') {
            return $value;
        }
        if ($isAssoc) {
            $object[$name] = $value;
        } else {
            // Add numeric names as strings
            // (if we convert array to an object instead, then the numeric keys will be inaccessible)
            $object->{$name} = $value;
        }

        // A comma or a closing brace
        if (!jsonSkipSpaces($json, $pos)) {
            return new JSONError;
        }
    
        if ($json[$pos] === '}') {
            $pos++;
            return $object;
        } elseif ($json[$pos] !== ',') {
            return new JSONError; // expected bracket or comma
        }
        $pos++;
    }
}

function jsonParseAtom($json, &$pos, $isAssoc)
{
    if (!jsonSkipSpaces($json, $pos)) {
        return new JSONError;
    }
    $ch = $json[$pos];
    
    if ($ch === '"') {
        $pos++;
        return jsonParseStr($json, $pos);
    } elseif ($ch === '-' || '0' <= $ch && $ch <= '9') {
        $atom = substr($json, $pos);
        if (preg_match('/^-?(?:0|[1-9][0-9]{0,8}+)(?![.eE])/', $atom, $match)) {
            $pos += strlen($match[0]);
            return intval($atom);
        } elseif (preg_match('/^-?(?:0|[1-9][0-9]*+)(?:\.[0-9]++)?(?:[eE][+-]?[0-9]++)?/', $atom, $match)) {
            $pos += strlen($match[0]);
            return floatval($atom);
        } else {
            return new JSONError; // Invalid number
        }
    } elseif ($ch === '[') {
        $pos++;
        return jsonParseArray($json, $pos, $isAssoc);
    } elseif ($ch === '{') {
        $pos++;
        return jsonParseObject($json, $pos, $isAssoc);
    } elseif ($ch === 'f' && substr($json, $pos, 5) === 'false') {
        $pos += 5;
        return false;
    } elseif ($ch === 't' && substr($json, $pos, 4) === 'true') {
        $pos += 4;
        return true;
    } elseif ($ch === 'n' && substr($json, $pos, 4) === 'null') {
        $pos += 4;
        return null;
    } else {
        return new JSONError;
    }
}

function jsonDcode($json, $isAssoc = false, $allowInternal = true)
{
    if (function_exists('json_decode') && $allowInternal) {
        return json_decode($json, $isAssoc);
    }
    
    $pos = 0;
    $ret = jsonParseAtom($json, $pos, $isAssoc);
    
    if (jsonSkipSpaces($json, $pos) || is_object($ret) && get_class($ret) === 'JSONError') {
        return null;
    }
    
    return $ret;
}

// ======================================================
// DB abstraction layer

class MySQLCommon
{
    function escapeName($name)
    {
        // See https://dev.mysql.com/doc/refman/5.7/en/identifiers.html
        return '"' . str_replace('"', '""', $name) . '"';
    }

    function begin()
    {
        $this->query('SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;');
        $this->query('BEGIN;');
    }

    function commit()
    {
        $this->query('COMMIT;');
    }

    function rollback()
    {
        $this->query('ROLLBACK;');
    }

    function getTablesColumns($config, $withCompositeKeys = false)
    {
        $scannedTypes = array(
            'varchar' => 1, 'char' => 1,
            'tinytext' => 1, 'mediumtext' => 1, 'text' => 1, 'longtext' => 1,
            'binary' => 1, 'varbinary' => 1,
            'tinyblob' => 1, 'blob' => 1, 'mediumblob' => 1, 'longblob' => 1);

        // Get all tables
        $tablesResult = $this->query('SHOW TABLES;');

        $allTables = array();
        while ($tablesRow = $this->fetchRow($tablesResult)) {
            // Report tables that does not begin with the Wordpress prefix
            $table = $tablesRow[0];
            if (!empty($config['prefix']) && 0 !== strncmp($table, $config['prefix'], strlen($config['prefix'])) &&
                !isset($_GET['dump'])) {
                printMsg("\nWARN: Table " . $table . ' does not start with prefix ' .
                    $config['prefix'] . ';', ' whitelists and CMS-specific rules will not work for this table');
            }

            $allTables[$table] = array();

            // Get all columns
            $columnsResult = $this->query('SHOW COLUMNS FROM ' . $this->escapeName($table) . ';');
            $uniqueString = null;
            $compositePrimary = false;
            $compositeKeys = array();
            while ($columnsRow = $this->fetchAssoc($columnsResult)) {
                $type = $columnsRow['Type'];
                $pos = strpos($type, "(");
                if ($pos !== false) {
                    $type = substr($type, 0, $pos);
                }

                if (strtoupper($columnsRow['Key']) === 'PRI') {
                    if (isset($allTables[$table]['idname'])) {
                        $compositePrimary = true;
                    }

                    if ($withCompositeKeys) {
                        $compositeKeys[] = $columnsRow['Field'];
                    }

                    $allTables[$table]['idname'] = $columnsRow['Field'];
                }

                $type = strtolower(trim($type));
                if (!isset($scannedTypes[$type])) {
                    continue;
                }

                // Prefer unique string keys to display wp_options.option_name instead of the numeric ID
                if (strtoupper($columnsRow['Key']) === 'UNI') {
                    $uniqueString = $columnsRow['Field'];
                }

                $allTables[$table][] = $columnsRow['Field'];
            }

            if ($uniqueString !== null) {
                $allTables[$table]['idname'] = $uniqueString;
            } elseif ($withCompositeKeys && count($compositeKeys) > 1) {
                /* return idname as an array of primary keys */
                $allTables[$table]['idname'] = $compositeKeys[0];
                
                //patch to attempt to fix composite keys index in drupal table field_data_body and others
                if($allTables[$table]['idname'] == 'entity_type'){
                    $allTables[$table]['idname'] = 'entity_id';
                }
            } elseif ($compositePrimary) { // Disable the cleanup for composite primary keys and no primary key
                unset($allTables[$table]['idname']);
            }
        }
        return $allTables;
    }

    function getColumnNamesTypes($table)
    {
        $columnsResult = $this->query('SHOW COLUMNS FROM ' . $this->escapeName($table) . ';');

        $binaryTypes = array('binary' => 1, 'varbinary' => 1,
            'tinyblob' => 1, 'blob' => 1, 'mediumblob' => 1, 'longblob' => 1);

        $numericTypes = array('tinyint' => 1, 'smallint' => 1, 'mediumint' => 1,
            'int' => 1, 'integer' => 1, 'bigint' => 1,
            'decimal' => 1, 'dec' => 1, 'numeric' => 1, 'fixed' => 1,
            'float' => 1, 'real' => 1, 'double' => 1, 'double precision' => 1);

        $names = array();
        $types = array();

        while ($columnsRow = $this->fetchAssoc($columnsResult)) {
            $names[] = $columnsRow['Field'];

            $type = $columnsRow['Type'];
            $pos = strpos($type, "(");
            if ($pos !== false) {
                $type = substr($type, 0, $pos);
            }
            $type = strtolower(trim($type));

            if (isset($numericTypes[$type])) {
                $types[] = 'num';
            } elseif (isset($binaryTypes[$type])) {
                $types[] = 'bin';
            } elseif ($type === 'bit') {
                $types[] = 'bit';
            } else {
                $types[] = 'other';
            }
        }

        return array($names, $types);
    }

    function exportRow($row, $types)
    {
        for ($i = 0; $i < count($row); $i++) {
            if ($row[$i] === null) {
                $row[$i] = 'NULL';
            } elseif ($types[$i] === 'bin') {
                // Story binary data as hex to avoid encoding problems
                $row[$i] = 'UNHEX(' . $this->escapeStr(bin2hex($row[$i])) . ')';
            } elseif ($types[$i] === 'bit') {
                // TODO: tests under PHP 4
                // See https://stackoverflow.com/questions/15106985/
                $row[$i] = "b'" . decbin($row[$i]) . "'";
            } elseif ($types[$i] === 'num') {
                $row[$i] = (string)$row[$i];
            } else {
                $row[$i] = $this->escapeStr($row[$i]);
            }
        }
        return implode(', ', $row);
    }

    function getCreateTableSql($table)
    {
        $res = $this->query('SHOW CREATE TABLE ' . $this->escapeName($table) . ';');
        $row = $this->fetchRow($res);

        if (!$row) {
            exit('Unexpected empty result from SHOW CREATE TABLE');
        }

        return $row[1] . ";\n\n";
    }

    function getCreateDBSql($db)
    {
        $res = $this->query('SHOW CREATE DATABASE ' . $this->escapeName($db) . ';');
        $row = $this->fetchRow($res);

        if (!$row) {
            exit('Unexpected empty result from SHOW CREATE DATABASE');
        }

        return $row[1] . ";\nUSE " . $this->escapeName($db) . ";\n\n";
    }

    function getDumpHeader()
    {
        return
            "SET NAMES 'utf8';\n" .
            "SET sql_mode='NO_AUTO_VALUE_ON_ZERO,ANSI_QUOTES,STRICT_TRANS_TABLES';\n" .
            "SET foreign_key_checks=0;\n";
    }

    function setModes()
    {
        $this->query("SET NAMES 'utf8';");
        $this->query("SET sql_mode='PIPES_AS_CONCAT,IGNORE_SPACE," .
            "NO_AUTO_VALUE_ON_ZERO,ANSI_QUOTES,STRICT_TRANS_TABLES';");
    }
}

class MySQLDriver extends MySQLCommon
{
    var $link;

    function connect($config)
    {
        // Connect to DB
        $host = $config['host'];
        if (isset($config['port'])) {
            $host .= ':' . $config['port'];
        }
        $this->link = @mysql_connect($host, $config['user'], $config['pass']);
        if ($this->link === false) {
            return 'Could not connect to MySQL database: ' . mysql_error();
        }

        mysql_select_db($config['db'], $this->link);

        $this->setModes();
        return true;
    }

    function disconnect()
    {
        mysql_close($this->link);
    }

    function escapeStr($str)
    {
        return "'" . mysql_real_escape_string($str, $this->link) . "'";
    }

    function query($sql, $forceResReturn = false)
    {
        global $isPlainText;
        $res = mysql_query($sql, $this->link);
        if (!$res) {
            if ($forceResReturn === true) {
                return $res;
            }

            $error = mysql_error($this->link);
            if (!$isPlainText) {
                $error = '<b>' . escapeHtml($error) . '</b>';
            }
            echo "\n" . escapeHtml(substr($sql, 0, 100)) . ' -- ' . $error . "\n";
            exit(1);
        }
        return $res;
    }

    function fetchAssoc($res)
    {
        $row = mysql_fetch_assoc($res);
        if (false === $row) {
            mysql_free_result($res);
        }
        return $row;
    }

    function fetchRow($res)
    {
        $row = mysql_fetch_row($res);
        if (false === $row) {
            mysql_free_result($res);
        }
        return $row;
    }

    function getAffectedRows()
    {
        return mysql_affected_rows($this->link);
    }

    function getName()
    {
        return 'MySQL database';
    }
}

class MySQLIDriver extends MySQLCommon
{
    var $link;

    function connect($config)
    {
        // Connect to DB
        $port = isset($config['port']) ? $config['port'] : null;
        $this->link = @mysqli_connect($config['host'], $config['user'], $config['pass'], $config['db'], $port);
        if ($this->link === false) {
            return 'Could not connect to MySQLi database: ' . mysqli_connect_error();
        }

        $this->setModes();
        return true;
    }

    function disconnect()
    {
        mysqli_close($this->link);
    }

    function escapeStr($str)
    {
        return "'" . mysqli_real_escape_string($this->link, $str) . "'";
    }

    function query($sql, $forceResReturn = false)
    {
        global $isPlainText;
        $res = mysqli_query($this->link, $sql);
        if (!$res) {
            if ($forceResReturn === true) {
                return $res;
            }

            $error = mysqli_error($this->link);
            if (!$isPlainText) {
                $error = '<b>' . escapeHtml($error) . '</b>';
            }

            echo "\n" . escapeHtml(substr($sql, 0, 100)) . ' -- ' . $error . "\n";
            exit(1);
        }
        return $res;
    }

    function fetchAssoc($res)
    {
        $row = mysqli_fetch_assoc($res);
        if (false === $row) {
            mysqli_free_result($res);
        }
        return $row;
    }

    function fetchRow($res)
    {
        $row = mysqli_fetch_row($res);
        if (false === $row) {
            mysqli_free_result($res);
        }
        return $row;
    }

    function getAffectedRows()
    {
        return mysqli_affected_rows($this->link);
    }

    function getName()
    {
        return 'MySQLi database';
    }
}


function printFoundVersion($fullName, $res)
{
    global $VULN_SOFTWARE, $VULNERABILITIES, $CMS;
    
    if ($res['name'] === 'htaccess') {
        if (isset($_GET['ht'])) {
            echo 'Checking htaccess: ' . escapeHtml($fullName) . "\n";
        }
        return;
    } elseif ($res['name'] === 'uploadify') {
        echo 'Warning: uploadify.php found at ' . escapeHtml($fullName) .
            ' . Please be sure that you have secured this plugin properly. You can find more info on: ' .
            "https://blog.sucuri.net/2012/06/uploadify-uploadify-and-uploadify-the-new-timthumb.html\n";
        return;
    } elseif ($res['name'] === 'phpunit') {
        echo 'Warning: PHPUnit ' . $res['version'] . ' found at ' . escapeHtml($fullName) .
            ' . Please be sure that you have secured this plugin properly. You can find more info on: ' .
            "https://labs.sucuri.net/vulnerabilities-digest-january-2020/\n";
        return;
    }

    if ($res['dir'] === '.') {
        if (isset($_GET['robot'])){
            $res['dir'] = '/ (main folder)';
        }
        else{
            $res['dir'] = './';
        }
    }
    
    $info = ' inside: ' . escapeHtml($res['dir']) . ' - Version: ' . escapeHtml($res['version']);
    
    //make all vars empty to display them for rob as empty
    $checkint = $close_tags = $warning_color = $ok_color = $vuln_color = $timthumb_check = $plugin_check = $plugin_updt_link = '';
    if (!isset($_GET['robot']) && !isTerminal()){
        $close_tags = '</b></em>';
        $warning_color = '<em style="color: #b71b1b;"><b>';
        $ok_color = '<em style="color: green;"><b>';
        $vuln_color = '<em style="color: red;"><b>';
        $array_info = explode('/', $info);
        if (isset($array_info[3])){
            switch ($array_info[3]) {
                case "advanced-cf7-db":
                case "appointment-booking-calendar":
                case "contact-form-7":
                case "easy-wp-smtp":
                case "fancybox-for-wordpress":
                case "social-warfare":
                case "woocommerce-checkout-manager":
                case "wordpress-seo":
                case "wp-gdpr-compliance":
                case "wp-live-chat-support":
                case "wp-mail-smtp":
                case "wp-slimstat":
                    $plugin_updt_link = " <label class='noselect'> - <a target='_blank' href='sucuri-cleanup.php?srun&update_plugin&plugin=" . escapeHtml($array_info[3]) . "'>Update this plugin</a></label>";
                    break;
            }
        }
        
        if ($CMS == '1'){
            $checkint = " <label class='noselect'> - <a target='_blank' href='sucuri-cleanup.php?srun&checkint&scandirs=" . escapeHtml($res['dir']) . "'>Checkint this directory</a></label>";
            $warning_color = '<em style="color: #e06b0b;"><b>';
        }        
    }
    if (isset($res['source'])) {
        $info .= ' (from ' . escapeHtml($res['source']) . ')';
    }

    if (isset($res['oldplugin'])) {
        $info = ' at ' . escapeHtml($res['dir']) . ' - Version: ' . escapeHtml($res['version']) .
            ' - Please update this plugin immediately' .
            (isset($res['url']) ? ': ' . $res['url'] : '.');
                echo $vuln_color . 'Warning: Vulnerable ' . $res['name'] . ' plugin found' . $info . $close_tags . $plugin_updt_link . ".\n";        
    } elseif ($res['updated']) {
        echo $ok_color . 'OK: ' . $res['name'] . ' found (updated)' . $info . $close_tags . "  " . $checkint . "\n";
    } else {
        echo $warning_color . 'Warning: Found outdated ' . $res['name'] . $info . "- Please update asap." . $close_tags . $checkint . "\n";
    }
    
    if ($res['name'] === 'WordPress') {
        if (isset($_GET['wpvulndb']) && isset($VULN_SOFTWARE['wordpress'][$res['version']])) {
            print( "\nAssociated vulnerabilities:\n" );
            foreach ($VULN_SOFTWARE['wordpress'][$res['version']] as $warningNo) {
                printf("%s - %s\n", str_repeat("\x20", 10), escapeHtml($VULNERABILITIES[$warningNo]));
            }
            print( "\n" );
        }
        if (isset($_GET['list-plugins'])) {
            listWordPressPlugins($res['config']);
        }
    } elseif ($res['name'] === 'TimThumb') {
        if (isset($_GET['ttupdate'])) {
            replaceTimThumb($fullName);
        }
    } elseif ($res['name'] === 'Joomla') {
        if (isset($_GET['list-plugins'])) {
            listJoomlaPlugins($res['config']);
        }
    }
}


// =========================
// TimThumb update functions

function backupFile($file)
{
    $backupCopy = $file . '_sucuribackup.' . time();
    if (!copy($file, $backupCopy)) {
        return false;
    }

    if (filesize($file) !== filesize($backupCopy)) {
        return false;
    }
    
    chmod($backupCopy, 000);

    $newfile = file($file);
    if ($newfile === false || empty($newfile)) {
        return false;
    }
    
    return true;
}

function replaceTimThumb($fileName)
{
    // Download the new version if not already downloaded
    global $NewTimThumb;
   
    if (!$NewTimThumb) {
        $NewTimThumb = file_get_contents('https://raw.githubusercontent.com/GabrielGil/TimThumb/564c00058271147af32da8ac665c00f6a1c4ac29/timthumb.php');
    }

    if (!strpos($NewTimThumb, 'define (\'VERSION')) {
        echo 'FAILED: TimThumb update at ' . escapeHtml($fileName) . " - Error on downloading new version\n";
        return;
    }

    // Backup the old file
    if (!backupFile($fileName)) {
        echo 'FAILED: TimThumb backup at ' . escapeHtml($fileName) . "\n";
        return;
    }

    // Replace the file with the downloaded version
    $fp = fopen($fileName, 'w');
    if (!$fp) {
        echo "Couldn't open ". escapeHtml($fileName) ."\n";
        return;
    }

    if (fwrite($fp, $NewTimThumb) === strlen($NewTimThumb)) {
        echo 'timthumb.php updated at ' . escapeHtml($fileName) . "\n";
    } else {
        echo 'FAILED: Unable to update TimThumb at ' . escapeHtml($fileName) . "\n";
    }

    fclose($fp);
}

// =====================
// List plugins

function printMsg($msg, $details = '')
{
    echo escapeHTML($msg . $details);
}

function getWordPressPlugins($configFileName)
{
    $configContent = file_get_contents($configFileName);
    if (false === $configContent) {
        echo 'Unable to read ' . escapeHtml($configFileName) . "\n\n";
        return array(array(), array());
    }
    
    $config = array(
        'host' => getOption('DB_HOST', $configContent, OPT_TYPE_CONST | OPT_TYPE_ENV),
        'user' => getOption('DB_USER', $configContent, OPT_TYPE_CONST),
        'pass' => getOption('DB_PASSWORD', $configContent, OPT_TYPE_CONST),
        'db' => getOption('DB_NAME', $configContent, OPT_TYPE_CONST),
        'prefix' => getOption('$table_prefix', $configContent, OPT_TYPE_VAR),
    );
    
    if (!$config['host'] || !$config['db'] || !$config['user']) {
        echo "WARN: No DB config\n\n";
        return array(array(), array());
    }
    
    $driver = function_exists('mysqli_connect') ? new MySQLIDriver() : new MySQLDriver();
    $errMsg = $driver->connect($config);
    if ($errMsg !== true) {
        echo escapeHtml($errMsg) . "\n";
        return array(array(), array());
    }
    
    if (!preg_match('/^[a-zA-Z0-9_-]+$/', $config['prefix'])) {
        echo 'Invalid prefix: ' . escapeHtml($config['prefix']) . "\n\n";
        return array(array(), array());
    }
    
    // Plugins
    $plugins = array();
    //First lets check the current transient
    $res = $driver->query('SELECT "option_value" FROM ' . $driver->escapeName($config['prefix'] . 'options') .
    ' WHERE "option_name" = \'_site_transient_update_plugins\';');  
    if ($row = $driver->fetchRow($res)) {
        $plugins = unserialize($row[0]);
            foreach ( $plugins as $key => $value) {
                if ($key == 'checked'){
                    $plugins = array_keys($value);
                    continue;
                }
            }
    }
    //As a fallback lets check the old slugs transient
    else{  
        $res = $driver->query('SELECT "option_value" FROM ' . $driver->escapeName($config['prefix'] . 'options') .
        ' WHERE "option_name" = \'_transient_plugin_slugs\';');
        if ($row = $driver->fetchRow($res)) {
            $plugins = unserialize($row[0]);
        }    
        
    }
    
    if (!is_array($plugins)) {
            echo 'Plugins error: ' . escapeHtml($configFileName) . "\n\n";
            return array(array(), array());
    }
    
    // Themes
    $res = $driver->query('SELECT "option_value" FROM ' . $driver->escapeName($config['prefix'] . 'options') .
        ' WHERE "option_name" = \'_site_transient_theme_roots\';');
    
    $themes = array();
    if ($row = $driver->fetchRow($res)) {
        $themes = unserialize($row[0]);
        if (!is_array($themes)) {
            echo 'Themes error: ' . escapeHtml($configFileName) . "\n\n";
            return array(array(), array());
        }
    }
    
    $driver->disconnect();
    
    return array($plugins, $themes);
}

function sortWordPressPlugins($dir, $plugins)
{
    $outPlugins = array(array(), array(), array(), array());
    $dir .= '/wp-content/plugins/';
    
    foreach ($plugins as $plugin) {
        if (preg_match('@^([^/]+)/.+$@ ', $plugin, $m)) {
            $name = $m[1];
        } else {
            $name = basename($plugin, '.php'); // TODO ???
        }
        
        if (!preg_match('/^[0-9a-zA-Z._-]+$/', $name)) {
            $outPlugins[3][] = 'Invalid name: ' . $name;
            continue;
        }
        
        $fileName = realpath($dir . $plugin);
        if (substr($fileName, 0, strlen(realpath($dir))) !== realpath($dir) || !is_file($fileName)) {
            $outPlugins[3][] = 'Broken plugin: ' . $plugin . '=' . $fileName;
            continue;
        }

        $version = getWpPluginVersion($fileName);
        if (false === $version) {
            $outPlugins[3][] = 'Invalid version for ' . $name;
            continue;
        }
                
        if (!$data = @ file_get_contents('https://api.wordpress.org/plugins/info/1.0/' . $name)) {
            $outPlugins[3][] = 'Could not contact WordPress to check for update: ' . $name .
                ' version ' . $version;
            continue;
        }
        
        $data = (array) unserialize($data);
            
        if (!isset($data['version'])) {
            $outPlugins[2][] = 'Possible premium plugin ' . $name . ' version ' . $version;
            continue;
        }

        if (version_compare($data['version'], $version) > 0) {
            $outPlugins[0][] = $name . ' version ' . $version .
                ' - There is a new version available: ' . $data['version'];
        } else {
            $outPlugins[1][] = $name . ' version ' . $version .
                ' - You are up to date.';
        }
    }
    
    return $outPlugins;
}

function sortWordPressThemes($dir, $themes)
{
    $outThemes = array(array(), array(), array(), array());
    $dir .= '/wp-content';
    $themesDir = realpath($dir . '/themes/');
    
    foreach ($themes as $theme => $subdir) {
        if (!preg_match('/^[0-9a-zA-Z._-]+$/', $theme)) {
            $outThemes[3][] = 'Invalid name: ' . $theme;
            continue;
        }

        $fileName = realpath($dir . $subdir . '/' . $theme . '/style.css');
        if (substr($fileName, 0, strlen($themesDir)) !== $themesDir || !is_file($fileName)) {
            $outThemes[3][] = 'Broken theme: ' . $theme .
                ($theme !== '/themes' ? ', subdir: ' . $subdir : '');
            continue;
        }

        $version = getWpPluginVersion($fileName);
        if (false === $version) {
            $outThemes[3][] = 'Invalid version for ' . $theme;
            continue;
        }

        $url = 'https://api.wordpress.org/themes/info/1.1/?action=theme_information&request[slug]=';
        if (!$data = @ file_get_contents($url . $theme)) {
            $outThemes[3][] = 'Could not contact WordPress to check for update: ' . $theme .
                ' version ' . $version;
            continue;
        }
        
        $data = jsonDcode($data, true);
        
        if (!isset($data['version'])) {
            $outThemes[2][] = 'Possible premium theme ' . $theme . ' version ' . $version;
            continue;
        }
        
        if (version_compare($data['version'], $version) > 0) {
            $outThemes[0][] = $theme . ' version ' . $version .
                ' - There is a new version available: ' . $data['version'];
        } else {
            $outThemes[1][] = $theme . ' version ' . $version .
                ' - You are up to date.';
        }
    }
    
    return $outThemes;
}

function listWordPressPlugins($configFileName)
{
    list($plugins, $themes) = getWordPressPlugins($configFileName);
    $plugins = sortWordPressPlugins(dirname($configFileName), $plugins);
    
    echo "\n [Plugins]\n";
    
    $label = array('Outdated plugins', 'Updated plugins', 'Possible premium plugins', 'Errors');
    foreach ($plugins as $order => $arr) {
        if (count($arr) > 0) {
            echo ' ' . $label[$order] . ":\n";
        }

        foreach ($arr as $msg) {
            echo ' ' . escapeHtml($msg) . "\n";
        }

        if (count($arr) > 0) {
            echo "\n";
        }
    }
    
    echo "\n [Themes]\n";
    
    $themes = sortWordPressThemes(dirname($configFileName), $themes);
    
    $label = array('Outdated themes', 'Updated themes', 'Possible premium themes', 'Errors');
    foreach ($themes as $order => $arr) {
        if (count($arr) > 0) {
            echo ' ' . $label[$order] . ":\n";
        }

        foreach ($arr as $msg) {
            echo ' ' . escapeHtml($msg) . "\n";
        }
        
        if (count($arr) > 0) {
            echo "\n";
        }
    }
}



function listJoomlaPlugins($configFileName)
{
    $configContent = file_get_contents($configFileName);
    
    $config = array(
        'host' => getOption('$host', $configContent, OPT_TYPE_VAR),
        'user' => getOption('$user', $configContent, OPT_TYPE_VAR),
        'pass' => getOption('$password', $configContent, OPT_TYPE_VAR),
        'db' => getOption('$db', $configContent, OPT_TYPE_VAR),
        'prefix' => getOption('$dbprefix', $configContent, OPT_TYPE_VAR),
    );
    if (!$config['host'] || !$config['db'] || !$config['user']) {
        echo "WARN: No DB config\n\n";
        return;
    }
    
    $driver = function_exists('mysqli_connect') ? new MySQLIDriver() : new MySQLDriver();
    $errMsg = $driver->connect($config);
    if ($errMsg !== true) {
        echo escapeHtml($errMsg) . "\n";
        return;
    }
    
    if (!preg_match('/^[a-zA-Z0-9_-]*$/', $config['prefix'])) {
        echo 'Invalid prefix: ' . escapeHtml($config['prefix']) . "\n";
        return;
    }
    
    $res = $driver->query('SELECT "name", "type", "manifest_cache" FROM ' .
        $driver->escapeName($config['prefix'] . 'extensions') .
        ' ORDER BY "type", "name";');
    
    $group = null;
    while ($row = $driver->fetchRow($res)) {
        if ($group === null || $row[1] !== $group) {
            $group = $row[1];
            echo "\n";
        }
        
        $manifest_cache = jsonDcode($row[2], true);

        if (!isset($manifest_cache['version'])) {
            $manifest_cache['version'] = 'N/A';
        }

        printf(
            " Found Joomla %s - %s - version: %s\n",
            escapeHtml($row[1]),
            escapeHtml($row[0]),
            escapeHtml($manifest_cache['version'])
        );
    }
    
    $driver->disconnect();
}


// =====================
// checkHosting

function checkHosting()
{
    if (isset($_SERVER['SERVER_ADDR'])) {
         printf("Server Addr: %s\n\n", $_SERVER['SERVER_ADDR']);
    }

    // Detect Hosting provider
    echo "Hosting Provider: ";

    $provider = @ gethostbyaddr($_SERVER['SERVER_ADDR']);
    if (false !== $provider) {
        $providers = array(
            'secureserver.net' => 'GoDaddy',
            'bluehost.com'     => 'BlueHost',
            'hostgator.com'    => 'HostGator',
            'site5.com'        => 'Site5',
            'amazonaws.com'    => 'Amazon',
            'siteground.com'   => 'Siteground',
            'gridserver.com'   => 'MediaTemple',
            'linode.com'       => 'WPEngine',
            '1e100.net'        => 'Google',
            'dreamhost.com'    => 'DreamHost',
        );
    
        $match = "Unknown provider - $provider.";
        foreach ($providers as $host => $name) {
            if (false !== strpos($provider, $host)) {
                $match = $name;
                break;
            }
        }

        echo "$match\n\n";
    } else {
        echo "Unable to determine.\n\n";
    }

    // Check CloudProxy
    echo "CloudProxy Active: ";

    $addr = @ gethostbyname($_SERVER['HTTP_HOST']);
    $host = @ gethostbyaddr($addr);
    echo preg_match('@^cloudproxy[0-9]+\.sucuri\.net$@', $host) ? "Yes\n\n" : "No\n\n";
}

function checkMayBeHacked()
{
    $User_Agent = "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.117 Safari/537.36";
    $Curl = curl_init();
    curl_setopt($Curl, CURLOPT_URL, "https://www.google.com/search?hl=en&tbo=d&site=&source=hp&q=site:".$_SERVER['HTTP_HOST']);
    curl_setopt($Curl, CURLOPT_USERAGENT, $User_Agent);
    curl_setopt($Curl, CURLOPT_RETURNTRANSFER, TRUE);
    if (!ini_get('safe_mode') && !ini_get('open_basedir')) curl_setopt($Curl, CURLOPT_FOLLOWLOCATION, TRUE);
    $Raw_Google_Output = curl_exec($Curl);
    curl_close($Curl);
    
    $Pattern = "/This site may harm your computer|This site (might|may) be hacked/";
    if (preg_match($Pattern, $Raw_Google_Output, $matches) === 1)
    {
       echo 'Google is flagging this site as \'may be hacked.\'',"\n\n";
       return 1;
    }

    $Pattern2 = "/302 Moved/";
    if (preg_match($Pattern2, $Raw_Google_Output, $matches) === 1)
    {
       echo 'Unable to check alerts on Google SERP.',"\n\n";
       return 2;
    }
    return 0;
}


echo "<pre>\n";
echo "Sucuri version report v1.1.1: " . $myversion . "\n\n";
echo "PHP Version: " . phpversion() . "\n\n";

checkHosting();

if (!isTerminal())  checkMayBeHacked();

$path = '.';
if (isset($_GET['up'])) {
    $path = '..';
} elseif (isset($_GET['upup'])) {
    $path = '../..';
} elseif (isset($_GET['upupup'])) {
    $path = '../../..';
}

runVersionCheck($path, 'printFoundVersion');

echo "\nCompleted.\n";
echo "</pre>\n";
exit(0);