diff --git a/CHANGELOG b/CHANGELOG index 3e6bd6e..35fd99b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,8 @@ +Version 2.31 Mar 04, 2020 +------------------------------ +- Setting "Check registration" added. +- Fix for ut8_decode function. + Version 2.30 Dec 24, 2019 ------------------------------ - Bug fixes and other minor improvements diff --git a/README.md b/README.md index 16d5808..ad7325c 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Build Status](https://travis-ci.org/CleanTalk/smf-antispam.svg)](https://travis-ci.org/CleanTalk/smf-antispam) -* **Version:** 2.30 +* **Version:** 2.31 * **License:** GNU General Public License * **Compatible with:** SMF 2.0 and up * **Languages:** English, Russian diff --git a/RoboFile.php b/RoboFile.php index f31e193..8ca2ef7 100644 --- a/RoboFile.php +++ b/RoboFile.php @@ -8,7 +8,7 @@ class RoboFile extends \Robo\Tasks { const PACKAGE = 'antispam_cleantalk_smf'; - const VERSION = '2.30'; + const VERSION = '2.31'; const SMF_VERSION = '2.0.14'; // for forumPrepare @@ -99,7 +99,7 @@ public function versionCheck() $errors[] = 'Not found version in CHANGELOG'; } $ctVersion = str_replace('.', '', $version); - if (!preg_match("#'smf-" . preg_quote($ctVersion) . "'\);#m", file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'cleantalkMod.php'))) { + if (!preg_match("#'smf-" . preg_quote($ctVersion) . "'\);#m", file_get_contents( __DIR__ . DIRECTORY_SEPARATOR . '/cleantalk/cleantalkMod.php' ))) { $errors[] = 'Not found CT_AGENT_VERSION in cleantalkMod.php'; } if (count($errors)) { diff --git a/attention.png b/cleantalk/attention.png similarity index 100% rename from attention.png rename to cleantalk/attention.png diff --git a/cleantalkMod.php b/cleantalk/cleantalkMod.php similarity index 98% rename from cleantalkMod.php rename to cleantalk/cleantalkMod.php index 523c42e..465900a 100644 --- a/cleantalkMod.php +++ b/cleantalk/cleantalkMod.php @@ -9,22 +9,20 @@ * @license GNU/GPL: http://www.gnu.org/copyleft/gpl.html */ +use CleantalkAP\Variables\Post; + if (!defined('SMF')) { die('Hacking attempt...'); } // Fixes for old PHP versions -require_once(dirname(__FILE__) . '/phpFix.php'); +require_once(dirname(__FILE__) . '/lib/phpFix.php'); -// Base classes -require_once(dirname(__FILE__) . '/Cleantalk.php'); -require_once(dirname(__FILE__) . '/CleantalkRequest.php'); -require_once(dirname(__FILE__) . '/CleantalkResponse.php'); -require_once(dirname(__FILE__) . '/CleantalkHelper.php'); -require_once(dirname(__FILE__) . '/CleantalkSFW.php'); +// Classes autoloader +require_once(dirname(__FILE__) . '/lib/autoloader.php'); // Common CleanTalk options -define('CT_AGENT_VERSION', 'smf-230'); +define('CT_AGENT_VERSION', 'smf-231'); define('CT_SERVER_URL', 'http://moderate.cleantalk.org'); define('CT_DEBUG', false); define('CT_REMOTE_CALL_SLEEP', 10); @@ -422,8 +420,12 @@ function cleantalk_check_register(&$regOptions, $theme_vars){ if (SMF == 'SSI') return; - if ($regOptions['interface'] == 'admin') + if ( + $regOptions['interface'] == 'admin' || // Skip admin + ! $modSettings['cleantalk_check_registrations'] // Skip if registrations check are disabled + ) return; + if ($executed_check_register) { $executed_check_register = false; @@ -982,7 +984,7 @@ function cleantalk_load() if(!empty($user_info['is_admin'])){ // Deleting selected users - if(isset($_POST['ct_del_user'])) + if(Post::get('ct_del_user')) { checkSession('request'); @@ -991,7 +993,7 @@ function cleantalk_load() if (isset($db_connection) && $db_connection != false) { - foreach($_POST['ct_del_user'] as $key=>$value) + foreach(Post::get('ct_del_user') as $key=>$value) { $result = $smcFunc['db_query']('', 'delete from {db_prefix}members where id_member='.intval($key),Array('db_error_skip' => true)); $result = $smcFunc['db_query']('', 'delete from {db_prefix}topics where id_member_started='.intval($key),Array('db_error_skip' => true)); @@ -1001,7 +1003,7 @@ function cleantalk_load() } // Deleting all users - if(isset($_POST['ct_delete_all'])) + if(Post::get('ct_delete_all')) { checkSession('request'); @@ -1222,12 +1224,12 @@ function cleantalk_buffer($buffer) { global $modSettings, $user_info, $smcFunc, $txt, $forum_version, $db_connection; - + if (SMF == 'SSI') return $buffer; - if($user_info['is_admin'] && isset($_GET['action'], $_GET['area']) && $_GET['action'] == 'admin' && $_GET['area'] == 'modsettings'){ - + if(isset($_GET['action'], $_GET['area']) && $_GET['action'] == 'admin' && $_GET['area'] == 'modsettings'){ + if(strpos($forum_version, 'SMF 2.0')===false){ $html=''; diff --git a/cleantalkModAdmin.php b/cleantalk/cleantalkModAdmin.php similarity index 93% rename from cleantalkModAdmin.php rename to cleantalk/cleantalkModAdmin.php index 4ca4ec7..a975e1b 100644 --- a/cleantalkModAdmin.php +++ b/cleantalk/cleantalkModAdmin.php @@ -9,6 +9,8 @@ * @license GNU/GPL: http://www.gnu.org/copyleft/gpl.html */ +use CleantalkAP\Variables\Post; + if (!defined('SMF')) { die('Hacking attempt...'); } @@ -67,6 +69,7 @@ function cleantalk_general_mod_settings($return_config = false) $config_vars = array( array('title', 'cleantalk_settings'), array('text', 'cleantalk_api_key'), + array('check', 'cleantalk_check_registrations', 'subtext' => $txt['cleantalk_check_registrations']), array('check', 'cleantalk_first_post_checking', 'subtext' => $txt['cleantalk_first_post_checking_postinput']), array('check', 'cleantalk_check_personal_messages', 'subtext' => $txt['cleantalk_check_personal_messages_postinput']), array('check', 'cleantalk_automod', 'subtext' => $txt['cleantalk_automod_postinput']), @@ -99,7 +102,7 @@ function cleantalk_general_mod_settings($return_config = false) } } - $save_key = $key_is_valid ? $save_key : $_POST['cleantalk_api_key']; + $save_key = $key_is_valid ? $save_key : Post::get( 'cleantalk_api_key' ); if(!$key_is_valid) { $result = CleantalkHelper::apbct_key_is_correct($save_key); @@ -109,9 +112,10 @@ function cleantalk_general_mod_settings($return_config = false) { $result = CleantalkHelper::api_method__notice_paid_till($save_key, preg_replace('/http[s]?:\/\//', '', $_SERVER['HTTP_HOST'], 1)); - if (empty($result['error'])) - { - if( $result['valid'] ) { + if (empty($result['error'])){ + + if($result['valid']){ + $key_is_ok = true; $settings_array = array( 'cleantalk_api_key' => ($save_key) ? $save_key : '', @@ -128,8 +132,8 @@ function cleantalk_general_mod_settings($return_config = false) 'cleantalk_account_name_ob' => isset($result['account_name_ob']) ? $result['account_name_ob'] : '', 'cleantalk_last_account_check' => time(), ); - if (isset($_POST['cleantalk_sfw']) && $_POST['cleantalk_sfw'] == 1) - { + + if (Post::get( 'cleantalk_sfw' ) == 1){ $settings_array['cleantalk_sfw'] = '1'; $settings_array['cleantalk_sfw_last_update'] = time(); $settings_array['cleantalk_sfw_last_logs_sent'] = time(); @@ -137,16 +141,12 @@ function cleantalk_general_mod_settings($return_config = false) $sfw->sfw_update($save_key); $sfw->send_logs($save_key); } - } - else - { + }else{ // @ToDo have to handle errors! // return array('error' => 'KEY_IS_NOT_VALID'); } - } - else - { + }else{ // @ToDo have to handle errors! // return array('error' => $result); } @@ -154,8 +154,7 @@ function cleantalk_general_mod_settings($return_config = false) $settings_array['cleantalk_api_key_is_ok'] = ($key_is_ok) ? '1' : '0'; updateSettings($settings_array, false); } - - + if (isset($_GET['save'])) { checkSession(); saveDBSettings($config_vars); diff --git a/lib/error.html b/cleantalk/error.html similarity index 100% rename from lib/error.html rename to cleantalk/error.html diff --git a/lib/Cleantalk.php b/cleantalk/lib/Cleantalk.php similarity index 100% rename from lib/Cleantalk.php rename to cleantalk/lib/Cleantalk.php diff --git a/cleantalk/lib/CleantalkAP/Common/API.php b/cleantalk/lib/CleantalkAP/Common/API.php new file mode 100644 index 0000000..6fb98a7 --- /dev/null +++ b/cleantalk/lib/CleantalkAP/Common/API.php @@ -0,0 +1,717 @@ + STRING) + */ + static public function method__get_2s_blacklists_db($api_key, $out = null, $do_check = true) + { + $request = array( + 'method_name' => '2s_blacklists_db', + 'auth_key' => $api_key, + 'out' => $out, + ); + + $result = static::send_request($request); + $result = $do_check ? static::check_response($result, '2s_blacklists_db') : $result; + + return $result; + } + + /** + * Wrapper for get_api_key API method. + * Gets access key automatically. + * + * @param string $product_name Type of product + * @param string $email Website admin email + * @param string $website Website host + * @param string $platform Website platform + * @param string|null $timezone + * @param string|null $language + * @param string|null $user_ip + * @param bool $wpms + * @param bool $white_label + * @param string $hoster_api_key + * @param bool $do_check + * + * @return array|bool|mixed + */ + static public function method__get_api_key($product_name, $email, $website, $platform, $timezone = null, $language = null, $user_ip = null, $wpms = false, $white_label = false, $hoster_api_key = '', $do_check = true) + { + $request = array( + 'method_name' => 'get_api_key', + 'product_name' => $product_name, + 'email' => $email, + 'website' => $website, + 'platform' => $platform, + 'timezone' => $timezone, + 'http_accept_language' => $language, + 'user_ip' => $user_ip, + 'wpms_setup' => $wpms, + 'hoster_whitelabel' => $white_label, + 'hoster_api_key' => $hoster_api_key, + ); + + $result = static::send_request($request); + $result = $do_check ? static::check_response($result, 'get_api_key') : $result; + + return $result; + } + + /** + * Wrapper for get_antispam_report API method. + * Gets spam report. + * + * @param string $host website host + * @param integer $period report days + * @param boolean $do_check + * + * @return array|bool|mixed + */ + static public function method__get_antispam_report($host, $period = 1, $do_check = true) + { + $request = Array( + 'method_name' => 'get_antispam_report', + 'hostname' => $host, + 'period' => $period + ); + + $result = static::send_request($request); + $result = $do_check ? static::check_response($result, 'get_antispam_report') : $result; + + return $result; + } + + /** + * Wrapper for get_antispam_report_breif API method. + * Ggets spam statistics. + * + * @param string $api_key + * @param bool $do_check + * + * @return array|bool|mixed + */ + static public function method__get_antispam_report_breif($api_key, $do_check = true) + { + $request = array( + 'method_name' => 'get_antispam_report_breif', + 'auth_key' => $api_key, + ); + + $result = static::send_request($request); + $result = $do_check ? static::check_response($result, 'get_antispam_report_breif') : $result; + + return $result; + } + + /** + * Wrapper for notice_paid_till API method. + * Gets information about renew notice. + * + * @param string $api_key API key + * @param string $path_to_cms Website URL + * @param string $product_name + * @param bool $do_check + * + * @return array|bool|mixed + */ + static public function method__notice_paid_till($api_key, $path_to_cms, $product_name = 'antispam', $do_check = true) + { + $request = array( + 'method_name' => 'notice_paid_till', + 'path_to_cms' => $path_to_cms, + 'auth_key' => $api_key, + ); + + $product_id = null; + $product_id = $product_name == 'antispam' ? 1 : $product_id; + $product_id = $product_name == 'security' ? 4 : $product_id; + if($product_id) + $request['product_id'] = $product_id; + + $result = static::send_request($request); + $result = $do_check ? static::check_response($result, 'notice_paid_till') : $result; + + return $result; + } + + /** + * Wrapper for ip_info API method. + * Gets IP country. + * + * @param string $data + * @param bool $do_check + * + * @return array|bool|mixed + */ + static public function method__ip_info($data, $do_check = true) + { + $request = array( + 'method_name' => 'ip_info', + 'data' => $data + ); + + $result = static::send_request($request); + $result = $do_check ? static::check_response($result, 'ip_info') : $result; + return $result; + } + + /** + * Wrapper for spam_check_cms API method. + * Checks IP|email via CleanTalk's database. + * + * @param string $api_key + * @param array $data + * @param null|string $date + * @param bool $do_check + * + * @return array|bool|mixed + */ + static public function method__spam_check_cms($api_key, $data, $date = null, $do_check = true) + { + $request = Array( + 'method_name' => 'spam_check_cms', + 'auth_key' => $api_key, + 'data' => is_array($data) ? implode(',', $data) : $data, + ); + + if($date) $request['date'] = $date; + + $result = static::send_request($request); + $result = $do_check ? static::check_response($result, 'spam_check_cms') : $result; + + return $result; + } + + /** + * Wrapper for spam_check API method. + * Checks IP|email via CleanTalk's database. + * + * @param string $api_key + * @param array $data + * @param null|string $date + * @param bool $do_check + * + * @return array|bool|mixed + */ + static public function method__spam_check($api_key, $data, $date = null, $do_check = true) + { + $request = Array( + 'method_name' => 'spam_check', + 'auth_key' => $api_key, + 'data' => is_array($data) ? implode(',', $data) : $data, + ); + + if($date) $request['date'] = $date; + + $result = static::send_request($request); + $result = $do_check ? static::check_response($result, 'spam_check') : $result; + + return $result; + } + + /** + * Wrapper for sfw_logs API method. + * Sends SpamFireWall logs to the cloud. + * + * @param string $api_key + * @param array $data + * @param bool $do_check + * + * @return array|bool|mixed + */ + static public function method__sfw_logs($api_key, $data, $do_check = true) + { + + $request = array( + 'auth_key' => $api_key, + 'method_name' => 'sfw_logs', + 'data' => json_encode($data), + 'rows' => count($data), + 'timestamp' => time() + ); + + $result = static::send_request($request); + $result = $do_check ? static::check_response($result, 'sfw_logs') : $result; + + return $result; + } + + /** + * Wrapper for security_logs API method. + * Sends security logs to the cloud. + * + * @param string $api_key + * @param array $data + * @param bool $do_check + * + * @return array|bool|mixed + */ + static public function method__security_logs($api_key, $data, $do_check = true) + { + $request = array( + 'auth_key' => $api_key, + 'method_name' => 'security_logs', + 'timestamp' => current_time('timestamp'), + 'data' => json_encode($data), + 'rows' => count($data), + ); + + $result = static::send_request($request); + $result = $do_check ? static::check_response($result, 'security_logs') : $result; + + return $result; + } + + /** + * Wrapper for security_logs API method. + * Sends Securitty Firewall logs to the cloud. + * + * @param string $api_key + * @param array $data + * @param bool $do_check + * + * @return array|bool|mixed + */ + static public function method__security_logs__sendFWData($api_key, $data, $do_check = true) + { + + $request = array( + 'auth_key' => $api_key, + 'method_name' => 'security_logs', + 'timestamp' => current_time('timestamp'), + 'data_fw' => json_encode($data), + 'rows_fw' => count($data), + ); + + $result = static::send_request($request); + $result = $do_check ? static::check_response($result, 'security_logs') : $result; + + return $result; + } + + /** + * Wrapper for security_logs API method. + * Sends empty data to the cloud to syncronize version. + * + * @param string $api_key + * @param bool $do_check + * + * @return array|bool|mixed + */ + static public function method__security_logs__feedback($api_key, $do_check = true) + { + $request = array( + 'auth_key' => $api_key, + 'method_name' => 'security_logs', + 'data' => '0', + ); + + $result = static::send_request($request); + $result = $do_check ? static::check_response($result, 'security_logs') : $result; + + return $result; + } + + /** + * Wrapper for security_firewall_data API method. + * Gets Securitty Firewall data to write to the local database. + * + * @param string $api_key + * @param bool $do_check + * + * @return array|bool|mixed + */ + static public function method__security_firewall_data($api_key, $do_check = true) + { + + $request = array( + 'auth_key' => $api_key, + 'method_name' => 'security_firewall_data', + ); + + $result = static::send_request($request); + $result = $do_check ? static::check_response($result, 'security_firewall_data') : $result; + + return $result; + } + + /** + * Wrapper for security_firewall_data_file API method. + * Gets URI with security firewall data in .csv.gz file to write to the local database. + * + * @param string $api_key + * @param bool $do_check + * + * @return array|bool|mixed + */ + static public function method__security_firewall_data_file($api_key, $do_check = true) + { + + $request = array( + 'auth_key' => $api_key, + 'method_name' => 'security_firewall_data_file', + ); + + $result = static::send_request($request); + $result = $do_check ? static::check_response($result, 'security_firewall_data_file') : $result; + + return $result; + } + + /** + * Wrapper for security_linksscan_logs API method. + * Send data to the cloud about scanned links. + * + * @param string $api_key + * @param string $scan_time Datetime of scan + * @param bool $scan_result + * @param int $links_total + * @param array $links_list + * @param bool $do_check + * + * @return array|bool|mixed + */ + static public function method__security_linksscan_logs($api_key, $scan_time, $scan_result, $links_total, $links_list, $do_check = true) + { + $request = array( + 'auth_key' => $api_key, + 'method_name' => 'security_linksscan_logs', + 'started' => $scan_time, + 'result' => $scan_result, + 'total_links_found' => $links_total, + 'links_list' => $links_list, + ); + + $result = static::send_request($request); + $result = $do_check ? static::check_response($result, 'security_linksscan_logs') : $result; + + return $result; + } + + /** + * Wrapper for security_mscan_logs API method. + * Sends result of file scan to the cloud. + * + * @param string $api_key + * @param int $service_id + * @param string $scan_time Datetime of scan + * @param bool $scan_result + * @param int $scanned_total + * @param array $modified List of modified files with details + * @param array $unknown List of modified files with details + * @param bool $do_check + * + * @return array|bool|mixed + */ + static public function method__security_mscan_logs($api_key, $service_id, $scan_time, $scan_result, $scanned_total, $modified, $unknown, $do_check = true) + { + $request = array( + 'method_name' => 'security_mscan_logs', + 'auth_key' => $api_key, + 'service_id' => $service_id, + 'started' => $scan_time, + 'result' => $scan_result, + 'total_core_files' => $scanned_total, + ); + + if(!empty($modified)){ + $request['failed_files'] = json_encode($modified); + $request['failed_files_rows'] = count($modified); + } + if(!empty($unknown)){ + $request['unknown_files'] = json_encode($unknown); + $request['unknown_files_rows'] = count($unknown); + } + + $result = static::send_request($request); + $result = $do_check ? static::check_response($result, 'security_mscan_logs') : $result; + + return $result; + } + + /** + * Wrapper for security_mscan_files API method. + * Sends file to the cloud for analysis. + * + * @param string $api_key + * @param string $file_path Path to the file + * @param array $file File itself + * @param string $file_md5 MD5 hash of file + * @param array $weak_spots List of weak spots found in file + * @param bool $do_check + * + * @return array|bool|mixed + */ + static public function method__security_mscan_files($api_key, $file_path, $file, $file_md5, $weak_spots, $do_check = true) + { + $request = array( + 'method_name' => 'security_mscan_files', + 'auth_key' => $api_key, + 'path_to_sfile' => $file_path, + 'attached_sfile' => $file, + 'md5sum_sfile' => $file_md5, + 'dangerous_code' => $weak_spots, + ); + + $result = static::send_request($request); + $result = $do_check ? static::check_response($result, 'security_mscan_files') : $result; + + return $result; + } + + /** + * Wrapper for get_antispam_report API method. + * Function gets spam domains report. + * + * @param string $api_key + * @param array|string|mixed $data + * @param string $date + * @param bool $do_check + * + * @return array|bool|mixed + */ + static public function method__backlinks_check_cms($api_key, $data, $date = null, $do_check = true) + { + $request = array( + 'method_name' => 'backlinks_check_cms', + 'auth_key' => $api_key, + 'data' => is_array($data) ? implode(',', $data) : $data, + ); + + if($date) $request['date'] = $date; + + $result = static::send_request($request); + $result = $do_check ? static::check_response($result, 'backlinks_check_cms') : $result; + + return $result; + } + + /** + * Wrapper for get_antispam_report API method. + * Function gets spam domains report + * + * @param string $api_key + * @param array $logs + * @param bool $do_check + * + * @return array|bool|mixed + */ + static public function method__security_backend_logs($api_key, $logs, $do_check = true) + { + $request = array( + 'method_name' => 'security_backend_logs', + 'auth_key' => $api_key, + 'logs' => json_encode($logs), + 'total_logs' => count($logs), + ); + + $result = static::send_request($request); + $result = $do_check ? static::check_response($result, 'security_backend_logs') : $result; + + return $result; + } + + /** + * Wrapper for get_antispam_report API method. + * Sends data about auto repairs + * + * @param string $api_key + * @param bool $repair_result + * @param string $repair_comment + * @param $repaired_processed_files + * @param $repaired_total_files_proccessed + * @param $backup_id + * @param bool $do_check + * + * @return array|bool|mixed + */ + static public function method__security_mscan_repairs($api_key, $repair_result, $repair_comment, $repaired_processed_files, $repaired_total_files_proccessed, $backup_id, $do_check = true) + { + $request = array( + 'method_name' => 'security_mscan_repairs', + 'auth_key' => $api_key, + 'repair_result' => $repair_result, + 'repair_comment' => $repair_comment, + 'repair_processed_files' => json_encode($repaired_processed_files), + 'repair_total_files_processed' => $repaired_total_files_proccessed, + 'backup_id' => $backup_id, + 'mscan_log_id' => 1, + ); + + $result = static::send_request($request); + $result = $do_check ? static::check_response($result, 'security_mscan_repairs') : $result; + + return $result; + } + + /** + * Wrapper for get_antispam_report API method. + * Force server to update checksums for specific plugin\theme + * + * @param string $api_key + * @param string $plugins_and_themes_to_refresh + * @param bool $do_check + * + * @return array|bool|mixed + */ + static public function method__request_checksums($api_key, $plugins_and_themes_to_refresh, $do_check = true) + { + $request = array( + 'method_name' => 'request_checksums', + 'auth_key' => $api_key, + 'data' => $plugins_and_themes_to_refresh + ); + + $result = static::send_request($request); + $result = $do_check ? static::check_response($result, 'request_checksums') : $result; + + return $result; + } + + static public function get_agent(){ + return defined( 'CLEANTALK_AGENT' ) ? CLEANTALK_AGENT : static::DEFAULT_AGENT; + } + + /** + * Function sends raw request to API server + * + * @param array $data to send + * @param string $url of API server + * @param integer $timeout timeout in seconds + * @param boolean $ssl use ssl on not + * + * @return array|bool + */ + static public function send_request($data, $url = self::URL, $ssl = false) + { + // Default preset is 'api' + $presets = array( 'api' ); + + $data['agent'] = static::get_agent(); + + // Add ssl to 'presets' if enabled + if( $ssl ) + array_push( $presets, 'ssl' ); + + $result = \CleantalkAP\Common\Helper::http__request( $url, $data, $presets ); + + // Retry with SSL enabled if failed + if( ! empty ( $result['error'] ) && $ssl === false ) + $result = \CleantalkAP\Common\Helper::http__request( $url, $data, 'api ssl' ); + + return $result; + } + + /** + * Function checks server response + * + * @param string $result + * @param string $method_name + * + * @return mixed (array || array('error' => true)) + */ + static public function check_response($result, $method_name = null) + { + // Errors handling + // Bad connection + if(is_array($result) && isset($result['error'])){ + return array( + 'error' => 'CONNECTION_ERROR: ' . (isset($result['error']) ? ' ' . $result['error'] : ''), + ); + } + + // JSON decode errors + $result = json_decode($result, true); + if(empty($result)){ + return array( + 'error' => 'JSON_DECODE_ERROR', + ); + } + + // Server errors + if($result && + (isset($result['error_no']) || isset($result['error_message'])) && + (isset($result['error_no']) && $result['error_no'] != 12) + ){ + return array( + 'error' => "SERVER_ERROR NO: {$result['error_no']} MSG: {$result['error_message']}", + 'error_no' => $result['error_no'], + 'error_message' => $result['error_message'], + ); + } + + // Pathces for different methods + switch($method_name){ + + // notice_paid_till + case 'notice_paid_till': + + $result = isset($result['data']) ? $result['data'] : $result; + + if((isset($result['error_no']) && $result['error_no'] == 12) || + ( + !(isset($result['service_id']) && is_int($result['service_id'])) && + empty($result['moderate_ip']) + ) + ) + $result['valid'] = 0; + else + $result['valid'] = 1; + + return $result; + + break; + + // get_antispam_report_breif + case 'get_antispam_report_breif': + + $out = isset($result['data']) && is_array($result['data']) + ? $result['data'] + : array('error' => 'NO_DATA'); + + for($tmp = array(), $i = 0; $i < 7; $i++){ + $tmp[date('Y-m-d', time() - 86400 * 7 + 86400 * $i)] = 0; + } + $out['spam_stat'] = (array)array_merge($tmp, isset($out['spam_stat']) ? $out['spam_stat'] : array()); + $out['top5_spam_ip'] = isset($out['top5_spam_ip']) ? $out['top5_spam_ip'] : array(); + + return $out; + + break; + + default: + return isset($result['data']) && is_array($result['data']) + ? $result['data'] + : array('error' => 'NO_DATA'); + break; + } + } +} \ No newline at end of file diff --git a/cleantalk/lib/CleantalkAP/Common/Arr.php b/cleantalk/lib/CleantalkAP/Common/Arr.php new file mode 100644 index 0000000..0533485 --- /dev/null +++ b/cleantalk/lib/CleantalkAP/Common/Arr.php @@ -0,0 +1,252 @@ +array = is_array( $array ) + ? $array + : array(); + + return $this; + } + + /** + * Recursive + * Check if Array has keys given keys + * Save found keys in $this->found + * + * @param array|string $keys + * @param bool $regexp + * @param array $array + * + * @return Arr + */ + public function get_keys( $keys = array(), $regexp = false, $array = array() ) + { + $array = $array ? $array : $this->array; + $keys = is_array( $keys ) ? $keys : explode( ',', $keys ); + + if( empty( $array ) || empty( $keys ) ) + return $this; + + $this->found = $keys === array('all') + ? $this->array + : $this->search( + 'key', + $array, + $keys, + $regexp + ); + + return $this; + } + + /** + * Recursive + * Check if Array has valuse given valuse + * Save found keys in $this->found + * + * @param array|string $values + * @param bool $regexp + * @param array $array + * + * @return $this + */ + public function get_values( $values = array(), $regexp = false, $array = array() ) + { + $array = $array ? $array : $this->array; + $keys = is_array( $values ) ? $values : explode( ',', $values ); + + if( empty( $array ) || empty( $values ) ) + return $this; + + $this->found = $values === array('all') + ? $this->array + : $this->search( + 'value', + $array, + $keys, + $regexp + ); + + return $this; + } + + public function get_array( $searched = array(), $regexp = false, $array = array() ){ + + $array = $array ? $array : $this->array; + + + if( empty( $array ) || empty( $searched ) ) + return $this; + + $this->found = $searched === array('all') + ? $this->array + : $this->search( + 'array', + $array, + $searched, + $regexp + ); + + $this->found = $this->found === $searched ? $this->found : array(); + + return $this; + } + + /** + * Recursive + * Check if array contains wanted data type + * + * @param string $type + * @param array $array + * @param array $found + * + * @return bool|void + */ + public function is( $type, $array = array(), $found = array() ) + { + $array = $array ? $array : $this->array; + $found = $found ? $found : $this->found; + + foreach ( $array as $key => $value ){ + + if( array_key_exists( $key, $found ) ){ + if( is_array( $found[ $key ] ) ){ + if( ! $this->is( $type, $value, $found[ $key ] ) ){ + return false; + } + }else{ + switch ( $type ){ + case 'regexp': + $value = preg_match( '/\/.*\//', $value ) === 1 ? $value : '/' . $value . '/'; + if( @preg_match( $value, null ) === false ){ + return false; + } + break; + } + } + } + + } + + return true; + } + + /** + * @param string $type + * @param array $array + * @param array $searched + * @param bool $regexp + * @param array $found + * + * @return array + */ + private function search( $type, $array = array(), $searched = array(), $regexp = false, $found = array() ) + { + foreach ( $array as $key => $value ){ + + // Recursion + if( is_array( $value ) ){ + $result = $this->search( $type, $value, $searched, $regexp, array() ); + if($result) + $found[$key] = $result; + + // Execution + }else{ + foreach ( $searched as $searched_key => $searched_val ){ + switch ($type){ + case 'key': + if( $key === $searched_val || ($regexp && preg_match( '/' . $searched_val . '/', $key) === 1) ) + $found[$key] = true; + break; + case 'value': + if( stripos($value, $searched_val) !== false || ($regexp && preg_match( '/' . $searched_val . '/', $value) === 1) ) + $found[$key] = true; + break; + case 'array': + if( stripos($key, $searched_key) !== false || ($regexp && preg_match( '/' . $searched_key . '/', $key) === 1) ) + if( is_array( $value ) && is_array( $value )){ + $result = $this->search( 'array', $value, $searched_key, $regexp, array() ); + if( $result ){ + $found[ $key ] = $result; + } + }else{ + $found[$key] = $value; + } + break; + } + } + } + } + + return $found; + } + + public function compare( $arr1, $arr2 ){ + // $arr1 = is_array( $arr1 ) ? $arr1 : array(); + // $arr2 = is_array( $arr2 ) ? $arr2 : array(); + foreach ( $arr1 as $key1 => $val1 ){ + if( $arr1 === $arr2 ){ + if(is_array($arr1) && is_array($arr2)){ + $result = $this->compare( $arr1, $arr2 ); + } + } + } + } + + /** + * Recursive + * Delete elements from array with found keys ( $this->found ) + * If $searched param is differ from 'arr_special_param' + * + * @param mixed $searched + * @param array $array + * @param array $found + * + * @return array + */ + public function delete( $searched = 'arr_special_param', $array = array(), $found =array() ) + { + $array = $array ? $array : $this->array; + $found = $found ? $found : $this->found; + + foreach($array as $key => $value){ + + if(array_key_exists($key, $found)){ + if( is_array( $found[ $key ] ) ){ + $array[ $key ] = $this->delete( $searched, $value, $found[ $key ] ); + if( empty( $array[ $key ] ) ) + unset( $array[ $key ] ); + }else{ + if( $searched === 'arr_special_param' || $searched === $value ){ + unset( $array[ $key ] ); + } + } + } + + } + + $this->result = $array; + return $array; + } + + public function result(){ + return (boolean) $this->found; + } +} \ No newline at end of file diff --git a/cleantalk/lib/CleantalkAP/Common/Cron.php b/cleantalk/lib/CleantalkAP/Common/Cron.php new file mode 100644 index 0000000..df2906f --- /dev/null +++ b/cleantalk/lib/CleantalkAP/Common/Cron.php @@ -0,0 +1,269 @@ +errors = static::getErrors(); + $this->tasks = empty($tasks) ? array() : $tasks; + } + + static public function getErrors(){ + return Err::getInstance(); + } + + /** + * Return arrray with tasks + * Should be rewritten for specific platform + * + * @return array + */ + static public function getTasks(){ + return array(); + } + + /** + * Save tasks in some way + * Should be rewritten for specific platform + * + * @param $tasks + * + * @return bool + */ + static public function saveTasks( $tasks ){ + return true; + } + + /** + * Creates new entry in task list + * + * @param string $task + * @param callable $handler + * @param int $period + * @param int|null $first_call + * @param array $params + * + * @return bool + */ + static public function addTask($task, $handler, $period, $first_call = null, $params = array()) + { + // First call time() + preiod + $first_call = !$first_call ? time() + $period : $first_call; + + $tasks = static::getTasks(); + + if(isset($tasks[$task])) + return false; + + // Task entry + $tasks[$task] = array( + 'handler' => $handler, + 'next_call' => $first_call, + 'period' => $period, + 'params' => $params, + ); + + return static::saveTasks( $tasks ); + } + + /** + * Remove task from the list and save it to base + * + * @param string $task + * + * @return bool + */ + static public function removeTask($task) + { + $tasks = static::getTasks(); + + if(!isset($tasks[$task])) + return false; + + unset($tasks[$task]); + + return static::saveTasks($tasks); + } + + /** + * Updates cron task, create task if not exists + * Save it to base + * + * @param string $task + * @param callable $handler + * @param int $period + * @param int|null $first_call + * @param array $params + * + * @return bool + */ + static public function updateTask($task, $handler, $period, $first_call = null, $params = array()){ + return static::removeTask($task) && + static::addTask($task, $handler, $period, $first_call, $params); + } + + /** + * Getting tasks which should be run. + * Putting tasks that should be run to $this->tasks_to_run + * + * @return array|bool + */ + public function checkTasks() + { + if(empty($this->tasks)) + return true; + + foreach($this->tasks as $task => $task_data){ + + if($task_data['next_call'] <= time()) + $this->tasks_to_run[] = $task; + + }unset($task, $task_data); + + return $this->tasks_to_run; + } + + /** + * Runs all tasks from $this->tasks_to_run + * Saves all results to (array) $this->tasks_completed + * + * @return void + */ + public function runTasks() + { + if(empty($this->tasks_to_run)){ + return; + } + + foreach($this->tasks_to_run as $task){ + + $this->selectTask($task); + + if(function_exists($this->handler)){ + + $result = call_user_func_array($this->handler, isset($this->params) ? $this->params : array()); + + if(empty($result['error'])){ + $this->tasks_completed[$task] = true; + $this->errors::delete('cron', $task); + }else{ + $this->tasks_completed[$task] = false; + $this->errors::add('cron', $task, $result); + } + + }else{ + $this->tasks_completed[$task] = false; + $this->errors::add('cron', $task, 'function ' . $this->handler . ' is not exists.'); + } + + $this->saveSelectedTask($task); + + }unset($task); + + // Merging executed tasks with updated during execution + $tasks = $this->getTasks(); + + foreach($tasks as $task => $task_data){ + + // Task where added during execution + if(!isset($this->tasks[$task])){ + $this->tasks[$task] = $task_data; + continue; + } + + // Task where updated during execution + if($task_data !== $this->tasks[$task]){ + $this->tasks[$task] = $task_data; + continue; + } + + // Setting next call depending on results + if(isset($this->tasks[$task], $this->tasks_completed[$task])){ + $this->tasks[$task]['next_call'] = $this->tasks_completed[$task] + ? time() + $this->tasks[$task]['period'] + : time() + round($this->tasks[$task]['period']/4); + } + + if(empty($this->tasks[$task]['next_call']) || $this->tasks[$task]['next_call'] < time()){ + $this->tasks[$task]['next_call'] = time() + $this->tasks[$task]['period']; + } + + } unset($task, $task_data); + + // Task where deleted during execution + $tmp = $this->tasks; + foreach($tmp as $task => $task_data){ + if(!isset($tasks[$task])) + unset($this->tasks[$task]); + } unset($task, $task_data); + + //*/ End of merging + + static::saveTasks( $this->tasks ); + + return; + } + + /** + * Save a task to private properties for comfortable use + * + * @param string $task + */ + protected function selectTask($task) + { + $this->task = $task; + $this->handler = $this->tasks[$task]['handler']; + $this->period = $this->tasks[$task]['period']; + $this->next_call = $this->tasks[$task]['next_call']; + $this->params = isset($this->tasks[$task]['params']) ? $this->tasks[$task]['params'] : array(); + } + + /** + * Save task from private properties to public for comfortable use + * + * @param string $task + */ + protected function saveSelectedTask($task) + { + $task = $this->task; + + $this->tasks[$task]['handler'] = $this->handler; + $this->tasks[$task]['period'] = $this->period; + $this->tasks[$task]['next_call'] = $this->next_call; + $this->tasks[$task]['params'] = $this->params; + } +} diff --git a/cleantalk/lib/CleantalkAP/Common/DB.php b/cleantalk/lib/CleantalkAP/Common/DB.php new file mode 100644 index 0000000..d172524 --- /dev/null +++ b/cleantalk/lib/CleantalkAP/Common/DB.php @@ -0,0 +1,98 @@ +query string for next uses + * + * @param $query + * @return $this + */ + public function set_query($query){ } + + /** + * Safely replace place holders + * + * @param string $query + * @param array $vars + * + * @return $this + */ + public function prepare($query, $vars = array()){ } + + /** + * Run any raw request + * + * @param $query + * + * @return bool|int Raw result + */ + public function execute($query){ } + + /** + * Fetchs first column from query. + * May receive raw or prepared query. + * + * @param bool $query + * @param bool $response_type + * + * @return array|object|void|null + */ + public function fetch($query = false, $response_type = false){ } + + /** + * Fetchs all result from query. + * May receive raw or prepared query. + * + * @param bool $query + * @param bool $response_type + * + * @return array|object|null + */ + public function fetch_all($query = false, $response_type = false){ } +} \ No newline at end of file diff --git a/cleantalk/lib/CleantalkAP/Common/Err.php b/cleantalk/lib/CleantalkAP/Common/Err.php new file mode 100644 index 0000000..03a89cc --- /dev/null +++ b/cleantalk/lib/CleantalkAP/Common/Err.php @@ -0,0 +1,62 @@ +errors[] = implode(': ', func_get_args()); + return static::$instance; + } + + public function prepend( $string ){ + $this->errors[ count( $this->errors ) - 1 ] = $string . ': ' . end( static::getInstance()->errors ); + } + + public function append( $string ){ + $this->string = $string . ': ' . $this->string; + } + + public static function get_last( $output_style = 'bool' ){ + $out = $out = (bool) static::$instance->errors; + if($output_style == 'as_json') + $out = json_encode( array('error' => end( static::$instance->errors ) ), true ); + if($output_style == 'string') + $out = array('error' => end( static::$instance->errors ) ); + return $out; + } + + public function get_all( $output_style = 'string' ){ + $out = static::$instance->errors; + if($output_style == 'as_json') + $out = json_encode( static::$instance->errors, true ); + return $out; + } + + public static function check(){ + return (bool)static::$instance->errors; + } + + public static function check_and_output( $output_style = 'string' ){ + if(static::check()) + return static::$instance->get_last( $output_style ); + else + return false; + } +} \ No newline at end of file diff --git a/cleantalk/lib/CleantalkAP/Common/File.php b/cleantalk/lib/CleantalkAP/Common/File.php new file mode 100644 index 0000000..c9f6cec --- /dev/null +++ b/cleantalk/lib/CleantalkAP/Common/File.php @@ -0,0 +1,179 @@ + array( + '10.0.0.0/8', + '100.64.0.0/10', + '172.16.0.0/12', + '192.168.0.0/16', + '127.0.0.1/32', + ), + 'v6' => array( + '0:0:0:0:0:0:0:1/128', // localhost + '0:0:0:0:0:0:a:1/128', // ::ffff:127.0.0.1 + ), + ); + + /** + * @var array Set of CleanTalk servers + */ + public static $cleantalks_servers = array( + // MODERATE + 'moderate1.cleantalk.org' => '162.243.144.175', + 'moderate2.cleantalk.org' => '159.203.121.181', + 'moderate3.cleantalk.org' => '88.198.153.60', + 'moderate4.cleantalk.org' => '159.69.51.30', + 'moderate5.cleantalk.org' => '95.216.200.119', + 'moderate6.cleantalk.org' => '138.68.234.8', + // APIX + 'apix1.cleantalk.org' => '35.158.52.161', + 'apix2.cleantalk.org' => '18.206.49.217', + 'apix3.cleantalk.org' => '3.18.23.246', + //ns + 'netserv2.cleantalk.org' => '178.63.60.214', + 'netserv3.cleantalk.org' => '188.40.14.173', + ); + + /** + * Getting arrays of IP (REMOTE_ADDR, X-Forwarded-For, X-Real-Ip, Cf_Connecting_Ip) + * + * @param array $ip_types Type of IP you want to receive + * @param bool $v4_only + * + * @return array|mixed|null + */ + static public function ip__get($ip_types = array('real', 'remote_addr', 'x_forwarded_for', 'x_real_ip', 'cloud_flare'), $v4_only = true) + { + $ips = array_flip($ip_types); // Result array with IPs + $headers = apache_request_headers(); + + // REMOTE_ADDR + if(isset($ips['remote_addr'])){ + $ip_type = self::ip__validate(Server::get( 'REMOTE_ADDR' )); + if($ip_type){ + $ips['remote_addr'] = $ip_type == 'v6' ? self::ip__v6_normalize(Server::get( 'REMOTE_ADDR' )) : Server::get( 'REMOTE_ADDR' ); + } + } + + // X-Forwarded-For + if(isset($ips['x_forwarded_for'])){ + if(isset($headers['X-Forwarded-For'])){ + $tmp = explode(",", trim($headers['X-Forwarded-For'])); + $tmp = trim($tmp[0]); + $ip_type = self::ip__validate($tmp); + if($ip_type){ + $ips['x_forwarded_for'] = $ip_type == 'v6' ? self::ip__v6_normalize($tmp) : $tmp; + } + } + } + + // X-Real-Ip + if(isset($ips['x_real_ip'])){ + if(isset($headers['X-Real-Ip'])){ + $tmp = explode(",", trim($headers['X-Real-Ip'])); + $tmp = trim($tmp[0]); + $ip_type = self::ip__validate($tmp); + if($ip_type){ + $ips['x_forwarded_for'] = $ip_type == 'v6' ? self::ip__v6_normalize($tmp) : $tmp; + } + } + } + + // Cloud Flare + if(isset($ips['cloud_flare'])){ + if(isset($headers['CF-Connecting-IP'], $headers['CF-IPCountry'], $headers['CF-RAY']) || isset($headers['Cf-Connecting-Ip'], $headers['Cf-Ipcountry'], $headers['Cf-Ray'])){ + $tmp = isset($headers['CF-Connecting-IP']) ? $headers['CF-Connecting-IP'] : $headers['Cf-Connecting-Ip']; + $tmp = strpos($tmp, ',') !== false ? explode(',', $tmp) : (array)$tmp; + $ip_type = self::ip__validate(trim($tmp[0])); + if($ip_type){ + $ips['real'] = $ip_type == 'v6' ? self::ip__v6_normalize(trim($tmp[0])) : trim($tmp[0]); + } + } + } + + // Getting real IP from REMOTE_ADDR or Cf_Connecting_Ip if set or from (X-Forwarded-For, X-Real-Ip) if REMOTE_ADDR is local. + if(isset($ips['real'])){ + + // Detect IP type + $ip_type = self::ip__validate(Server::get( 'REMOTE_ADDR' ) ); + if($ip_type) + $ips['real'] = $ip_type == 'v6' ? self::ip__v6_normalize(Server::get( 'REMOTE_ADDR' )) : Server::get( 'REMOTE_ADDR' ); + + // Cloud Flare + if(isset($headers['CF-Connecting-IP'], $headers['CF-IPCountry'], $headers['CF-RAY']) || isset($headers['Cf-Connecting-Ip'], $headers['Cf-Ipcountry'], $headers['Cf-Ray'])){ + $tmp = isset($headers['CF-Connecting-IP']) ? $headers['CF-Connecting-IP'] : $headers['Cf-Connecting-Ip']; + $tmp = strpos($tmp, ',') !== false ? explode(',', $tmp) : (array)$tmp; + $ip_type = self::ip__validate(trim($tmp[0])); + if($ip_type) + $ips['real'] = $ip_type == 'v6' ? self::ip__v6_normalize(trim($tmp[0])) : trim($tmp[0]); + + // Sucury + }elseif(isset($headers['X-Sucuri-Clientip'], $headers['X-Sucuri-Country'])){ + $ip_type = self::ip__validate($headers['X-Sucuri-Clientip']); + if($ip_type) + $ips['real'] = $ip_type == 'v6' ? self::ip__v6_normalize($headers['X-Sucuri-Clientip']) : $headers['X-Sucuri-Clientip']; + + // OVH + }elseif(isset($headers['X-Cdn-Any-Ip'], $headers['Remote-Ip'])){ + $ip_type = self::ip__validate($headers['X-Cdn-Any-Ip']); + if($ip_type) + $ips['real'] = $ip_type == 'v6' ? self::ip__v6_normalize($headers['X-Cdn-Any-Ip']) : $headers['X-Cdn-Any-Ip']; + + // Incapsula proxy + }elseif(isset($headers['Incap-Client-Ip'])){ + $ip_type = self::ip__validate($headers['Incap-Client-Ip']); + if($ip_type) + $ips['real'] = $ip_type == 'v6' ? self::ip__v6_normalize($headers['Incap-Client-Ip']) : $headers['Incap-Client-Ip']; + } + + // Is private network + if($ip_type === false || ($ip_type && (self::ip__is_private_network($ips['real'], $ip_type) || self::ip__mask_match($ips['real'], filter_input(INPUT_SERVER, 'SERVER_ADDR') . '/24', $ip_type)))){ + + // X-Forwarded-For + if(isset($headers['X-Forwarded-For'])){ + $tmp = explode(',', trim($headers['X-Forwarded-For'])); + $tmp = trim($tmp[0]); + $ip_type = self::ip__validate($tmp); + if($ip_type) + $ips['real'] = $ip_type == 'v6' ? self::ip__v6_normalize($tmp) : $tmp; + + // X-Real-Ip + }elseif(isset($headers['X-Real-Ip'])){ + $tmp = explode(',', trim($headers['X-Real-Ip'])); + $tmp = trim($tmp[0]); + $ip_type = self::ip__validate($tmp); + if($ip_type) + $ips['real'] = $ip_type == 'v6' ? self::ip__v6_normalize($tmp) : $tmp; + } + } + } + + // Validating IPs + $result = array(); + foreach($ips as $key => $ip){ + $ip_version = self::ip__validate($ip); + if($ip && (($v4_only && $ip_version == 'v4') || !$v4_only)){ + $result[$key] = $ip; + } + } + + $result = array_unique($result); + return count($result) > 1 + ? $result + : (reset($result) !== false + ? reset($result) + : null); + } + + /** + * Checks if the IP is in private range + * + * @param string $ip + * @param string $ip_type + * + * @return bool + */ + static function ip__is_private_network($ip, $ip_type = 'v4') + { + return self::ip__mask_match($ip, self::$private_networks[$ip_type], $ip_type); + } + + /** + * Check if the IP belong to mask. Recursive. + * Octet by octet for IPv4 + * Hextet by hextet for IPv6 + * + * @param string $ip + * @param string $cidr work to compare with + * @param string $ip_type IPv6 or IPv4 + * @param int $xtet_count Recursive counter. Determs current part of address to check. + * + * @return bool + */ + static public function ip__mask_match($ip, $cidr, $ip_type = 'v4', $xtet_count = 0) + { + if(is_array($cidr)){ + foreach($cidr as $curr_mask){ + if(self::ip__mask_match($ip, $curr_mask, $ip_type)){ + return true; + } + } + unset($curr_mask); + return false; + } + + $xtet_base = ($ip_type == 'v4') ? 8 : 16; + + // Calculate mask + $exploded = explode('/', $cidr); + $net_ip = $exploded[0]; + $mask = $exploded[1]; + + // Exit condition + $xtet_end = ceil($mask / $xtet_base); + if($xtet_count == $xtet_end) + return true; + + // Lenght of bits for comparsion + $mask = $mask - $xtet_base * $xtet_count >= $xtet_base ? $xtet_base : $mask - $xtet_base * $xtet_count; + + // Explode by octets/hextets from IP and Net + $net_ip_xtets = explode($ip_type == 'v4' ? '.' : ':', $net_ip); + $ip_xtets = explode($ip_type == 'v4' ? '.' : ':', $ip); + + // Standartizing. Getting current octets/hextets. Adding leading zeros. + $net_xtet = str_pad(decbin($ip_type == 'v4' ? $net_ip_xtets[$xtet_count] : hexdec($net_ip_xtets[$xtet_count])), $xtet_base, 0, STR_PAD_LEFT); + $ip_xtet = str_pad(decbin($ip_type == 'v4' ? $ip_xtets[$xtet_count] : hexdec($ip_xtets[$xtet_count])), $xtet_base, 0, STR_PAD_LEFT); + + // Comparing bit by bit + for($i = 0, $result = true; $mask != 0; $mask--, $i++){ + if($ip_xtet[$i] != $net_xtet[$i]){ + $result = false; + break; + } + } + + // Recursing. Moving to next octet/hextet. + if($result) + $result = self::ip__mask_match($ip, $cidr, $ip_type, $xtet_count + 1); + + return $result; + + } + + /** + * Converts long mask like 4294967295 to number like 32 + * + * @param int $long_mask + * + * @return int + */ + static function ip__mask__long_to_number($long_mask) + { + $num_mask = strpos((string)decbin($long_mask), '0'); + return $num_mask === false ? 32 : $num_mask; + } + + /** + * Validating IPv4, IPv6 + * + * @param string $ip + * + * @return string|bool + */ + static public function ip__validate($ip) + { + if(!$ip) return false; // NULL || FALSE || '' || so on... + if(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) && $ip != '0.0.0.0') return 'v4'; // IPv4 + if(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) && self::ip__v6_reduce($ip) != '0::0') return 'v6'; // IPv6 + return false; // Unknown + } + + /** + * Expand IPv6 + * + * @param string $ip + * + * @return string IPv6 + */ + static public function ip__v6_normalize($ip) + { + $ip = trim($ip); + // Searching for ::ffff:xx.xx.xx.xx patterns and turn it to IPv6 + if(preg_match('/^::ffff:([0-9]{1,3}\.?){4}$/', $ip)){ + $ip = dechex(sprintf("%u", ip2long(substr($ip, 7)))); + $ip = '0:0:0:0:0:0:' . (strlen($ip) > 4 ? substr('abcde', 0, -4) : '0') . ':' . substr($ip, -4, 4); + // Normalizing hextets number + }elseif(strpos($ip, '::') !== false){ + $ip = str_replace('::', str_repeat(':0', 8 - substr_count($ip, ':')) . ':', $ip); + $ip = strpos($ip, ':') === 0 ? '0' . $ip : $ip; + $ip = strpos(strrev($ip), ':') === 0 ? $ip . '0' : $ip; + } + // Simplifyng hextets + if(preg_match('/:0(?=[a-z0-9]+)/', $ip)){ + $ip = preg_replace('/:0(?=[a-z0-9]+)/', ':', strtolower($ip)); + $ip = self::ip__v6_normalize($ip); + } + return $ip; + } + + /** + * Reduce IPv6 + * + * @param string $ip + * + * @return string IPv6 + */ + static public function ip__v6_reduce($ip) + { + if(strpos($ip, ':') !== false){ + $ip = preg_replace('/:0{1,4}/', ':', $ip); + $ip = preg_replace('/:{2,}/', '::', $ip); + $ip = strpos($ip, '0') === 0 ? substr($ip, 1) : $ip; + } + return $ip; + } + + /** + * Get URL form IP. Check if it's belong to cleantalk. + * + * @param string $ip + * + * @return false|int|string + */ + static public function ip__is_cleantalks($ip) + { + if(self::ip__validate($ip)){ + $url = array_search($ip, self::$cleantalks_servers); + return $url + ? true + : false; + }else + return false; + } + + /** + * Get URL form IP. Check if it's belong to cleantalk. + * + * @param $ip + * + * @return false|int|string + */ + static public function ip__resolve__cleantalks($ip) + { + if(self::ip__validate($ip)){ + $url = array_search($ip, self::$cleantalks_servers); + return $url + ? $url + : self::ip__resolve($ip); + }else + return $ip; + } + + /** + * Get URL form IP + * + * @param $ip + * + * @return string + */ + static public function ip__resolve($ip) + { + if(self::ip__validate($ip)){ + $url = gethostbyaddr($ip); + if($url) + return $url; + } + return $ip; + } + + /** + * Resolve DNS to IP + * + * @param $host + * @param bool $out + * + * @return bool + */ + static public function dns__resolve($host, $out = false) + { + + // Get DNS records about URL + if(function_exists('dns_get_record')){ + $records = dns_get_record($host, DNS_A); + if($records !== false){ + $out = $records[0]['ip']; + } + } + + // Another try if first failed + if(!$out && function_exists('gethostbynamel')){ + $records = gethostbynamel($host); + if($records !== false){ + $out = $records[0]; + } + } + + return $out; + + } + + static public function http__user_agent(){ + return defined( 'CLEANTALK_USER_AGENT' ) ? CLEANTALK_USER_AGENT : static::DEFAULT_USER_AGENT; + } + + /** + * Function sends raw http request + * + * May use 4 presets(combining possible): + * get_code - getting only HTTP response code + * async - async requests + * get - GET-request + * ssl - use SSL + * + * @param string $url URL + * @param array $data POST|GET indexed array with data to send + * @param string|array $presets String or Array with presets: get_code, async, get, ssl, dont_split_to_array + * @param array $opts Optional option for CURL connection + * + * @return array|bool (array || array('error' => true)) + */ + static public function http__request($url, $data = array(), $presets = null, $opts = array()) + { + // For debug purposes + if( defined( 'CLEANTALK_DEBUG' ) && CLEANTALK_DEBUG ){ + global $apbct_debug; + $apbct_debug['data'] = $data; + } + + // Preparing presets + $presets = is_array($presets) ? $presets : explode(' ', $presets); + $curl_only = in_array( 'async', $presets ) || + in_array( 'dont_follow_redirects', $presets ) || + in_array( 'ssl', $presets ) || + in_array( 'split_to_array', $presets ) + ? true : false; + + if(function_exists('curl_init')){ + + $ch = curl_init(); + + // Set data if it's not empty + if(!empty($data)){ + // If $data scalar converting it to array + $opts[CURLOPT_POSTFIELDS] = is_scalar($data) + ? array($data) + : $data; + } + + // Merging OBLIGATORY options with GIVEN options + // Using POST method by default + $opts = static::array_merge__save_numeric_keys( + array( + CURLOPT_URL => $url, + CURLOPT_TIMEOUT => 5, + CURLOPT_CONNECTTIMEOUT_MS => 3000, + CURLOPT_FORBID_REUSE => true, + CURLOPT_POST => true, + CURLOPT_SSL_VERIFYPEER => false, + CURLOPT_SSL_VERIFYHOST => 0, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_MAXREDIRS => 5, + CURLOPT_USERAGENT => static::http__user_agent() . '; ' . ( ! empty( Server::get( 'SERVER_NAME' ) ) ? Server::get( 'SERVER_NAME' ) : 'UNKNOWN_HOST' ), + CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_0, // see http://stackoverflow.com/a/23322368 + CURLOPT_RETURNTRANSFER => true, // receive server response ... + CURLOPT_HTTPHEADER => array('Expect:'), // Fix for large data and old servers http://php.net/manual/ru/function.curl-setopt.php#82418 + ), + $opts + ); + + foreach($presets as $preset){ + + switch($preset){ + + // Do not follow redirects + case 'dont_follow_redirects': + $opts[CURLOPT_FOLLOWLOCATION] = false; + $opts[CURLOPT_MAXREDIRS] = 0; + break; + + // Get headers only + case 'get_code': + $opts[CURLOPT_HEADER] = true; + $opts[CURLOPT_NOBODY] = true; + break; + + // Make a request, don't wait for an answer + case 'async': + $opts[CURLOPT_CONNECTTIMEOUT_MS] = 1000; + $opts[CURLOPT_TIMEOUT_MS] = 500; + break; + + case 'get': + $opts[CURLOPT_URL] .= $data ? '?' . str_replace( "&", "&", http_build_query( $data ) ) : ''; + $opts[CURLOPT_POST] = false; + unset($opts[CURLOPT_POSTFIELDS]); + break; + + case 'ssl': + $opts[CURLOPT_SSL_VERIFYPEER] = true; + $opts[CURLOPT_SSL_VERIFYHOST] = 2; + if(defined('CLEANTALK_CASERT_PATH') && CLEANTALK_CASERT_PATH) + $opts[CURLOPT_CAINFO] = CLEANTALK_CASERT_PATH; + break; + + default: + + break; + } + } + unset($preset); + + curl_setopt_array($ch, $opts); + $result = curl_exec($ch); + + // RETURN if async request + if(in_array('async', $presets)) + return true; + + if($result){ + + // Split to array by lines if such preset given + if( in_array( 'split_to_array', $presets ) ) + $result = explode(PHP_EOL, $result); + + // Get code crossPHP method + if(in_array('get_code', $presets)){ + $curl_info = curl_getinfo($ch); + $result = $curl_info['http_code']; + } + + $out = $result; + + }else + $out = array('error' => curl_error($ch)); + + curl_close($ch); + + // Curl not installed. Trying file_get_contents() + }elseif( ini_get( 'allow_url_fopen' ) && ! $curl_only ){ + + // Trying to get code via get_headers() + if( in_array( 'get_code', $presets ) ){ + $headers = get_headers( $url ); + $result = (int) preg_replace( '/.*(\d{3}).*/', '$1', $headers[0] ); + + // Making common request + }else{ + $opts = array( + 'http' => array( + 'method' => in_array( 'get', $presets ) ? 'GET' : 'POST', + 'timeout' => 5, + 'content' => str_replace( "&", "&", http_build_query( $data ) ), + ), + ); + $context = stream_context_create( $opts ); + $result = @file_get_contents( $url, 0, $context ); + } + + $out = $result === false + ? 'FAILED_TO_USE_FILE_GET_CONTENTS' + : $result; + + }else + $out = array('error' => 'CURL not installed and allow_url_fopen is disabled'); + + return $out; + } + + /** + * Merging arrays without reseting numeric keys + * + * @param array $arr1 One-dimentional array + * @param array $arr2 One-dimentional array + * + * @return array Merged array + */ + public static function array_merge__save_numeric_keys($arr1, $arr2) + { + foreach($arr2 as $key => $val){ + $arr1[$key] = $val; + } + return $arr1; + } + + /** + * Merging arrays without reseting numeric keys recursive + * + * @param array $arr1 One-dimentional array + * @param array $arr2 One-dimentional array + * + * @return array Merged array + */ + public static function array_merge__save_numeric_keys__recursive($arr1, $arr2) + { + foreach($arr2 as $key => $val){ + + // Array | array => array + if(isset($arr1[$key]) && is_array($arr1[$key]) && is_array($val)){ + $arr1[$key] = self::array_merge__save_numeric_keys__recursive($arr1[$key], $val); + + // Scalar | array => array + }elseif(isset($arr1[$key]) && !is_array($arr1[$key]) && is_array($val)){ + $tmp = $arr1[$key] = + $arr1[$key] = $val; + $arr1[$key][] = $tmp; + + // array | scalar => array + }elseif(isset($arr1[$key]) && is_array($arr1[$key]) && !is_array($val)){ + $arr1[$key][] = $val; + + // scalar | scalar => scalar + }else{ + $arr1[$key] = $val; + } + } + return $arr1; + } + + /** + * Function removing non UTF8 characters from array|string|object + * + * @param array|object|string $data + * + * @return array|object|string + */ + public static function removeNonUTF8($data) + { + // Array || object + if(is_array($data) || is_object($data)){ + foreach($data as $key => &$val){ + $val = self::removeNonUTF8($val); + } + unset($key, $val); + + //String + }else{ + if(!preg_match('//u', $data)) + $data = 'Nulled. Not UTF8 encoded or malformed.'; + } + return $data; + } + + /** + * Function convert anything to UTF8 and removes non UTF8 characters + * + * @param array|object|string $obj + * @param string $data_codepage + * + * @return mixed(array|object|string) + */ + public static function toUTF8($obj, $data_codepage = null) + { + // Array || object + if(is_array($obj) || is_object($obj)){ + foreach($obj as $key => &$val){ + $val = self::toUTF8($val, $data_codepage); + } + unset($key, $val); + + //String + }else{ + if(!preg_match('//u', $obj) && function_exists('mb_detect_encoding') && function_exists('mb_convert_encoding')){ + $encoding = mb_detect_encoding($obj); + $encoding = $encoding ? $encoding : $data_codepage; + if($encoding) + $obj = mb_convert_encoding($obj, 'UTF-8', $encoding); + } + } + return $obj; + } + + /** + * Function convert from UTF8 + * + * @param array|object|string $obj + * @param string $data_codepage + * + * @return mixed (array|object|string) + */ + public static function fromUTF8($obj, $data_codepage = null) + { + // Array || object + if(is_array($obj) || is_object($obj)){ + foreach($obj as $key => &$val){ + $val = self::fromUTF8($val, $data_codepage); + } + unset($key, $val); + + //String + }else{ + if(preg_match('u', $obj) && function_exists('mb_convert_encoding') && $data_codepage !== null) + $obj = mb_convert_encoding($obj, $data_codepage, 'UTF-8'); + } + return $obj; + } + + /** + * Checks if the string is JSON type + * + * @param string + * + * @return bool + */ + static public function is_json($string) + { + return is_string($string) && is_array(json_decode($string, true)) ? true : false; + } + + /** + * Checks if given string is valid regular expression + * + * @param string $regexp + * + * @return bool + */ + static public function is_regexp($regexp){ + return @preg_match('/' . $regexp . '/', null) !== false; + } + + static public function convert_to_regexp( $string ){ + $string = preg_replace( '/\$/', '\\\\$', $string ); + $string = preg_replace( '/\//', '\/', $string ); + return $string; + } + + static function get_mime_type($data ) + { + if( @file_exists( $data )){ + $mime = mime_content_type( $data ); + }else{ + $finfo = finfo_open(FILEINFO_MIME_TYPE); + $mime = finfo_buffer($finfo, $data); + finfo_close($finfo); + } + return $mime; + } + + static function buffer__trim_and_clear_from_empty_lines( $buffer ){ + $buffer = (array) $buffer; + foreach( $buffer as $indx => &$line ){ + $line = trim( $line ); + if($line === '') + unset( $buffer[$indx] ); + } + return $buffer; + } + + static function buffer__parse__csv( $buffer ){ + $buffer = explode( "\n", $buffer ); + $buffer = self::buffer__trim_and_clear_from_empty_lines( $buffer ); + foreach($buffer as &$line){ + $line = str_getcsv($line, ',', '\''); + } + return $buffer; + } +} \ No newline at end of file diff --git a/cleantalk/lib/CleantalkAP/Common/State.php b/cleantalk/lib/CleantalkAP/Common/State.php new file mode 100644 index 0000000..17bf7b3 --- /dev/null +++ b/cleantalk/lib/CleantalkAP/Common/State.php @@ -0,0 +1,384 @@ + -1, + '2fa_roles' => array('administrator'), + 'block_timer__1_fails' => 3, + 'block_timer__5_fails' => 10, + + // Key + 'spbc_key' => '', + 'custom_key' => 0, + + // Traffic Control + 'traffic_control_enabled' => 1, + 'traffic_control_autoblock_amount' => 1000, + 'traffic_control_autoblock_period' => 3600, + + // Scanner + 'scanner_auto_start' => 1, + 'scanner_outbound_links' => 0, + 'scanner_outbound_links_mirrors' => '', + 'scanner_heuristic_analysis' => 1, + 'scanner_signature_analysis' => 1, + 'scanner_auto_cure' => 1, + 'scanner_frontend_analysis' => 0, + + // Web Application Firewall + 'waf_enabled' => 1, + 'waf_xss_check' => 1, + 'waf_sql_check' => 1, + 'waf_file_check' => 1, + 'waf_exploit_check' => 1, + + // Misc + 'backend_logs_enable' => 1, + 'set_cookies' => 1, + 'forbid_to_show_in_iframes' => 1, + 'show_link_in_login_form' => 1, + 'use_buitin_http_api' => 0, + 'complete_deactivation' => 0, + ); + public $def_data = array( + 'plugin_version' => SPBC_VERSION, + 'user_token' => '', + 'key_is_ok' => false, + 'moderate' => false, + 'logs_last_sent' => null, + 'last_sent_events_count' => null, + 'last_firewall_updated' => null, + 'firewall_entries' => null, + 'last_firewall_send' => null, + 'last_firewall_send_count' => null, + 'notice_show' => null, + 'notice_renew' => false, + 'notice_trial' => false, + 'notice_were_updated' => false, + 'service_id' => '', + 'license_trial' => 0, + 'account_name_ob' => '', + 'salt' => '', + 'scanner' => array( + 'last_signature_update' => null, + 'last_wp_version' => null, + 'cron' => array( + 'state' => 'get_hashes', + 'total_scanned' => 0, + 'offset' => 0, + ), + 'cured' => array(), + 'last_backup' => 0, + ), + 'cron' => array( + 'running' => false, + ), + 'errors' => array( + 'cron' => array( + + ), + ), + 'last_php_log_sent' => 0, + '2fa_keys' => array(), + ); + public $def_network_settings = array( + 'allow_custom_key' => false, + 'allow_cleantalk_cp' => false, + 'key_is_ok' => false, + 'spbc_key' => '', + 'user_token' => '', + 'service_id' => '', + 'moderate' => 0 + ); + + public $def_remote_calls = array( + + // Common + 'close_renew_banner' => array('last_call' => 0,), + 'update_plugin' => array('last_call' => 0,), + 'update_security_firewall' => array('last_call' => 0, 'cooldown' => 3), + 'drop_security_firewall' => array('last_call' => 0,), + 'update_settings' => array('last_call' => 0,), + + // Inner + 'download__quarantine_file' => array('last_call' => 0, 'cooldown' => 3), + + // Backups + 'backup_signatures_files' => array('last_call' => 0,), + 'rollback_repair' => array('last_call' => 0,), + + // Scanner + 'scanner_signatures_update' => array('last_call' => 0,), + 'scanner_clear_hashes' => array('last_call' => 0,), + + 'scanner__controller' => array('last_call' => 0, 'cooldown' => 3), + 'scanner__get_remote_hashes' => array('last_call' => 0,), + 'scanner__count_hashes_plug' => array('last_call' => 0,), + 'scanner__get_remote_hashes__plug' => array('last_call' => 0,), + 'scanner__clear_table' => array('last_call' => 0,), + 'scanner__count_files' => array('last_call' => 0,), + 'scanner__scan' => array('last_call' => 0,), + 'scanner__count_files__by_status' => array('last_call' => 0,), + 'scanner__scan_heuristic' => array('last_call' => 0,), + 'scanner__scan_signatures' => array('last_call' => 0,), + 'scanner__count_cure' => array('last_call' => 0,), + 'scanner__cure' => array('last_call' => 0,), + 'scanner__links_count' => array('last_call' => 0,), + 'scanner__links_scan' => array('last_call' => 0,), + 'scanner__frontend_scan' => array('last_call' => 0,), + ); + + public $def_errors = array(); + + public function __construct($option_prefix, $options = array('settings'), $wpms = false) + { + $this->option_prefix = $option_prefix; + + if($wpms){ + $option = get_site_option($this->option_prefix.'_network_settings'); + $option = is_array($option) ? $option : $this->def_network_settings; + $this->network_settings = new \ArrayObject($option); + } + + foreach($options as $option_name){ + + $option = get_option($this->option_prefix.'_'.$option_name); + + // Default options + if($this->option_prefix.'_'.$option_name === 'spbc_settings'){ + $option = is_array($option) ? array_merge($this->def_settings, $option) : $this->def_settings; + if(!is_main_site()) $option['backend_logs_enable'] = 0; + } + + // Default data + if($this->option_prefix.'_'.$option_name === 'spbc_data'){ + $option = is_array($option) ? array_merge($this->def_data, $option) : $this->def_data; + if(empty($option['salt'])) $option['salt'] = str_pad(rand(0, getrandmax()), 6, '0').str_pad(rand(0, getrandmax()), 6, '0'); + if(empty($option['last_php_log_sent'])) $option['last_php_log_sent'] = time(); + } + + // Default errors + if($this->option_prefix.'_'.$option_name === 'spbc_errors'){ + $option = is_array($option) ? array_merge($this->def_errors, $option) : $this->def_errors; + } + + // Default remote calls + if($this->option_prefix.'_'.$option_name === 'spbc_remote_calls'){ + $option = is_array($option) ? array_merge($this->def_remote_calls, $option) : $this->def_remote_calls; + } + + $this->$option_name = is_array($option) ? new \ArrayObject($option) : $option; + + } + } + + private function getOption($option_name) + { + $option = get_option('spbc_'.$option_name); + $this->$option_name = gettype($option) === 'array' + ? new \ArrayObject($option) + : $option; + } + + /** + * @param string $option_name + * @param bool $use_perfix + * @param bool $autoload + */ + public function save($option_name, $use_perfix = true, $autoload = true) + { + $option_name_to_save = $use_perfix ? $this->option_prefix.'_'.$option_name : $option_name; + $arr = array(); + foreach($this->$option_name as $key => $value){ + $arr[$key] = $value; + } + update_option($option_name_to_save, $arr, $autoload); + } + + public function saveSettings() + { + update_option($this->option_prefix.'_settins', $this->settings); + } + + public function saveData() + { + update_option($this->option_prefix.'_data', $this->data); + } + + public function saveNetworkSettings() + { + update_site_option($this->option_prefix.'_network_settings', $this->network_settings); + } + + public function deleteOption($option_name, $use_prefix = false) + { + if($this->__isset($option_name)){ + $this->__unset($option_name); + delete_option( ($use_prefix ? $this->option_prefix.'_' : '') . $option_name); + } + } + + /** + * Prepares an adds an error to the plugin's data + * + * @param string $type + * @param string $error + * @param string $major_type + * @param bool $set_time + * + * @return void + */ + public function error_add($type, $error, $major_type = null, $set_time = true) + { + $error = is_array($error) + ? $error['error'] + : $error; + + // Exceptions + if( ($type == 'send_logs' && $error == 'NO_LOGS_TO_SEND') || + ($type == 'send_firewall_logs' && $error == 'NO_LOGS_TO_SEND') || + $error == 'LOG_FILE_NOT_EXISTS' + ) + return; + + $error = array( + 'error' => $error, + 'error_time' => $set_time ? current_time('timestamp') : null, + ); + + if(!empty($major_type)){ + $this->errors[$major_type][$type] = $error; + }else{ + $this->errors[$type] = $error; + } + + $this->save('errors'); + } + + /** + * Deletes an error from the plugin's data + * + * @param string $type + * @param bool $save_flag + * @param string $major_type + * + * @return void + */ + public function error_delete($type, $save_flag = false, $major_type = null) + { + if(is_string($type)) + $type = explode(' ', $type); + + foreach($type as $val){ + if($major_type){ + if(isset($this->errors[$major_type][$val])) + unset($this->errors[$major_type][$val]); + }else{ + if(isset($this->errors[$val])) + unset($this->errors[$val]); + } + } + + // Save if flag is set and there are changes + if($save_flag) + $this->save('errors'); + } + + /** + * Deletes all errors from the plugin's data + * + * @param bool $save_flag + * + * @return void + */ + public function error_delete_all($save_flag = false) + { + $this->errors = new \ArrayObject($this->def_errors); + if($save_flag) + $this->save('errors'); + } + + public function error_toggle($add_flag = true, $type, $save_flag = false, $major_type = null){ + if($add_flag) + $this->error_add($type, $save_flag, $major_type); + else + $this->error_delete($type, $save_flag, $major_type); + } + + public function __set($name, $value) + { + $this->storage[$name] = $value; + } + + public function __get($name) + { + if (array_key_exists($name, $this->storage)){ + return $this->storage[$name]; + }else{ + $this->getOption($name); + return $this->storage[$name]; + } + + // return !empty($this->storage[$name]) ? $this->storage[$name] : null; + } + + public function __isset($name) + { + return isset($this->storage[$name]); + } + + public function __unset($name) + { + unset($this->storage[$name]); + } + + public function __call($name, $arguments) + { + error_log ("Calling method '$name' with arguments: " . implode(', ', $arguments). "\n"); + } + + public static function __callStatic($name, $arguments) + { + error_log("Calling static method '$name' with arguments: " . implode(', ', $arguments). "\n"); + } + + public function server(){ + return \CleantalkAP\Variables\Server::getInstance(); + } + public function cookie(){ + return \CleantalkAP\Variables\Cookie::getInstance(); + } + public function request(){ + return \CleantalkAP\Variables\Request::getInstance(); + } + public function post(){ + return \CleantalkAP\Variables\Post::getInstance(); + } + public function get(){ + return \CleantalkAP\Variables\Get::getInstance(); + } +} diff --git a/cleantalk/lib/CleantalkAP/SMF/API.php b/cleantalk/lib/CleantalkAP/SMF/API.php new file mode 100644 index 0000000..7a255c4 --- /dev/null +++ b/cleantalk/lib/CleantalkAP/SMF/API.php @@ -0,0 +1,80 @@ +settings['use_buitin_http_api']){ + + $args = array( + 'body' => $data, + 'timeout' => 5, + 'user-agent' => SPBC_AGENT.' '.get_bloginfo( 'url' ), + ); + + $result = wp_remote_post($url, $args); + + if( is_wp_error( $result ) ) { + $errors = $result->get_error_message(); + $result = false; + }else{ + $result = wp_remote_retrieve_body($result); + } + + // Use Cleantalk CURL version if disabled + }else{ + // Default preset is 'api' + $presets = array( 'api' ); + + // Add ssl to 'presets' if enabled + if( $ssl ) + array_push( $presets, 'ssl' ); + + $result = \CleantalkAP\SMF\Helper::http__request( $url, $data, $presets ); + + // Retry with SSL enabled if failed + if( ! empty ( $result['error'] ) && $ssl === false ) + $result = \CleantalkAP\SMF\Helper::http__request( $url, $data, 'api ssl' ); + } + + return empty($result) || !empty($errors) + ? array('error' => $errors) + : $result; + + } +} \ No newline at end of file diff --git a/cleantalk/lib/CleantalkAP/SMF/Cron.php b/cleantalk/lib/CleantalkAP/SMF/Cron.php new file mode 100644 index 0000000..1534d4a --- /dev/null +++ b/cleantalk/lib/CleantalkAP/SMF/Cron.php @@ -0,0 +1,56 @@ +spbc = $spbc; + // $this->load(); + } + + /** + * Loads saved errors from DB + */ + public function load(){ + $errors = get_option('spbc_errors'); + $this->errors = $errors ? $errors : array(); + } + + /** + * Save loaded errors to DB + */ + public function save(){ + update_option('spbct_errors', $this->errors); + } + + /** + * Adds new error + */ + public static function add(){ + $args = func_get_args(); + if(count($args) === 3){ + static::getInstance()->spbc->error_add( + $args[2], + $args[1], + $args[0] + ); + }else{ + static::getInstance()->spbc->error_add( + $args[0], + $args[1], + ); + } + } + + /** + * Adds new error + */ + public static function delete(){ + $args = func_get_args(); + if(count($args) === 2){ + static::getInstance()->spbc->error_delete( + $args[1], + true, + $args[0], + ); + }else{ + static::getInstance()->spbc->error_delete( + $args[0], + true + ); + } + } +} \ No newline at end of file diff --git a/cleantalk/lib/CleantalkAP/SMF/FireWall.php b/cleantalk/lib/CleantalkAP/SMF/FireWall.php new file mode 100644 index 0000000..dcdee2f --- /dev/null +++ b/cleantalk/lib/CleantalkAP/SMF/FireWall.php @@ -0,0 +1,714 @@ +', + '', + 'javascript:', +// 'data:', + ); + + public $waf_sql_patterns = array( + '-\d\s?union', + ';\s?union', + ';\s?or', + 'pid=\d+\+union\+select\+0x5e2526,0x5e2526,0x5e2526,' + ); + + public $waf_exploit_patterns = array( + '(random.*)?action=update-plugin(.*random)?', + ); + + public $waf_file_mime_check = array( + 'text/x-php', + 'text/plain', + 'image/x-icon', + ); + + public $statuses_priority = array( + 'PASS', + 'DENY', + 'DENY_BY_NETWORK', + 'DENY_BY_DOS', + 'PASS_BY_WHITELIST', + 'PASS_BY_TRUSTED_NETWORK', // Highest + ); + /** + * @var int + */ + + function __construct($params = array()){ + + // TC + $this->tc_enabled = isset($params['tc_enabled']) ? (bool)$params['tc_enabled'] : false; + $this->tc_limit = isset($params['tc_limit']) ? (int)$params['tc_limit'] : 1000; + $this->tc_period = isset($params['tc_period']) ? (int)$params['tc_period'] : 3600; + $this->tc_period = isset($params['tc_period']) ? (int)$params['tc_period'] : 3600; + + $this->chance_to_clean = 100; // from 0 to 1000 + $this->store_interval = 300; // in seconds + + // WAF + $this->waf_enabled = isset($params['waf_enabled']) ? (bool)$params['waf_enabled'] : false; + $this->waf_xss_check = isset($params['waf_xss_check']) ? (bool)$params['waf_xss_check'] : false; + $this->waf_sql_check = isset($params['waf_sql_check']) ? (bool)$params['waf_sql_check'] : false; + $this->waf_file_check = isset($params['waf_file_check']) ? (bool)$params['waf_file_check'] : false; + $this->waf_exploit_check = isset($params['waf_exploit_check']) ? (bool)$params['waf_exploit_check'] : false; + + // MISC + $this->was_logged_in = isset($params['was_logged_in']) ? (bool)$params['was_logged_in'] : false; + + $this->ip_array = (array)static::ip__get(array('real')); + + $this->db = $params['db']; + } + + static public function ip__get($ip_types = array('real', 'remote_addr', 'x_forwarded_for', 'x_real_ip', 'cloud_flare')){ + + $result = (array)SpbcHelper::ip__get($ip_types); + + global $spbc; + + if(isset($_GET['spbct_test_ip'], $_GET['spbct_test'], $spbc->settings['spbc_key']) && $_GET['spbct_test'] == md5($spbc->settings['spbc_key'])){ + $ip_type = SpbcHelper::ip__validate($_GET['spbct_test_ip']); + $test_ip = $ip_type == 'v6' ? SpbcHelper::ip__v6_normalize($_GET['spbct_test_ip']) : $_GET['spbct_test_ip']; + if($ip_type) + $result['test'] = $test_ip; + } + + return $result; + } + + public function ip__test(){ + + global $wpdb; + + $fw_results = array(); + + foreach($this->ip_array as $ip_origin => $current_ip){ + + $ip_type = SpbcHelper::ip__validate($current_ip); + + if($ip_type && $ip_type == 'v4'){ + + $current_ip_v4 = sprintf("%u", ip2long($current_ip)); + + $sql = 'SELECT status, is_personal + FROM `'. SPBC_TBL_FIREWALL_DATA ."` + WHERE spbc_network_4 = $current_ip_v4 & spbc_mask_4 + AND ipv6 = 0;"; + + }elseif($ip_type){ + + $current_ip_txt = explode(':', $current_ip); + $current_ip_1 = hexdec($current_ip_txt[0].$current_ip_txt[1]); + $current_ip_2 = hexdec($current_ip_txt[2].$current_ip_txt[3]); + $current_ip_3 = hexdec($current_ip_txt[4].$current_ip_txt[5]); + $current_ip_4 = hexdec($current_ip_txt[6].$current_ip_txt[7]); + + $sql = 'SELECT status, is_personal + FROM `'. SPBC_TBL_FIREWALL_DATA ."` + WHERE spbc_network_1 = $current_ip_1 & spbc_mask_1 + AND spbc_network_2 = $current_ip_2 & spbc_mask_2 + AND spbc_network_3 = $current_ip_3 & spbc_mask_3 + AND spbc_network_4 = $current_ip_4 & spbc_mask_4 + AND ipv6 = 1;"; + } + + $result = $wpdb->get_results($sql, ARRAY_A); + + // In base + if(!empty($result)){ + + $in_base = true; + foreach($result as $entry){ + switch ($entry['status']) { + case 2: $fw_results[] = array('ip' => $current_ip, 'is_personal' => (bool)$entry['is_personal'], 'status' => 'PASS_BY_TRUSTED_NETWORK',); $this->tc_skip = true; break; + case 1: $fw_results[] = array('ip' => $current_ip, 'is_personal' => (bool)$entry['is_personal'], 'status' => 'PASS_BY_WHITELIST',); $this->tc_skip = true; break; + case 0: $fw_results[] = array('ip' => $current_ip, 'is_personal' => (bool)$entry['is_personal'], 'status' => 'DENY',); break; + case -1: $fw_results[] = array('ip' => $current_ip, 'is_personal' => (bool)$entry['is_personal'], 'status' => 'DENY_BY_NETWORK',); break; + case -2: $fw_results[] = array('ip' => $current_ip, 'is_personal' => (bool)$entry['is_personal'], 'status' => 'DENY_BY_DOS',); break; + } + } + + // Not in base + }else + $fw_results[] = array('ip' => $current_ip, 'is_personal' => false, 'status' => 'PASS',); + } + + $current_fw_result_priority = 0; + foreach ($fw_results as $fw_result) { + $priority = array_search($fw_result['status'], $this->statuses_priority) + ($fw_result['is_personal'] ? count($this->statuses_priority) : 0); + if($priority >= $current_fw_result_priority){ + $current_fw_result_priority = $priority; + $this->result = $fw_result['status']; + $this->passed_ip = $fw_result['ip']; + $this->blocked_ip = $fw_result['ip']; + } + } + + if(!$this->tc_enabled && $priority == 0){ + $this->result = null; + $this->passed_ip = ''; + $this->blocked_ip = ''; + } + } + + public function tc__test(){ + if($this->tc_enabled && !$this->tc_skip && !$this->was_logged_in){ + $this->tc__clear_table(); + $time = time(); + foreach($this->ip_array as $ip_origin => $current_ip){ + $result = $this->db->get_results( + "SELECT SUM(entries) as total_count" + . ' FROM `' . SPBC_TBL_TC_LOG . '`' + . " WHERE ip = '$current_ip' AND interval_start < '$time';", + OBJECT + ); + if(!empty($result) && $result[0]->total_count >= $this->tc_limit){ + $this->result = 'DENY_BY_DOS'; + $this->blocked_ip = $current_ip; + return; + } + } + } + } + + public function tc__update_logs( $ip = array() ){ + $ip = !empty( $ip ) ? $ip : $this->ip_array; + $interval_time = SpbcHelper::time__get_interval_start( $this->store_interval ); + foreach($this->ip_array as $ip_origin => $current_ip){ + $id = md5( $current_ip . $interval_time ); + $this->db->query( + "INSERT INTO " . SPBC_TBL_TC_LOG . " SET + id = '$id', + ip = '$current_ip', + entries = 1, + interval_start = $interval_time + ON DUPLICATE KEY UPDATE + ip = ip, + entries = entries + 1, + interval_start = $interval_time;" + ); + } + } + + + public function tc__clear_table(){ + if( rand( 0, 1000 ) < $this->chance_to_clean ){ + $interval_start = SpbcHelper::time__get_interval_start( $this->tc_period ); + $this->db->query( + 'DELETE + FROM ' . SPBC_TBL_TC_LOG . ' + WHERE interval_start < '. $interval_start .' + LIMIT 100000;' + ); + } + } + + public function waf__test(){ + + if($this->waf_enabled && !in_array($this->result, array('PASS_BY_TRUSTED_NETWORK', 'PASS_BY_WHITELIST'))){ + + // XSS + if($this->waf_xss_check){ + if($this->waf_xss_check($_POST) || $this->waf_xss_check($_GET) || $this->waf_xss_check($_COOKIE)){ + $this->result = 'DENY_BY_WAF_XSS'; + $this->blocked_ip = end($this->ip_array); + } + } + + // SQL-injection + if($this->waf_sql_check){ + if($this->waf_sql_check($_POST) || $this->waf_sql_check($_GET)){ + $this->result = 'DENY_BY_WAF_SQL'; + $this->blocked_ip = end($this->ip_array); + } + } + + // File + if($this->waf_file_check){ + if($this->waf_file_check()){ + $this->result = 'DENY_BY_WAF_FILE'; + $this->blocked_ip = end($this->ip_array); + } + } + + // Exploits + if($this->waf_exploit_check){ + if($this->waf_exploit_check()){ + $this->result = 'DENY_BY_WAF_EXPLOIT'; + $this->blocked_ip = end($this->ip_array); + } + } + } + } + + public function waf_xss_check($arr){ + foreach($arr as $name => $param){ + if(is_array($param)){ + $result = $this->waf_xss_check($param); + if($result === true) + return true; + continue; + } + foreach($this->waf_xss_patterns as $pattern){ + if(stripos($param, $pattern) !== false){ + $this->waf_pattern = array('critical' => $pattern); + return true; + } + } + // Test + if($name == 'spbct_test_waf' && $param == 'xss'){ + $this->waf_pattern = array('critical' => 'test'); + return true; + } + } + } + + public function waf_sql_check($arr){ + foreach($arr as $name => $param){ + if(is_array($param)){ + $result = $this->waf_sql_check($param); + if($result === true) + return true; + continue; + } + foreach($this->waf_sql_patterns as $pattern){ + if(preg_match('/'.$pattern.'/i', $param) === 1){ + $this->waf_pattern = array('critical' => $pattern); + return true; + } + } + // Test + if($name == 'spbct_test_waf' && $param == 'sql'){ + $this->waf_pattern = array('critical' => 'test'); + return true; + } + } + } + + public function waf_exploit_check(){ + $query = filter_input(INPUT_SERVER, 'QUERY_STRING'); + if(!empty($query)){ + foreach($this->waf_exploit_patterns as $pattern){ + if(preg_match('/'.$pattern.'/i', $query) === 1){ + $this->waf_pattern = array('critical' => $pattern); + return true; + } + } + // Test + if(strpos($query, 'spbct_test_waf=exploit') !== false){ + $this->waf_pattern = array('critical' => 'test'); + return true; + } + } + } + + /** + * Check uploaded files for malicious code + * + * @todo Mime tipe detection from file content + * @return boolean Does the file contain malicious code + */ + public function waf_file_check(){ + if(!empty($_FILES)){ + foreach($_FILES as $filez){ + if ((empty($filez['errror']) || $filez['errror'] == UPLOAD_ERR_OK)) { + $filez['tmp_name'] = is_array($filez['tmp_name']) ? $filez['tmp_name'] : array($filez['tmp_name']); + foreach($filez['tmp_name'] as $file){ + if(is_string($file) && is_uploaded_file($file) && is_readable($file) && (function_exists('mime_content_type') && in_array(mime_content_type($file), $this->waf_file_mime_check))){ + $fileh = new SpbcScannerH(null, array('content' => file_get_contents($file))); + if(empty($fileh->error)){ + $fileh->process_file(); + if(!empty($fileh->verdict)){ + foreach($fileh->verdict as $severity => $result){ + $this->waf_pattern[$severity] = reset($result); + } + return true; + } + } + } + } + } + } + } + } + + // AJAX callback for detailes about latest blocked file + public static function waf_file__get_last_blocked_info() + { + check_ajax_referer('spbc_secret_nonce', 'security'); + + global $wpdb; + + $timestamp = intval(Post::get( 'timestamp' )); + + // Select only latest ones. + $result = $wpdb->get_results( + 'SELECT *' + .' FROM '. SPBC_TBL_FIREWALL_LOG + .' WHERE status = "DENY_BY_WAF_FILE" AND entry_timestamp > '.($timestamp - 2) + .' ORDER BY entry_timestamp DESC LIMIT 1;' + , OBJECT + ); + + if($result){ + $result = $result[0]; + $out = array( + 'blocked' => true, + 'warning' => __('Security by CleanTalk: File was blocked by Web Application FireWall.', 'security-malware-firewall'), + 'pattern_title' => __('Detected pattern: ', 'security-malware-firewall'), + 'pattern' => json_decode($result->pattern, true), + ); + }else + $out = array('blocked' => false); + + die(json_encode($out)); + } + + public function _die($service_id, $reason = '', $additional_reason = ''){ + + // Adding block reason + switch($reason){ + case 'DENY': $reason = __('Blacklisted', 'security-malware-firewall'); break; + case 'DENY_BY_NETWORK': $reason = __('Hazardous network', 'security-malware-firewall'); break; + case 'DENY_BY_DOS': $reason = __('Blocked by DoS prevention system', 'security-malware-firewall'); break; + case 'DENY_BY_WAF_XSS': $reason = __('Blocked by Web Application Firewall: XSS atatck detected.', 'security-malware-firewall'); break; + case 'DENY_BY_WAF_SQL': $reason = __('Blocked by Web Application Firewall: SQL-injection detected.', 'security-malware-firewall'); break; + case 'DENY_BY_WAF_EXPLOIT': $reason = __('Blocked by Web Application Firewall: Exploit detected.', 'security-malware-firewall'); break; + case 'DENY_BY_WAF_FILE': $reason = __('Blocked by Web Application Firewall: Malicious files upload.', 'security-malware-firewall'); break; + } + + $spbc_die_page = file_get_contents(SPBC_PLUGIN_DIR . 'inc/spbc_die_page.html'); + + $spbc_die_page = str_replace( "{TITLE}", __('Blocked: Security by CleanTalk', 'security-malware-firewall'), $spbc_die_page ); + $spbc_die_page = str_replace( "{REMOTE_ADDRESS}", $this->blocked_ip, $spbc_die_page ); + $spbc_die_page = str_replace( "{SERVICE_ID}", $service_id, $spbc_die_page ); + $spbc_die_page = str_replace( "{HOST}", $_SERVER['HTTP_HOST'], $spbc_die_page ); + $spbc_die_page = str_replace( "{TEST_TITLE}", (!empty($_GET['spbct_test']) ? __('This is the testing page for Security FireWall', 'security-malware-firewall') : ''), $spbc_die_page ); + $spbc_die_page = str_replace( "{REASON}", $reason, $spbc_die_page ); + $spbc_die_page = str_replace( "{GENERATED_TIMESTAMP}", time(), $spbc_die_page ); + $spbc_die_page = str_replace( "{FALSE_POSITIVE_WARNING}", __('Maybe you\'ve been blocked by a mistake. Please refresh the page (press CTRL + F5) or try again later.', 'security-malware-firewall'), $spbc_die_page ); + + if(headers_sent() === false){ + header('Expires: '.date(DATE_RFC822, mktime(0, 0, 0, 1, 1, 1971))); + header('Cache-Control: no-store, no-cache, must-revalidate'); + header('Cache-Control: post-check=0, pre-check=0', FALSE); + header('Pragma: no-cache'); + header("HTTP/1.0 403 Forbidden"); + $spbc_die_page = str_replace("{GENERATED}", "", $spbc_die_page); + }else{ + $spbc_die_page = str_replace("{GENERATED}", "

The page was generated at ".date("D, d M Y H:i:s")."

",$spbc_die_page); + } + wp_die( $spbc_die_page, "Blacklisted", Array('response'=>403) ); + } + + static public function firewall_update($spbc_key, $file_url = null, $immediate = false){ + + global $wpdb; + + // Getting remote file name + if(!$file_url){ + + $result = API::method__security_firewall_data_file($spbc_key); + + if(empty($result['error'])){ + + if( !empty($result['file_url']) ){ + + $file_url = $result['file_url']; + + if(!$immediate){ + + sleep(5); // Wait till file will be created + + // Asynchronously call + return SpbcHelper::http__request( + get_option('siteurl'), + array( + 'spbc_remote_call_token' => md5($spbc_key), + 'spbc_remote_call_action' => 'update_security_firewall', + 'plugin_name' => 'spbc', + 'file_url' => $result['file_url'], + ), + array('get', 'async') + ); + } + + }else + return array('error' => 'BAD_RESPONSE'); + }else + return $result; + } + + // Check for remote file + if($file_url){ + + if( SpbcHelper::http__request__get_response_code($file_url) === 200){ // Check if it's there + + $gz_data = SpbcHelper::http__request__get_content($file_url); + + if(SpbcHelper::get_mime_type($gz_data)){ + + if(function_exists('gzdecode')) { + + $data = gzdecode( $gz_data ); + + if($data !== false){ + + $lines = SpbcHelper::buffer__parse__csv($data); + + }else + return array('error' => 'COULDNT_UNPACK'); + }else + return array('error' => 'Wrong mime type'); + }else + return array('error' => 'Function gzdecode not exists. Please update your PHP to version 5.4'); + + $wpdb->query('DELETE FROM `'. SPBC_TBL_FIREWALL_DATA .'`;'); + + for( $count_result = 0; current($lines) !== false; ) { + + $query = "INSERT INTO `" . SPBC_TBL_FIREWALL_DATA . "` VALUES "; + + for ( $i=0; SPBC_WRITE_LIMIT !== $i && current($lines) !== false; $i++, $count_result++, next($lines) ) { + + $entry = current($lines); + + if ( empty( $entry ) ) { + continue; + } + if ( SPBC_WRITE_LIMIT !== $i ) { + + $ip = $entry[0]; + $mask = $entry[1]; + // $comment = $entry[2]; // Comment from user + $status = isset( $entry[3] ) ? $entry[3] : 0; + $is_personal = isset( $entry[4] ) ? intval( $entry[4] ) : 0; + + // IPv4 + if ( is_numeric( $ip ) ) { + $mask = sprintf( '%u', ip2long( long2ip( - 1 << ( 32 - (int) $mask ) ) ) ); + $query .= "(0, 0, 0, $ip, 0, 0, 0, $mask, $status, 0, $is_personal),"; + // IPv6 + } else { + $ip = substr( $ip, 1, - 1 ); // Cut "" + $ip = SpbcHelper::ip__v6_normalize( $ip ); // Normalize + $ip = explode( ':', $ip ); + + $ip_1 = hexdec( $ip[0] . $ip[1] ); + $ip_2 = hexdec( $ip[2] . $ip[3] ); + $ip_3 = hexdec( $ip[4] . $ip[5] ); + $ip_4 = hexdec( $ip[6] . $ip[7] ); + + $ip_1 = $ip_1 ? $ip_1 : 0; + $ip_2 = $ip_2 ? $ip_2 : 0; + $ip_3 = $ip_3 ? $ip_3 : 0; + $ip_4 = $ip_4 ? $ip_4 : 0; + + for ( $k = 1; $k < 5; $k ++ ) { + $curr = 'mask_' . $k; + $curr = pow( 2, 32 ) - pow( 2, 32 - ( $mask - 32 >= 0 ? 32 : $mask ) ); + $mask = ( $mask - 32 <= 0 ? 0 : $mask - 32 ); + } + $query .= "($ip_1, $ip_2, $ip_3, $ip_4, $mask_1, $mask_2, $mask_3, $mask_4, $status, 1, $is_personal),"; + } + } + + }; + + //Exclusion for servers IP (SERVER_ADDR) + if ( ! empty( $_SERVER['HTTP_HOST'] ) ) { + $exclusions[] = SpbcHelper::dns__resolve( $_SERVER['HTTP_HOST'] ); + $exclusions[] = '127.0.0.1'; + foreach ( $exclusions as $exclusion ) { + if ( SpbcHelper::ip__validate( $exclusion ) && sprintf( '%u', ip2long( $exclusion ) ) ) { + $query .= '(0, 0, 0, ' . sprintf( '%u', ip2long( $exclusion ) ) . ', 0, 0, 0, ' . sprintf( '%u', 4294967295 << ( 32 - 32 ) ) . ', 2, 0, 0),'; + $query .= '(0, 0, 0, ' . sprintf( '%u', ip2long( $exclusion ) ) . ', 0, 0, 0, ' . sprintf( '%u', 4294967295 << ( 32 - 32 ) ) . ', 2, 0, 0),'; + } + } + } + + $wpdb->query( substr( $query, 0, - 1 ) . ';' ); + + } + + return $count_result; + + }else + return array('error' => 'NO_REMOTE_FILE_FOUND'); + } + } + + //Add entries to SFW log + public function update_logs($ip, $status, $pattern = array()) + { + if(empty($ip) || empty($status)) + return; + + // Parameters + $time = time(); + $page_url = addslashes(Server::get('HTTPS') != 'off' ? 'https://' : 'http://') . Server::get('HTTP_HOST') . Server::get('REQUEST_URI'); + $page_url = substr($page_url, 0 , 4096); + $http_user_agent = Server::get('HTTP_USER_AGENT') + ? addslashes(htmlspecialchars(substr(Server::get('HTTP_USER_AGENT'), 0, 300))) + : 'unknown'; + $request_method = Server::get('REQUEST_METHOD'); + $x_forwarded_for = addslashes(htmlspecialchars(substr(Server::get('HTTP_X_FORWARDED_FOR'), 0 , 15))); + $id = md5($ip.$http_user_agent.$status); + $pattern = !empty($pattern) + ? json_encode($pattern) + : ''; + + $this->db->query( + "INSERT INTO ". SPBC_TBL_FIREWALL_LOG ." SET + entry_id = '$id', + ip_entry = '$ip', + entry_timestamp = $time, + status = '$status', + pattern = IF('$pattern' = '', NULL, '$pattern'), + requests = 1, + page_url = '$page_url', + http_user_agent = '$http_user_agent', + request_method = '$request_method', + x_forwarded_for = IF('$x_forwarded_for' = '', NULL, '$x_forwarded_for') + ON DUPLICATE KEY UPDATE + ip_entry = ip_entry, + entry_timestamp = $time, + status = '$status', + pattern = IF('$pattern' = '', NULL, '$pattern'), + requests = requests + 1, + page_url = '$page_url', + http_user_agent = http_user_agent, + request_method = '$request_method', + x_forwarded_for = IF('$x_forwarded_for' = '', NULL, '$x_forwarded_for')" + ); + } + + //*Send and wipe SFW log + public static function send_logs($spbc_key){ + + global $wpdb; + + //Getting logs + $result = $wpdb->get_results("SELECT * FROM `". SPBC_TBL_FIREWALL_LOG ."` LIMIT ".SPBC_SELECT_LIMIT, ARRAY_A); + + if(count($result)){ + //Compile logs + $data = array(); + + foreach($result as $key => $value){ + + //Compile log + $to_data = array( + 'datetime' => date('Y-m-d H:i:s', $value['entry_timestamp']), + 'page_url' => $value['page_url'], + 'visitor_ip' => SpbcHelper::ip__validate($value['ip_entry']) == 'v4' ? (int)sprintf('%u', ip2long($value['ip_entry'])) : (string)$value['ip_entry'], + 'http_user_agent' => $value['http_user_agent'], + 'request_method' => $value['request_method'], + 'x_forwarded_for' => $value['x_forwarded_for'], + 'hits' => (int)$value['requests'], + ); + + // Legacy + switch($value['status']){ + case 'PASS_BY_TRUSTED_NETWORK': $to_data['status_efw'] = 3; break; + case 'PASS_BY_WHITELIST': $to_data['status_efw'] = 2; break; + case 'PASS': $to_data['status_efw'] = 1; break; + case 'DENY': $to_data['status_efw'] = 0; break; + case 'DENY_BY_NETWORK': $to_data['status_efw'] = -1; break; + case 'DENY_BY_DOS': $to_data['status_efw'] = -2; break; + case 'DENY_BY_WAF_XSS': $to_data['status_efw'] = -3; $to_data['waf_attack_type'] = 'XSS'; $to_data['waf_comment'] = $value['pattern']; break; + case 'DENY_BY_WAF_SQL': $to_data['status_efw'] = -4; $to_data['waf_attack_type'] = 'SQL_INJECTION'; $to_data['waf_comment'] = $value['pattern']; break; + case 'DENY_BY_WAF_FILE': $to_data['status_efw'] = -5; $to_data['waf_attack_type'] = 'MALWARE'; $to_data['waf_comment'] = $value['pattern']; break; + case 'DENY_BY_WAF_EXPLOIT': $to_data['status_efw'] = -6; $to_data['waf_attack_type'] = 'EXPLOIT'; $to_data['waf_comment'] = $value['pattern']; break; + } + + switch($value['status']){ + case 'PASS_BY_TRUSTED_NETWORK': $to_data['status'] = 3; break; + case 'PASS_BY_WHITELIST': $to_data['status'] = 2; break; + case 'PASS': $to_data['status'] = 1; break; + case 'DENY': $to_data['status'] = 0; break; + case 'DENY_BY_NETWORK': $to_data['status'] = -1; break; + case 'DENY_BY_DOS': $to_data['status'] = -2; break; + case 'DENY_BY_WAF_XSS': $to_data['status'] = -3; $to_data['waf_attack_type'] = 'XSS'; $to_data['waf_comment'] = $value['pattern']; break; + case 'DENY_BY_WAF_SQL': $to_data['status'] = -4; $to_data['waf_attack_type'] = 'SQL_INJECTION'; $to_data['waf_comment'] = $value['pattern']; break; + case 'DENY_BY_WAF_FILE': $to_data['status'] = -5; $to_data['waf_attack_type'] = 'MALWARE'; $to_data['waf_comment'] = $value['pattern']; break; + case 'DENY_BY_WAF_EXPLOIT': $to_data['status'] = -6; $to_data['waf_attack_type'] = 'EXPLOIT'; $to_data['waf_comment'] = $value['pattern']; break; + } + + $data[] = $to_data; + + } unset($key, $value, $result, $to_data); + + // Sendings request + $result = API::method__security_logs__sendFWData($spbc_key, $data); + + // Checking answer and deleting all lines from the table + if(empty($result['error'])){ + if($result['rows'] == count($data)){ + $wpdb->query("DELETE FROM `". SPBC_TBL_FIREWALL_LOG ."`"); + return count($data); + } + }else{ + return $result; + } + }else{ + return array( + 'error' => 'NO_LOGS_TO_SEND' + ); + } + } +} diff --git a/cleantalk/lib/CleantalkAP/SMF/Helper.php b/cleantalk/lib/CleantalkAP/SMF/Helper.php new file mode 100644 index 0000000..5de7882 --- /dev/null +++ b/cleantalk/lib/CleantalkAP/SMF/Helper.php @@ -0,0 +1,82 @@ +_real_escape($param) . $quotes; + break; + } + return $param; + } + + public static function time__get_interval_start( $interval = 300 ){ + return time() - ( ( time() - strtotime( date( 'd F Y' ) ) ) % $interval ); + } +} \ No newline at end of file diff --git a/cleantalk/lib/CleantalkAP/SMF/RemoteCalls.php b/cleantalk/lib/CleantalkAP/SMF/RemoteCalls.php new file mode 100644 index 0000000..13a9a76 --- /dev/null +++ b/cleantalk/lib/CleantalkAP/SMF/RemoteCalls.php @@ -0,0 +1,221 @@ +remote_calls)){ + + $cooldown = isset($spbc->remote_calls[$action]['cooldown']) ? $spbc->remote_calls[$action]['cooldown'] : self::COOLDOWN; +// $pass_cooldown = SpbcHelper::ip__get(array('real')) === filter_input(INPUT_SERVER, 'SERVER_ADDR'); +// $pass_cooldown = false; // Temp crutch + + if(time() - $spbc->remote_calls[$action]['last_call'] > $cooldown +// || $pass_cooldown + ){ + + $spbc->remote_calls[$action]['last_call'] = time(); + $spbc->save('remote_calls'); + + // Check API key + if($token == strtolower(md5($spbc->settings['spbc_key'])) ){ + + $action = 'action__'.$action; + + if(method_exists('SpbcRemoteCalls', $action)){ + + if(get_option('spbc_deactivation_in_process') === false){ // Continue if plugin is active + + if(!empty($_GET['delay'])) sleep($_GET['delay']); // Delay before perform action; + $out = static::$action(); + + // Stop execution if plguin is deactivated + }else{ + delete_option('spbc_deactivation_in_process'); + $out = 'FAIL '.json_encode(array('error' => 'PLUGIN_DEACTIVATION_IN_PROCESS')); + } + }else + $out = 'FAIL '.json_encode(array('error' => 'UNKNOWN_ACTION_METHOD')); + }else + $out = 'FAIL '.json_encode(array('error' => 'WRONG_TOKEN')); + }else + $out = 'FAIL '.json_encode(array('error' => 'TOO_MANY_ATTEMPTS')); + }else + $out = 'FAIL '.json_encode(array('error' => 'UNKNOWN_ACTION')); + + die($out); + } + + /** + * + */ + static function action__close_renew_banner() { + global $spbc; + $spbc->data['notice_show'] = 0; + $spbc->save('data'); + // Updating cron task + Cron::updateTask('access_key_notices', 'spbc_access_key_notices', 86400); + die('OK'); + } + + static function action__update_plugin() { + add_action('template_redirect', 'spbc_update', 1); + } + + static function action__update_security_firewall() { + $result = spbc_security_firewall_update(true); + die(empty($result['error']) ? 'OK' : 'FAIL '.json_encode(array('error' => $result['error']))); + } + + static function action__drop_security_firewall() { + $result = spbc_security_firewall_drop(); + die(empty($result['error']) ? 'OK' : 'FAIL '.json_encode(array('error' => $result['error']))); + } + + static function action__download__quarantine_file() { + $result = spbc_scanner_file_download(true, $_GET['file_id']); + if(empty($result['error'])){ + header('Content-Type: application/octet-stream'); + header('Content-Disposition: attachment; filename='.$result['file_name']); + } + die(empty($result['error']) + ? $result['file_content'] + : 'FAIL '.json_encode(array('error' => $result['error']))); + } + + static function action__update_settings() { + global $spbc; + $source = $_GET; + foreach($spbc->def_settings as $setting => $value){ + if(array_key_exists($setting, $source)){ + $var = $source[$setting]; + $type = gettype($spbc->settings[$setting]); + settype($var, $type); + if($type == 'string') + $var = preg_replace(array('/=/', '/`/'), '', $var); + $spbc->settings[$setting] = $var; + } + } + $spbc->save('settings'); + die('OK'); + } + + static function action__backup_signatures_files() { + $result = spbc_backup__files_with_signatures(); + die(empty($result['error']) + ? 'OK' + : 'FAIL '.json_encode(array('error' => $result['error']))); + } + + static function action__rollback_repair() { + $result = spbc_rollback($_GET['backup_id']); + die(empty($result['error']) + ? 'OK' + : 'FAIL '.json_encode(array('error' => $result['error']))); + } + + static function action__scanner_clear_hashes() { + $result = true; + switch($_GET['type']){ + case 'plugins': delete_option(SPBC_PLUGINS); break; + case 'themes': delete_option(SPBC_THEMES); break; + case 'plugins_and_themes': delete_option(SPBC_THEMES); delete_option(SPBC_PLUGINS); break; + case 'all': $result = spbc_scanner_clear(); break; + default: $result = spbc_scanner_clear(); break; + } + die(empty($result['error']) + ? 'OK' + : 'FAIL '.json_encode(array('error' => 'COULDNT_CLEAR_ALL_DB_ERROR'))); + } + + static function action__scanner_signatures_update() { + $result = spbc_scanner__signatures_update(); + die(empty($result['error']) + ? 'OK' . ' ' . (!empty($result['success']) ? $result['success'] : '') + : 'FAIL '.json_encode(array('error' => $result['error']))); + } + + static function action__scanner__controller() { + return spbc_scanner__controller(); + } + + static function action__scanner__get_remote_hashes() { + spbc_scanner_get_remote_hashes(); + } + + static function action__scanner__count_hashes_plug() { + spbc_scanner_count_hashes_plug(); + } + + static function action__scanner__get_remote_hashes__plug() { + spbc_scanner_get_remote_hashes__plug(); + } + + static function action__scanner__clear_table() { + spbc_scanner_clear_table(); + } + + static function action__scanner__count_files() { + spbc_scanner_count_files(); + } + + static function action__scanner__scan() { + spbc_scanner_scan(); + } + + static function action__scanner__count_files__by_status() { + spbc_scanner_count_files__by_status(); + } + + static function action__scanner__scan_heuristic() { + spbc_scanner_scan_signatures(); + } + + static function action__scanner__scan_signatures() { + spbc_scanner_scan_signatures(); + } + + static function action__scanner__backup_sigantures() { + spbc_backup__files_with_signatures(); + } + + static function action__scanner__count_cure() { + spbc_scanner_count_cure(); + } + + static function action__scanner__cure() { + spbc_scanner_cure(); + } + + static function action__scanner__links_count() { + spbc_scanner_links_count(); + } + + static function action__scanner__links_scan() { + spbc_scanner_links_scan(); + } + + static function action__scanner__frontend_scan() { + spbc_scanner_frontend__scan(); + } + + static function action_scanner__send_results() { + spbc_scanner_send_results(); + } +} diff --git a/cleantalk/lib/CleantalkAP/SMF/State.php b/cleantalk/lib/CleantalkAP/SMF/State.php new file mode 100644 index 0000000..539cfaf --- /dev/null +++ b/cleantalk/lib/CleantalkAP/SMF/State.php @@ -0,0 +1,344 @@ + 1.0, + + 'account' => array( + // Notices + 'show_notice' => 0, + 'show_review' => 0, + 'renew' => 0, + 'trial' => 0, + 'update' => 0, + // Account params + 'moderate_ip' => 0, + 'moderate' => 0, + 'valid' => 0, + 'ip_license' => 0, + 'service_id' => 0, + 'license_trial' => 0, + // Display stuff + 'spam_count' => 0, + 'account_name_ob' => '', + // Misc + 'user_token' => '', + ), + + 'stats' => array( + 'fw_log_sent_time', + 'fw_log_sent_amount', + 'fw_entries', + 'fw_updated_time', + 'fw_networks_amount', + 'events_log_sent_time', + 'events_log_sent_amount', + 'events_entries', + 'fw_logs_last_sent', + 'fw_logs_last_sent', + 'php_log_sent_amount' => 0, + 'php_log_sent_time' => 0, + ), + + 'status' => array( + 'key_is_ok' => false, + ), + + 'salt' => '', + + ); + public $def_network_settings = array( + 'allow_custom_key' => false, + 'allow_cleantalk_cp' => false, + 'key_is_ok' => false, + 'spbc_key' => '', + 'user_token' => '', + 'service_id' => '', + 'moderate' => 0 + ); + + public $def_remote_calls = array( + + // Common + 'close_renew_banner' => array('last_call' => 0,), + 'update_plugin' => array('last_call' => 0,), + 'update_security_firewall' => array('last_call' => 0, 'cooldown' => 3), + 'drop_security_firewall' => array('last_call' => 0,), + 'update_settings' => array('last_call' => 0,), + + // Inner + 'download__quarantine_file' => array('last_call' => 0, 'cooldown' => 3), + + // Backups + 'backup_signatures_files' => array('last_call' => 0,), + 'rollback_repair' => array('last_call' => 0,), + + // Scanner + 'scanner_signatures_update' => array('last_call' => 0,), + 'scanner_clear_hashes' => array('last_call' => 0,), + + 'scanner__controller' => array('last_call' => 0, 'cooldown' => 3), + 'scanner__get_remote_hashes' => array('last_call' => 0,), + 'scanner__count_hashes_plug' => array('last_call' => 0,), + 'scanner__get_remote_hashes__plug' => array('last_call' => 0,), + 'scanner__clear_table' => array('last_call' => 0,), + 'scanner__count_files' => array('last_call' => 0,), + 'scanner__scan' => array('last_call' => 0,), + 'scanner__count_files__by_status' => array('last_call' => 0,), + 'scanner__scan_heuristic' => array('last_call' => 0,), + 'scanner__scan_signatures' => array('last_call' => 0,), + 'scanner__count_cure' => array('last_call' => 0,), + 'scanner__cure' => array('last_call' => 0,), + 'scanner__links_count' => array('last_call' => 0,), + 'scanner__links_scan' => array('last_call' => 0,), + 'scanner__frontend_scan' => array('last_call' => 0,), + ); + + public $def_errors = array(); + + public function __construct($option_prefix, $options = array('settings'), $wpms = false) + { + $this->option_prefix = $option_prefix; + + if($wpms){ + $option = get_site_option($this->option_prefix.'_network_settings'); + $option = is_array($option) ? $option : $this->def_network_settings; + $this->network_settings = new \ArrayObject($option); + } + + foreach($options as $option_name){ + + $option = get_option($this->option_prefix.'_'.$option_name); + + // Default options + if($this->option_prefix.'_'.$option_name === 'spbc_settings'){ + $option = is_array($option) ? array_merge($this->default_settings, $option) : $this->default_settings; + if(!is_main_site()) $option['backend_logs_enable'] = 0; + } + + // Default data + if($this->option_prefix.'_'.$option_name === 'spbc_data'){ + $option = is_array($option) ? array_merge($this->default_data, $option) : $this->default_data; + if(empty($option['salt'])) $option['salt'] = str_pad(rand(0, getrandmax()), 6, '0').str_pad(rand(0, getrandmax()), 6, '0'); + if(empty($option['last_php_log_sent'])) $option['last_php_log_sent'] = time(); + } + + // Default errors + if($this->option_prefix.'_'.$option_name === 'spbc_errors'){ + $option = is_array($option) ? array_merge($this->def_errors, $option) : $this->def_errors; + } + + // Default remote calls + if($this->option_prefix.'_'.$option_name === 'spbc_remote_calls'){ + $option = is_array($option) ? array_merge($this->def_remote_calls, $option) : $this->def_remote_calls; + } + + $this->$option_name = is_array($option) ? new \ArrayObject($option) : $option; + + } + } + + private function getOption($option_name) + { + $option = get_option('spbc_'.$option_name); + $this->$option_name = gettype($option) === 'array' + ? new \ArrayObject($option) + : $option; + } + + /** + * @param string $option_name + * @param bool $use_perfix + * @param bool $autoload + */ + public function save($option_name, $use_perfix = true, $autoload = true) + { + $option_name_to_save = $use_perfix ? $this->option_prefix.'_'.$option_name : $option_name; + $arr = array(); + foreach($this->$option_name as $key => $value){ + $arr[$key] = $value; + } + update_option($option_name_to_save, $arr, $autoload); + } + + public function saveSettings() + { + update_option($this->option_prefix.'_settins', $this->settings); + } + + public function saveData() + { + update_option($this->option_prefix.'_data', $this->data); + } + + public function saveNetworkSettings() + { + update_site_option($this->option_prefix.'_network_settings', $this->network_settings); + } + + public function deleteOption($option_name, $use_prefix = false) + { + if($this->__isset($option_name)){ + $this->__unset($option_name); + delete_option( ($use_prefix ? $this->option_prefix.'_' : '') . $option_name); + } + } + + /** + * Prepares an adds an error to the plugin's data + * + * @param string type + * @param mixed array || string + * @returns null + */ + public function error_add($type, $error, $major_type = null, $set_time = true) + { + $error = is_array($error) + ? $error['error'] + : $error; + + // Exceptions + if( ($type == 'send_logs' && $error == 'NO_LOGS_TO_SEND') || + ($type == 'send_firewall_logs' && $error == 'NO_LOGS_TO_SEND') || + $error == 'LOG_FILE_NOT_EXISTS' + ) + return; + + $error = array( + 'error' => $error, + 'error_time' => $set_time ? current_time('timestamp') : null, + ); + + if(!empty($major_type)){ + $this->errors[$major_type][$type] = $error; + }else{ + $this->errors[$type] = $error; + } + + $this->save('errors'); + } + + /** + * Deletes an error from the plugin's data + * + * @param string $type + * @param bool $save_flag + * @param string $major_type + * + * @return void + */ + public function error_delete($type, $save_flag = false, $major_type = null) + { + if(is_string($type)) + $type = explode(' ', $type); + + foreach($type as $val){ + if($major_type){ + if(isset($this->errors[$major_type][$val])) + unset($this->errors[$major_type][$val]); + }else{ + if(isset($this->errors[$val])) + unset($this->errors[$val]); + } + } + + // Save if flag is set and there are changes + if($save_flag) + $this->save('errors'); + } + + /** + * Deletes all errors from the plugin's data + * + * @param bool $save_flag + * + * @return void + */ + public function error_delete_all($save_flag = false) + { + $this->errors = new \ArrayObject($this->def_errors); + if($save_flag) + $this->save('errors'); + } + + public function error_toggle($add_flag = true, $type, $save_flag = false, $major_type = null){ + if($add_flag) + $this->error_add($type, $save_flag, $major_type); + else + $this->error_delete($type, $save_flag, $major_type); + } + + public function __set($name, $value) + { + $this->storage[$name] = $value; + } + + public function __get($name) + { + if (array_key_exists($name, $this->storage)){ + return $this->storage[$name]; + }else{ + $this->getOption($name); + return $this->storage[$name]; + } + + // return !empty($this->storage[$name]) ? $this->storage[$name] : null; + } + + public function __isset($name) + { + return isset($this->storage[$name]); + } + + public function __unset($name) + { + unset($this->storage[$name]); + } + + public function __call($name, $arguments) + { + error_log ("Calling method '$name' with arguments: " . implode(', ', $arguments). "\n"); + } + + public static function __callStatic($name, $arguments) + { + error_log("Calling static method '$name' with arguments: " . implode(', ', $arguments). "\n"); + } + + public function server(){ + return \CleantalkAP\Variables\Server::getInstance(); + } + public function cookie(){ + return \CleantalkAP\Variables\Cookie::getInstance(); + } + public function request(){ + return \CleantalkAP\Variables\Request::getInstance(); + } + public function post(){ + return \CleantalkAP\Variables\Post::getInstance(); + } + public function get(){ + return \CleantalkAP\Variables\Get::getInstance(); + } +} diff --git a/cleantalk/lib/CleantalkAP/Templates/Singleton.php b/cleantalk/lib/CleantalkAP/Templates/Singleton.php new file mode 100644 index 0000000..6020441 --- /dev/null +++ b/cleantalk/lib/CleantalkAP/Templates/Singleton.php @@ -0,0 +1,32 @@ +init(); + } + return static::$instance; + } + + /** + * Alternative constructor + */ + protected function init(){ + + } + +} \ No newline at end of file diff --git a/cleantalk/lib/CleantalkAP/Variables/Cookie.php b/cleantalk/lib/CleantalkAP/Variables/Cookie.php new file mode 100644 index 0000000..c96c74f --- /dev/null +++ b/cleantalk/lib/CleantalkAP/Variables/Cookie.php @@ -0,0 +1,37 @@ +variables + if(isset(static::$instance->variables[$name])) + return static::$instance->variables[$name]; + + if( function_exists( 'filter_input' ) ) + $value = filter_input( INPUT_COOKIE, $name ); + + if( empty( $value ) ) + $value = isset( $_COOKIE[ $name ] ) ? $_COOKIE[ $name ] : ''; + + return $value; + } +} \ No newline at end of file diff --git a/cleantalk/lib/CleantalkAP/Variables/Get.php b/cleantalk/lib/CleantalkAP/Variables/Get.php new file mode 100644 index 0000000..01005ee --- /dev/null +++ b/cleantalk/lib/CleantalkAP/Variables/Get.php @@ -0,0 +1,40 @@ +variables + if(isset(static::$instance->variables[$name])) + return static::$instance->variable[$name]; + + if( function_exists( 'filter_input' ) ) + $value = filter_input( INPUT_GET, $name ); + + if( empty( $value ) ) + $value = isset( $_GET[ $name ] ) ? $_GET[ $name ] : ''; + + // Remember for thurther calls + static::getInstance()->remember_variable( $name, $value ); + + return $value; + } +} \ No newline at end of file diff --git a/cleantalk/lib/CleantalkAP/Variables/Post.php b/cleantalk/lib/CleantalkAP/Variables/Post.php new file mode 100644 index 0000000..130105a --- /dev/null +++ b/cleantalk/lib/CleantalkAP/Variables/Post.php @@ -0,0 +1,40 @@ +variables + if(isset(static::$instance->variables[$name])) + return static::$instance->variables[$name]; + + if( function_exists( 'filter_input' ) ) + $value = filter_input( INPUT_POST, $name ); + + if( empty( $value ) ) + $value = isset( $_POST[ $name ] ) ? $_POST[ $name ] : ''; + + // Remember for thurther calls + static::getInstance()->remember_variable( $name, $value ); + + return $value; + } +} \ No newline at end of file diff --git a/cleantalk/lib/CleantalkAP/Variables/Request.php b/cleantalk/lib/CleantalkAP/Variables/Request.php new file mode 100644 index 0000000..1bcaa83 --- /dev/null +++ b/cleantalk/lib/CleantalkAP/Variables/Request.php @@ -0,0 +1,36 @@ +variables + if(isset(static::$instance->variables[$name])) + return static::$instance->variables[$name]; + + $value = isset( $_REQUEST[ $name ] ) ? $_REQUEST[ $name ] : ''; + + // Remember for thurther calls + static::getInstance()->remember_variable( $name, $value ); + + return $value; + } +} \ No newline at end of file diff --git a/cleantalk/lib/CleantalkAP/Variables/Server.php b/cleantalk/lib/CleantalkAP/Variables/Server.php new file mode 100644 index 0000000..03c56ea --- /dev/null +++ b/cleantalk/lib/CleantalkAP/Variables/Server.php @@ -0,0 +1,84 @@ +server + if(isset(static::$instance->variable[$name])) + return static::$instance->variable[$name]; + + $name = strtoupper( $name ); + + if( function_exists( 'filter_input' ) ) + $value = filter_input( INPUT_SERVER, $name ); + + if( empty( $value ) ) + $value = isset( $_SERVER[ $name ] ) ? $_SERVER[ $name ] : ''; + + // Convert to upper case for REQUEST_METHOD + if( in_array( $name, array( 'REQUEST_METHOD' ) ) ) + $value = strtoupper( $value ); + + // Convert HTML chars for HTTP_USER_AGENT, HTTP_USER_AGENT, SERVER_NAME + if( in_array( $name, array( 'HTTP_USER_AGENT', 'HTTP_USER_AGENT', 'SERVER_NAME' ) ) ) + $value = htmlspecialchars( $value ); + + // Remember for thurther calls + static::getInstance()->remember_variable( $name, $value ); + + return $value; + } + + /** + * Checks if $_SERVER['REQUEST_URI'] contains string + * + * @param string $string needle + * + * @return bool|int + */ + public static function in_uri( $string ){ + return self::has_string( 'REQUEST_URI', $string ); + } + + /** + * Checks if $_SERVER['REQUEST_URI'] contains string + * + * @param string $string needle + * + * @return bool|int + */ + public static function in_referer( $string ){ + return self::has_string( 'HTTP_REFERER', $string ); + } + + /** + * Checks if $_SERVER['REQUEST_URI'] contains string + * + * @param string $string needle + * + * @return bool|int + */ + public static function is_post(){ + return self::get( 'REQUEST_METHOD' ) === 'POST'; + } +} \ No newline at end of file diff --git a/cleantalk/lib/CleantalkAP/Variables/SuperGlobalVariables.php b/cleantalk/lib/CleantalkAP/Variables/SuperGlobalVariables.php new file mode 100644 index 0000000..5934adb --- /dev/null +++ b/cleantalk/lib/CleantalkAP/Variables/SuperGlobalVariables.php @@ -0,0 +1,81 @@ +get_variable( $name ); + } + + /** + * BLUEPRINT + * Gets given ${_SOMETHING} variable and seva it to memory + * @param $name + * + * @return mixed|string + */ + protected function get_variable( $name ){ + return true; + } + + /** + * Save variable to $this->variables[] + * + * @param string $name + * @param string $value + */ + protected function remember_variable( $name, $value ){ + static::$instance->variables[$name] = $value; + } + + /** + * Checks if variable contains given string + * + * @param string $var Haystack to search in + * @param string $string Needle to search + * + * @return bool|int + */ + static function has_string( $var, $string ){ + return stripos( self::get( $var ), $string ) !== false; + } + + /** + * Checks if variable equal to $param + * + * @param string $var Variable to compare + * @param string $param Param to compare + * + * @return bool|int + */ + static function equal( $var, $param ){ + return self::get( $var ) == $param; + } +} \ No newline at end of file diff --git a/lib/CleantalkHelper.php b/cleantalk/lib/CleantalkHelper.php similarity index 100% rename from lib/CleantalkHelper.php rename to cleantalk/lib/CleantalkHelper.php diff --git a/lib/CleantalkRequest.php b/cleantalk/lib/CleantalkRequest.php similarity index 100% rename from lib/CleantalkRequest.php rename to cleantalk/lib/CleantalkRequest.php diff --git a/lib/CleantalkResponse.php b/cleantalk/lib/CleantalkResponse.php similarity index 100% rename from lib/CleantalkResponse.php rename to cleantalk/lib/CleantalkResponse.php diff --git a/lib/CleantalkSFW.php b/cleantalk/lib/CleantalkSFW.php similarity index 100% rename from lib/CleantalkSFW.php rename to cleantalk/lib/CleantalkSFW.php diff --git a/cleantalk/lib/autoloader.php b/cleantalk/lib/autoloader.php new file mode 100644 index 0000000..7999aa0 --- /dev/null +++ b/cleantalk/lib/autoloader.php @@ -0,0 +1,23 @@ + $val){ + if(preg_match('/\AHTTP_/', $key)){ + $server_key = preg_replace('/\AHTTP_/', '', $key); + $key_parts = explode('_', $server_key); + if(count($key_parts) > 0 and strlen($server_key) > 2){ + foreach($key_parts as $part_index => $part){ + $key_parts[$part_index] = function_exists('mb_strtolower') ? mb_strtolower($part) : strtolower($part); + $key_parts[$part_index][0] = strtoupper($key_parts[$part_index][0]); + } + $server_key = implode('-', $key_parts); + } + $headers[$server_key] = $val; + } + } + return $headers; + } +} + +/* + * Patch for locale_get_display_region() + * For old PHP versions + */ +if( !function_exists('locale_get_display_region') ){ + function locale_get_display_region($locale, $in_locale = 'EN'){ + + return 'Unkonwn' . ($locale ? ': ' . $locale : ''); + } +} + +/* + * Patch for utf8_decode() + * If PHP complied without XML support + * From getID3() by James Heinrich under GNU GPL + */ +if(!function_exists('utf8_decode')){ + function utf8_decode($string){ + + $newcharstring = ''; + $offset = 0; + $stringlength = strlen($string); + while ($offset < $stringlength) { + if ((ord($string[$offset]) | 0x07) == 0xF7) { + $charval = ((ord($string[($offset + 0)]) & 0x07) << 18) & + ((ord($string[($offset + 1)]) & 0x3F) << 12) & + ((ord($string[($offset + 2)]) & 0x3F) << 6) & + (ord($string[($offset + 3)]) & 0x3F); + $offset += 4; + } elseif ((ord($string[$offset]) | 0x0F) == 0xEF) { + $charval = ((ord($string[($offset + 0)]) & 0x0F) << 12) & + ((ord($string[($offset + 1)]) & 0x3F) << 6) & + (ord($string[($offset + 2)]) & 0x3F); + $offset += 3; + } elseif ((ord($string[$offset]) | 0x1F) == 0xDF) { + $charval = ((ord($string[($offset + 0)]) & 0x1F) << 6) & + (ord($string[($offset + 1)]) & 0x3F); + $offset += 2; + } elseif ((ord($string[$offset]) | 0x7F) == 0x7F) { + $charval = ord($string[$offset]); + $offset += 1; + } else { + $charval = false; + $offset += 1; + } + if ($charval !== false) { + $newcharstring .= (($charval < 256) ? chr($charval) : '?'); + } + } + return $newcharstring; + } +} diff --git a/lib/sfw_die_page.html b/cleantalk/lib/sfw_die_page.html similarity index 100% rename from lib/sfw_die_page.html rename to cleantalk/lib/sfw_die_page.html diff --git a/install.php b/install.php index 3d3bc2d..36d1086 100644 --- a/install.php +++ b/install.php @@ -43,6 +43,7 @@ // Cleantalk's settings. Getting and set previous valuse, or set default valuse. updateSettings(array('cleantalk_api_key' => isset($modSettings['cleantalk_api_key']) ? $modSettings['cleantalk_api_key'] : ''), false); + updateSettings(array('cleantalk_check_registrations' => isset($modSettings['cleantalk_check_registrations']) ? $modSettings['cleantalk_check_registrations'] : '1'), false); updateSettings(array('cleantalk_first_post_checking' => isset($modSettings['cleantalk_first_post_checking']) ? $modSettings['cleantalk_first_post_checking'] : '1'), false); updateSettings(array('cleantalk_logging' => isset($modSettings['cleantalk_logging']) ? $modSettings['cleantalk_logging'] : '0'), false); updateSettings(array('cleantalk_tell_others' => isset($modSettings['cleantalk_tell_others']) ? $modSettings['cleantalk_tell_others'] : '1'), false); @@ -152,7 +153,7 @@ if (isset($modSettings['cleantalk_api_key']) && $modSettings['cleantalk_api_key'] != '' && isset($modSettings['cleantalk_sfw']) && $modSettings['cleantalk_sfw'] == 1) { if (!class_exists('CleantalkSFW')) - require_once(dirname(__FILE__) . '/lib/CleantalkSFW.php'); + require_once(dirname(__FILE__) . '/cleantalk/lib/CleantalkSFW.php'); $sfw = new CleantalkSFW; $sfw->sfw_update($modSettings['cleantalk_api_key']); unset($sfw); diff --git a/languages/english-utf8.xml b/languages/english-utf8.xml index c7a5f97..3984bf9 100644 --- a/languages/english-utf8.xml +++ b/languages/english-utf8.xml @@ -12,6 +12,7 @@ $txt['cleantalk_name'] = 'Antispam by CleanTalk'; $txt['cleantalk_settings'] = 'Antispam by CleanTalk Mod settings'; $txt['cleantalk_api_key'] = 'Access key'; +$txt['cleantalk_check_registrations'] = 'Enable spam test on registration'; $txt['cleantalk_get_access_manually'] = 'Get access key manually'; $txt['cleantalk_get_access_key_or'] = 'or'; $txt['cleantalk_get_access_automatically'] = 'Get access key automatically'; diff --git a/languages/english.xml b/languages/english.xml index fa0642c..ee73d10 100644 --- a/languages/english.xml +++ b/languages/english.xml @@ -12,6 +12,7 @@ $txt['cleantalk_name'] = 'Antispam by CleanTalk'; $txt['cleantalk_settings'] = 'Antispam by CleanTalk Mod settings'; $txt['cleantalk_api_key'] = 'Access key'; +$txt['cleantalk_check_registrations'] = 'Enable spam test on registration'; $txt['cleantalk_get_access_manually'] = 'Get access key manually'; $txt['cleantalk_get_access_key_or'] = 'or'; $txt['cleantalk_get_access_automatically'] = 'Get access key automatically'; diff --git a/languages/russian-utf8.xml b/languages/russian-utf8.xml index 7071559..e5b7618 100644 --- a/languages/russian-utf8.xml +++ b/languages/russian-utf8.xml @@ -12,6 +12,7 @@ $txt['cleantalk_name'] = 'Антиспам от CleanTalk'; $txt['cleantalk_settings'] = 'Антиспам CleanTalk Mod настройки'; $txt['cleantalk_api_key'] = 'Ключ доступа'; +$txt['cleantalk_check_registrations'] = 'Включить проверку регистраций на спам'; $txt['cleantalk_get_access_manually'] = 'Получить ключ'; $txt['cleantalk_get_access_key_or'] = 'или'; $txt['cleantalk_get_access_automatically'] = 'Получить ключ автоматически'; diff --git a/languages/russian.xml b/languages/russian.xml index 7f9069f..299eacf 100644 --- a/languages/russian.xml +++ b/languages/russian.xml @@ -12,6 +12,7 @@ $txt['cleantalk_name'] = ' CleanTalk'; $txt['cleantalk_settings'] = ' CleanTalk Mod '; $txt['cleantalk_api_key'] = ' '; +$txt['cleantalk_check_registrations'] = ' '; $txt['cleantalk_get_access_manually'] = ' '; $txt['cleantalk_get_access_key_or'] = ''; $txt['cleantalk_get_access_automatically'] = ' '; diff --git a/languages/spanish_es-utf8.xml b/languages/spanish_es-utf8.xml index 64df716..4ac7258 100644 --- a/languages/spanish_es-utf8.xml +++ b/languages/spanish_es-utf8.xml @@ -12,6 +12,7 @@ $txt['cleantalk_name'] = 'Antispam por CleanTalk'; $txt['cleantalk_settings'] = 'Ajustes del Mod Antispam por CleanTalk Mod'; $txt['cleantalk_api_key'] = 'Clave de Acceso'; +$txt['cleantalk_check_registrations'] = 'Activar verificación de registro de spam'; $txt['cleantalk_get_access_manually'] = 'Obtener la clave de acceso manualmente'; $txt['cleantalk_get_access_key_or'] = 'o'; $txt['cleantalk_get_access_automatically'] = 'Obtener a clave de acceso automáticamente'; diff --git a/languages/spanish_es.xml b/languages/spanish_es.xml index caad7ea..4e32ae1 100644 --- a/languages/spanish_es.xml +++ b/languages/spanish_es.xml @@ -12,62 +12,63 @@ $txt['cleantalk_name'] = 'Antispam por CleanTalk'; $txt['cleantalk_settings'] = 'Ajustes del Mod Antispam por CleanTalk Mod'; $txt['cleantalk_api_key'] = 'Clave de Acceso'; +$txt['cleantalk_check_registrations'] = 'Activar verificación de registro de spam'; $txt['cleantalk_get_access_manually'] = 'Obtener la clave de acceso manualmente'; $txt['cleantalk_get_access_key_or'] = 'o'; -$txt['cleantalk_get_access_automatically'] = 'Obtener a clave de acceso automticamente'; +$txt['cleantalk_get_access_automatically'] = 'Obtener a clave de acceso autom�ticamente'; $txt['cleantalk_license_agreement'] = 'Acuerdo de licencia'; -$txt['cleantalk_key_valid'] = 'La clave de acceso es vlida'; +$txt['cleantalk_key_valid'] = 'La clave de acceso es v�lida'; $txt['cleantalk_account_name_ob'] = 'Cuenta en cleantalk.org'; -$txt['cleantalk_moderate_ip'] = 'El servicio anti-spam corre a cargo de tu proveedor de alojamiento web. La licencia es la nmero '; -$txt['cleantalk_key_not_valid'] = 'La clave de acceso no es vlida!'; -$txt['cleantalk_get_statistics'] = 'Pincha aqu para acceder a las estadsticas anti-spam'; -$txt['cleantalk_admin_email_will_be_used'] = 'Para el registro se usar el correo del administrador (%s)'; -$txt['cleantalk_first_post_checking'] = 'Activar la prueba antispam solo para usuarios recin registrados'; -$txt['cleantalk_first_post_checking_postinput'] = 'Se verificar el spam de todos los mensajes de los usuarios recin registrados (Solo el grupo %GROUP%)'; -$txt['cleantalk_automod'] = 'Activar la automoderacin en los mensajes'; -$txt['cleantalk_automod_postinput'] = 'Al activar esta casilla, los comentarios sospechosos se pondrn de forma automtica a la espera de aprobacin manual, aunque no haya un 100% de certeza, y se bloquearn los comentarios que sean claramente spam. Asegrate de que la funcin Moderacin de mensajes est activada dentro del Centro de Administracin SMF .> Permisos -> Moderacin de mensajes '; -$txt['cleantalk_api_key_description'] = 'Pgina web de este extensin de SMF: http://cleantalk.org'; +$txt['cleantalk_moderate_ip'] = 'El servicio anti-spam corre a cargo de tu proveedor de alojamiento web. La licencia es la n�mero '; +$txt['cleantalk_key_not_valid'] = '�La clave de acceso no es v�lida!'; +$txt['cleantalk_get_statistics'] = 'Pincha aqu� para acceder a las estad�sticas anti-spam'; +$txt['cleantalk_admin_email_will_be_used'] = 'Para el registro se usar� el correo del administrador (%s)'; +$txt['cleantalk_first_post_checking'] = 'Activar la prueba antispam solo para usuarios reci�n registrados'; +$txt['cleantalk_first_post_checking_postinput'] = 'Se verificar� el spam de todos los mensajes de los usuarios reci�n registrados (Solo el grupo %GROUP%)'; +$txt['cleantalk_automod'] = 'Activar la automoderaci�n en los mensajes'; +$txt['cleantalk_automod_postinput'] = 'Al activar esta casilla, los comentarios sospechosos se pondr�n de forma autom�tica a la espera de aprobaci�n manual, aunque no haya un 100% de certeza, y se bloquear�n los comentarios que sean claramente spam. Aseg�rate de que la funci�n Moderaci�n de mensajes est� activada dentro del Centro de Administraci�n SMF .> Permisos -> Moderaci�n de mensajes '; +$txt['cleantalk_api_key_description'] = 'P�gina web de este extensi�n de SMF: http://cleantalk.org'; $txt['cleantalk_logging'] = 'Activar registro'; -$txt['cleantalk_logging_postinput'] = 'Todas las acciones de esta modificacin se aadirn al registro del foro . (Centro de Administracin SMF -> Registros )'; -$txt['cleantalk_tell_others'] = 'Dile a tus visitantes que usas CleanTalk!'; +$txt['cleantalk_logging_postinput'] = 'Todas las acciones de esta modificaci�n se a�adir�n al registro del foro . (Centro de Administraci�n SMF -> Registros )'; +$txt['cleantalk_tell_others'] = '�Dile a tus visitantes que usas CleanTalk!'; $txt['cleantalk_email_notifications'] = 'Activar las notificaciones por correo para el administrador'; -$txt['cleantalk_email_notifications_postinput'] = 'Enva un correo de notificacin al administrador para todos los resultados de accesos bloqueados/permitidos'; +$txt['cleantalk_email_notifications_postinput'] = 'Env�a un correo de notificaci�n al administrador para todos los resultados de accesos bloqueados/permitidos'; $txt['cleantalk_check_personal_messages'] = 'Activar prueba de antispam para mensaje personal'; $txt['cleantalk_check_personal_messages_postinput'] = 'Preparadas todos los mensajes personales para spam'; $txt['cleantalk_ccf_checking'] = 'Comprobar formularios personalizados'; -$txt['cleantalk_ccf_checking_postinput'] = 'Esta opcin puede provocar conflictos con otras secciones!'; -$txt['cleantalk_tell_others_postinput'] = 'Al marcar esta casilla se aadir un enlace en el pie de tu foro indicando que usas CleanTalk para proteger a tu foro de spam.'; -$txt['cleantalk_tell_others_footer_message'] = 'El spam de este foro est controlado por CleanTalk'; +$txt['cleantalk_ccf_checking_postinput'] = '�Esta opci�n puede provocar conflictos con otras secciones!'; +$txt['cleantalk_tell_others_postinput'] = 'Al marcar esta casilla se a�adir� un enlace en el pie de tu foro indicando que usas CleanTalk para proteger a tu foro de spam.'; +$txt['cleantalk_tell_others_footer_message'] = 'El spam de este foro est� controlado por CleanTalk'; $txt['cleantalk_sfw'] = 'Activar SpamFireWall'; -$txt['cleantalk_sfw_postinput'] = 'Esta opcin permite filtrar bots de spam antes de que accedan al foro. Esta opcin permite reducir el consumo de CPU del servidor y, por tanto, acelera la carga de pginas.'; +$txt['cleantalk_sfw_postinput'] = 'Esta opci�n permite filtrar bots de spam antes de que accedan al foro. Esta opci�n permite reducir el consumo de CPU del servidor y, por tanto, acelera la carga de p�ginas.'; $txt['cleantalk_check_users'] = '%CLEANTALK_CHECK_USERS%'; $txt['cleantalk_check_users_button'] = 'Buscar usuarios generadores de spam'; -$txt['cleantalk_check_users_button_after'] = 'Anti-spam por CleanTalk usar bases de datos con listas negras con todos los usuarios registrados en tu foro y te mostrar cules de ellos han generado actividades de spam en otras webs.'; +$txt['cleantalk_check_users_button_after'] = 'Anti-spam por CleanTalk usar� bases de datos con listas negras con todos los usuarios registrados en tu foro y te mostrar� cu�les de ellos han generado actividades de spam en otras webs.'; $txt['cleantalk_check_users_key_is_bad'] = 'No es posible acceder a las bases de datos en la nube por un problema con la clave de acceso.'; -$txt['cleantalk_check_users_nofound'] = 'Bien! No se encontraron usuarios generadores de spam'; +$txt['cleantalk_check_users_nofound'] = '�Bien! No se encontraron usuarios generadores de spam'; $txt['cleantalk_check_users_tbl_select'] = 'Seleccionar'; $txt['cleantalk_check_users_tbl_username'] = 'Nombre de usuario'; $txt['cleantalk_check_users_tbl_username_details'] = 'Detalles'; $txt['cleantalk_check_users_tbl_joined'] = 'Fecha de registro'; -$txt['cleantalk_check_users_tbl_lastvisit'] = 'ltima visita'; +$txt['cleantalk_check_users_tbl_lastvisit'] = '�ltima visita'; $txt['cleantalk_check_users_tbl_posts'] = 'Mensajes'; $txt['cleantalk_check_users_tbl_posts_show'] = 'Mostrar'; -$txt['cleantalk_check_users_pages'] = 'Pginas'; -$txt['cleantalk_check_users_done'] = 'Completado! Se han revisado todos los usuarios del foro usando bases de datos de listas negras. Se muestran a continuacin los resultados.'; +$txt['cleantalk_check_users_pages'] = 'P�ginas'; +$txt['cleantalk_check_users_done'] = '�Completado! Se han revisado todos los usuarios del foro usando bases de datos de listas negras. Se muestran a continuaci�n los resultados.'; $txt['cleantalk_check_users_tbl_delselect'] = 'Eliminar los seleccionados'; $txt['cleantalk_check_users_tbl_delall'] = 'Eliminarlos todos'; -$txt['cleantalk_check_users_tbl_delnotice'] = 'Todos los temas y mensajes de los usuarios seleccionados sern tambin eliminados'; -$txt['cleantalk_check_users_confirm'] = 'Eliminar la(s) cuenta(s) junto con los temas y mensajes asociados?'; -$txt['cleantalk_sfw_die_notice_ip'] = 'SpamFireWall est activado para tu IP '; -$txt['cleantalk_sfw_die_make_sure_js_enabled'] = 'Para continuar usando la web, por favor, asegarate de tener activado JavaScript.'; -$txt['cleantalk_sfw_die_click_to_pass'] = 'Por favor, pincha aqu para pasar la proteccin,'; -$txt['cleantalk_sfw_die_you_will_be_redirected'] = 'o sers redirigido de forma automtica a la pgina solicitada en 3 segundos.'; +$txt['cleantalk_check_users_tbl_delnotice'] = 'Todos los temas y mensajes de los usuarios seleccionados ser�n tambi�n eliminados'; +$txt['cleantalk_check_users_confirm'] = '�Eliminar la(s) cuenta(s) junto con los temas y mensajes asociados?'; +$txt['cleantalk_sfw_die_notice_ip'] = 'SpamFireWall est� activado para tu IP '; +$txt['cleantalk_sfw_die_make_sure_js_enabled'] = 'Para continuar usando la web, por favor, aseg�arate de tener activado JavaScript.'; +$txt['cleantalk_sfw_die_click_to_pass'] = 'Por favor, pincha aqu� para pasar la protecci�n,'; +$txt['cleantalk_sfw_die_you_will_be_redirected'] = 'o ser�s redirigido de forma autom�tica a la p�gina solicitada en 3 segundos.'; $txt['cleantalk_sfw_die_no_file'] = 'Tu IP ha sido usada con anterioridad para generar spam'; $txt['cleantalk_banner_renew_1'] = 'Por favor, renueva tu licencia anti-spam por %s.'; -$txt['cleantalk_banner_renew_2'] = 'prximo ao'; -$txt['cleantalk_banner_trial_1'] = 'El perodo de prueba de Cleantalk finaliza. Por favor, actualiza a %s!'; -$txt['cleantalk_banner_trial_2'] = 'Versin Premium'; -$txt['cleantalk_banner_bad_key_1'] = 'Introduce la Clave de Acceso %s en los ajustes para activar la proteccin anti-spam'; +$txt['cleantalk_banner_renew_2'] = 'pr�ximo a�o'; +$txt['cleantalk_banner_trial_1'] = 'El per�odo de prueba de Cleantalk finaliza. Por favor, actualiza a %s!'; +$txt['cleantalk_banner_trial_2'] = 'Versi�n Premium'; +$txt['cleantalk_banner_bad_key_1'] = 'Introduce la Clave de Acceso %s en los ajustes para activar la protecci�n anti-spam'; $txt['cleantalk_banner_bad_key_2'] = 'Mod CleanTalk'; // CleanTalk messages end ]]> diff --git a/lib/phpFix.php b/lib/phpFix.php deleted file mode 100644 index 2e8b62a..0000000 --- a/lib/phpFix.php +++ /dev/null @@ -1,38 +0,0 @@ -cleantalk:antispam Anti-spam by CleanTalk modification - 2.30 + 2.31 readme.txt readme_russian.txt + + - Mod main file - Mod admin file - Base class - Extend for base class - Extend for base class - Helper functions - SFW class - Fix for old PHP versions - SFW die page - Cleantalk error template - attention picture + + + Copying extension files + + modification.xml languages/english.xml languages/english-utf8.xml @@ -27,7 +23,11 @@ languages/russian-utf8.xml languages/spanish_es.xml languages/spanish_es-utf8.xml + + install.php + + Please stand by while you are being redirected to this mod configuration settings. @@ -489,5 +489,38 @@ Please stand by while you are being redirected to this mod configuration settings. - + + + This upgrade for Antispam by Cleantalk from 2.29 to 2.31 version: + - Setting "Check registration" added. + - Fix for ut8_decode function. + + Обновление Анти-спама от Cleantalk с версии 2.29 до 2.30: + - Добавлена опция "Проверка регистраций" + - Исправления для функции ut8_decode. + + + + + + + + Copying extension files + + + modification.xml + languages/english.xml + languages/english-utf8.xml + languages/russian.xml + languages/russian-utf8.xml + languages/spanish_es.xml + languages/spanish_es-utf8.xml + + + install.php + + Please stand by while + you are being redirected to this mod configuration settings. + +