db__table__data = $data_table ?: null; $this->db__table__logs = $log_table ?: null; foreach( $params as $param_name => $param ){ $this->$param_name = isset( $this->$param_name ) ? $param : false; } $this->debug = (bool) Get::get( 'debug' ); } /** * @param $ips */ public function ip__append_additional( &$ips ){ $this->real_ip = isset($ips['real']) ? $ips['real'] : null; if( Get::get( 'sfw_test_ip' ) ){ if( Helper::ip__validate( Get::get( 'sfw_test_ip' ) ) !== false ){ $ips['sfw_test'] = Get::get( 'sfw_test_ip' ); $this->test_ip = Get::get( 'sfw_test_ip' ); $this->test = true; } } } /** * Use this method to execute main logic of the module. * * @return array Array of the check results */ public function check(){ $results = array(); $status = 0; // Skip by cookie foreach( $this->ip_array as $current_ip ){ if( substr( Cookie::get( 'ct_sfw_pass_key' ), 0, 32 ) == md5( $current_ip . $this->api_key ) ){ if( Cookie::get( 'ct_sfw_passed' ) ){ if( ! headers_sent() ){ \Cleantalk\Common\Helper::apbct_cookie__set( 'ct_sfw_passed', '0', time() + 86400 * 3, '/', null, false, true, 'Lax' ); } else { $results[] = array( 'ip' => $current_ip, 'is_personal' => false, 'status' => 'PASS_SFW__BY_COOKIE', ); } if( $this->sfw_counter ){ $this->apbct->data['sfw_counter']['all'] ++; $this->apbct->saveData(); } } if( strlen( Cookie::get( 'ct_sfw_pass_key' ) ) > 32 ) { $status = substr( Cookie::get( 'ct_sfw_pass_key' ), -1 ); } if( $status ) { $results[] = array('ip' => $current_ip, 'is_personal' => false, 'status' => 'PASS_SFW__BY_WHITELIST',); } return $results; } } // Common check foreach($this->ip_array as $origin => $current_ip){ $current_ip_v4 = sprintf("%u", ip2long($current_ip)); for ( $needles = array(), $m = 6; $m <= 32; $m ++ ) { $mask = sprintf( "%u", ip2long( long2ip( - 1 << ( 32 - (int) $m ) ) ) ); $needles[] = bindec( decbin( $mask ) & decbin( $current_ip_v4 ) ); } $needles = array_unique( $needles ); $db_results = $this->db->fetch_all("SELECT network, mask, status FROM " . $this->db__table__data . " WHERE network IN (". implode( ',', $needles ) .") AND network = " . $current_ip_v4 . " & mask"); if( ! empty( $db_results ) ){ foreach( $db_results as $db_result ){ if( $db_result['status'] == 1 ) $results[] = array('ip' => $current_ip, 'is_personal' => false, 'status' => 'PASS_SFW__BY_WHITELIST',); else $results[] = array('ip' => $current_ip, 'is_personal' => false, 'status' => 'DENY_SFW',); } }else{ $results[] = array( 'ip' => $current_ip, 'is_personal' => false, 'status' => 'PASS_SFW' ); } } return $results; } /** * Add entry to SFW log. * Writes to database. * * @param string $ip * @param $status */ public function update_log( $ip, $status ) { if( in_array( $status, array( 'PASS_SFW__BY_WHITELIST', 'PASS_SFW', 'PASS_ANTIFLOOD', 'PASS_ANTICRAWLER' ) ) ){ return; } $id = md5( $ip . $this->module_name ); $time = time(); $query = "INSERT INTO " . $this->db__table__logs . " SET id = '$id', ip = '$ip', status = '$status', all_entries = 1, blocked_entries = 1, entries_timestamp = '" . $time . "' ON DUPLICATE KEY UPDATE status = '$status', all_entries = all_entries + 1, blocked_entries = blocked_entries" . ( strpos( $status, 'DENY' ) !== false ? ' + 1' : '' ) . ", entries_timestamp = '" . intval( $time ) . "'"; $this->db->execute( $query ); } public function actions_for_denied( $result ){ if( $this->sfw_counter ){ $this->apbct->data['sfw_counter']['blocked']++; $this->apbct->saveData(); } } public function actions_for_passed( $result ){ if( $this->set_cookies && ! headers_sent() ) { $status = $result['status'] == 'PASS_SFW__BY_WHITELIST' ? '1' : '0'; $cookie_val = md5( $result['ip'] . $this->api_key ) . $status; \Cleantalk\ApbctWP\Helper::apbct_cookie__set( 'ct_sfw_pass_key', $cookie_val, time() + 86400 * 30, '/', null, false ); } } /** * Shows DIE page. * Stops script executing. * * @param $result */ public function _die( $result ){ global $apbct; parent::_die( $result ); // Statistics if(!empty($this->blocked_ips)){ reset($this->blocked_ips); $this->apbct->stats['last_sfw_block']['time'] = time(); $this->apbct->stats['last_sfw_block']['ip'] = $result['ip']; $this->apbct->save('stats'); } // File exists? if(file_exists(CLEANTALK_PLUGIN_DIR . "lib/Cleantalk/ApbctWP/Firewall/die_page_sfw.html")){ $sfw_die_page = file_get_contents(CLEANTALK_PLUGIN_DIR . "lib/Cleantalk/ApbctWP/Firewall/die_page_sfw.html"); $status = $result['status'] == 'PASS_SFW__BY_WHITELIST' ? '1' : '0'; $cookie_val = md5( $result['ip'] . $this->api_key ) . $status; // Translation $replaces = array( '{SFW_DIE_NOTICE_IP}' => __('SpamFireWall is activated for your IP ', 'cleantalk-spam-protect'), '{SFW_DIE_MAKE_SURE_JS_ENABLED}' => __( 'To continue working with the web site, please make sure that you have enabled JavaScript.', 'cleantalk-spam-protect' ), '{SFW_DIE_CLICK_TO_PASS}' => __('Please click the link below to pass the protection,', 'cleantalk-spam-protect'), '{SFW_DIE_YOU_WILL_BE_REDIRECTED}' => sprintf(__('Or you will be automatically redirected to the requested page after %d seconds.', 'cleantalk-spam-protect'), 3), '{CLEANTALK_TITLE}' => ($this->test ? __('This is the testing page for SpamFireWall', 'cleantalk-spam-protect') : ''), '{REMOTE_ADDRESS}' => $result['ip'], '{SERVICE_ID}' => $this->apbct->data['service_id'], '{HOST}' => Server::get( 'HTTP_HOST' ), '{GENERATED}' => '

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

", '{REQUEST_URI}' => Server::get( 'REQUEST_URI' ), // Cookie '{COOKIE_PREFIX}' => '', '{COOKIE_DOMAIN}' => $this->cookie_domain, '{COOKIE_SFW}' => $this->test ? $this->test_ip : $cookie_val, '{COOKIE_ANTICRAWLER}' => hash( 'sha256', $apbct->api_key . $apbct->data['salt'] ), // Test '{TEST_TITLE}' => '', '{REAL_IP__HEADER}' => '', '{TEST_IP__HEADER}' => '', '{TEST_IP}' => '', '{REAL_IP}' => '', ); // Test if($this->test){ $replaces['{TEST_TITLE}'] = __( 'This is the testing page for SpamFireWall', 'cleantalk-spam-protect' ); $replaces['{REAL_IP__HEADER}'] = 'Real IP:'; $replaces['{TEST_IP__HEADER}'] = 'Test IP:'; $replaces['{TEST_IP}'] = $this->test_ip; $replaces['{REAL_IP}'] = $this->real_ip; } // Debug if($this->debug){ $debug = '

Headers

' . var_export(apache_request_headers(), true) . '

REMOTE_ADDR

' . Server::get( 'REMOTE_ADDR' ) . '

SERVER_ADDR

' . Server::get( 'REMOTE_ADDR' ) . '

IP_ARRAY

' . var_export($this->ip_array, true) . '

ADDITIONAL

' . var_export($this->debug_data, true); } $replaces['{DEBUG}'] = isset( $debug ) ? $debug : ''; foreach( $replaces as $place_holder => $replace ){ $sfw_die_page = str_replace( $place_holder, $replace, $sfw_die_page ); } wp_die($sfw_die_page, "Blacklisted", Array('response'=>403)); }else{ wp_die("IP BLACKLISTED. Blocked by SFW " . $result['ip'], "Blacklisted", Array('response'=>403)); } } /** * Sends and wipe SFW log * * @param $db * @param $log_table * @param string $ct_key API key * * @return array|bool array('error' => STRING) */ public static function send_log( $db, $log_table, $ct_key ) { //Getting logs $query = "SELECT * FROM " . $log_table . ";"; $db->fetch_all( $query ); if( count( $db->result ) ){ //Compile logs $data = array(); foreach( $db->result as $key => $value ){ // Converting statuses to API format $value['status'] = $value['status'] === 'DENY_ANTICRAWLER' ? 'BOT_PROTECTION' : $value['status']; $value['status'] = $value['status'] === 'PASS_ANTICRAWLER' ? 'BOT_PROTECTION' : $value['status']; $value['status'] = $value['status'] === 'DENY_ANTIFLOOD' ? 'FLOOD_PROTECTION' : $value['status']; $value['status'] = $value['status'] === 'PASS_ANTIFLOOD' ? 'FLOOD_PROTECTION' : $value['status']; $value['status'] = $value['status'] === 'PASS_SFW__BY_COOKIE' ? null : $value['status']; $value['status'] = $value['status'] === 'DENY_SFW' ? null : $value['status']; $row = array( trim( $value['ip'] ), $value['all_entries'], $value['all_entries'] - $value['blocked_entries'], $value['entries_timestamp'], ); if( $value['status'] ) $row[] = $value['status']; $data[] = $row; } unset( $key, $value ); //Sending the request $result = \Cleantalk\Common\API::method__sfw_logs( $ct_key, $data ); //Checking answer and deleting all lines from the table if( empty( $result['error'] ) ){ if( $result['rows'] == count( $data ) ){ $db->execute( "TRUNCATE TABLE " . $log_table . ";" ); return $result; } return array( 'error' => 'SENT_AND_RECEIVED_LOGS_COUNT_DOESNT_MACH' ); } else{ return $result; } } else{ return $result = array( 'rows' => 0 ); } } /** * Updates SFW local base * * @param $db * @param $db__table__data * @param string $ct_key API key * @param null|string $file_url File URL with SFW data. * @param bool $immediate Requires immmediate update. Without remote call * * @return array|bool array('error' => STRING) */ public static function update( $db, $db__table__data, $ct_key, $file_url = null, $immediate = false){ // Getting remote file name if(!$file_url){ $result = \Cleantalk\Common\API::method__get_2s_blacklists_db($ct_key, 'multifiles', '2_0'); sleep(4); if( empty( $result['error'] ) ){ if( ! empty( $result['file_url'] ) ){ $file_url = trim( $result['file_url'] ); $response_code = Helper::http__request__get_response_code( $file_url ); if( empty( $response_code['error'] ) ){ if( $response_code == 200 || $response_code == 501 ){ $gz_data = Helper::http__request__get_content( $file_url ); if( empty( $gz_data['error'] ) ){ if( Helper::get_mime_type( $gz_data, 'application/x-gzip' ) ){ if( function_exists( 'gzdecode' ) ){ $data = gzdecode( $gz_data ); if( $data !== false ){ $result__clear_db = self::clear_data_table( $db, $db__table__data ); if( empty( $result__clear_db['error'] ) ){ $lines = Helper::buffer__parse__csv( $data ); /*$file_urls = array(); while( current( $lines ) !== false ){ $file_urls[] = current( $lines )[0]; next( $lines ); }*/ $patterns = array(); $patterns[] = 'get'; if( ! $immediate ){ $patterns[] = 'async'; } return Helper::http__request( get_option( 'siteurl' ), array( 'spbc_remote_call_token' => md5( $ct_key ), 'spbc_remote_call_action' => 'sfw_update', 'plugin_name' => 'apbct', 'file_urls' => $file_url, 'url_count' => count( $lines ), 'current_url' => 0, ), $patterns ); }else return $result__clear_db; }else return array('error' => 'COULD_DECODE_MULTIFILE'); }else return array('error' => 'FUNCTION_GZ_DECODE_DOES_NOT_EXIST'); }else return array('error' => 'WRONG_MULTIFILE_MIME_TYPE'); }else return array('error' => 'COULD_NOT_GET_MULTIFILE: ' . $gz_data['error'] ); }else return array('error' => 'MULTIFILE_BAD_RESPONSE_CODE: '. (int) $response_code ); }else return array('error' => 'MULTIFILE_COULD_NOT_GET_RESPONSE_CODE: '. $response_code['error'] ); }else return array('error' => 'NO_REMOTE_MULTIFILE_FOUND: ' . $result['file_url'] ); }else return $result; }else{ $response_code = Helper::http__request($file_url, array(), 'get_code'); if( empty( $response_code['error'] ) ){ if( $response_code == 200 || $response_code == 501 ){ // Check if it's there $gz_data = Helper::http__request__get_content( $file_url ); if( empty( $gz_data['error'] ) ){ if( Helper::get_mime_type( $gz_data, 'application/x-gzip' ) ){ if( function_exists( 'gzdecode' ) ){ $data = gzdecode( $gz_data ); if( $data !== false ){ $lines = Helper::buffer__parse__csv( $data ); }else return array('error' => 'COULD_DECODE_FILE'); }else return array('error' => 'FUNCTION_GZ_DECODE_DOES_NOT_EXIST'); }else return array('error' => 'WRONG_FILE_MIME_TYPE'); reset( $lines ); for( $count_result = 0; current($lines) !== false; ) { $query = "INSERT INTO ".$db__table__data." (network, mask, status) VALUES "; for( $i = 0, $values = array(); APBCT_WRITE_LIMIT !== $i && current( $lines ) !== false; $i ++, $count_result ++, next( $lines ) ){ $entry = current($lines); if(empty($entry)) continue; if ( APBCT_WRITE_LIMIT !== $i ) { // Cast result to int $ip = preg_replace('/[^\d]*/', '', $entry[0]); $mask = preg_replace('/[^\d]*/', '', $entry[1]); $private = isset($entry[2]) ? $entry[2] : 0; } $values[] = '('. $ip .','. $mask .','. $private .')'; } if( ! empty( $values ) ){ $query = $query . implode( ',', $values ) . ';'; $db->execute( $query ); } } return $count_result; }else return array('error' => 'COULD_NOT_GET_FILE: ' . $gz_data['error'] ); }else return array('error' => 'FILE_BAD_RESPONSE_CODE: '. (int) $response_code ); }else return array('error' => 'FILE_COULD_NOT_GET_RESPONSE_CODE: '. $response_code['error'] ); } } /** * Clear SFW table * * @param $db * @param $db__table__data * * @return string[] */ public static function clear_data_table( $db, $db__table__data ) { $db->execute( "TRUNCATE TABLE {$db__table__data};" ); $db->set_query( "SELECT COUNT(network) as cnt FROM {$db__table__data};" )->fetch(); // Check if it is clear if( $db->result['cnt'] != 0 ){ $db->execute( "DELETE FROM {$db__table__data};" ); // Truncate table $db->set_query( "SELECT COUNT(network) as cnt FROM {$db__table__data};" )->fetch(); // Check if it is clear if( $db->result['cnt'] != 0 ){ return array( 'error' => 'COULD_NOT_CLEAR_SFW_TABLE' ); // throw an error } } $db->execute( "ALTER TABLE {$db__table__data} AUTO_INCREMENT = 1;" ); // Drop AUTO INCREMENT } }