class Helper in Anti Spam by CleanTalk 9.1.x
Same name in this branch
- 9.1.x src/lib/Cleantalk/Common/Helper.php \Cleantalk\Common\Helper
- 9.1.x src/lib/Cleantalk/ApbctDrupal/Helper.php \Cleantalk\ApbctDrupal\Helper
Same name and namespace in other branches
- 8.4 src/lib/Cleantalk/Common/Helper.php \Cleantalk\Common\Helper
CleanTalk Helper class. Compatible with any CMS.
@package PHP Antispam by CleanTalk @subpackage Helper @Version 3.5 @author Cleantalk team (welcome@cleantalk.org) @copyright (C) 2014 CleanTalk team (http://cleantalk.org) @license GNU/GPL: http://www.gnu.org/copyleft/gpl.html
Hierarchy
- class \Cleantalk\Common\Helper
Expanded class hierarchy of Helper
See also
https://github.com/CleanTalk/php-antispam
6 files declare their use of Helper
- AntiCrawler.php in src/
lib/ Cleantalk/ Common/ Firewall/ Modules/ AntiCrawler.php - AntiFlood.php in src/
lib/ Cleantalk/ Common/ Firewall/ Modules/ AntiFlood.php - Firewall.php in src/
lib/ Cleantalk/ Common/ Firewall/ Firewall.php - FirewallModule.php in src/
lib/ Cleantalk/ Common/ Firewall/ FirewallModule.php - FirewallUpdater.php in src/
lib/ Cleantalk/ Common/ Firewall/ FirewallUpdater.php
File
- src/
lib/ Cleantalk/ Common/ Helper.php, line 19
Namespace
Cleantalk\CommonView source
class Helper {
/**
* Default user agent for HTTP requests
*/
const AGENT = 'Cleantalk-Helper/3.4';
/**
* @var array Set of private networks IPv4 and IPv6
*/
public static $private_networks = array(
'v4' => 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',
),
);
/**
* @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',
'apix4.cleantalk.org' => '44.227.90.42',
'apix5.cleantalk.org' => '15.188.198.212',
'apix6.cleantalk.org' => '54.219.94.72',
//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 string $ip_type_to_get Type of IP you want to receive
* @param bool $v4_only
*
* @return string|null
*/
public static function ip__get($ip_type_to_get = 'real', $v4_only = true, $headers = array()) {
$out = null;
switch ($ip_type_to_get) {
// Cloud Flare
case 'cloud_flare':
$headers = $headers ?: self::http__get_headers();
if (isset($headers['Cf-Connecting-Ip'], $headers['Cf-Ipcountry'], $headers['Cf-Ray'])) {
$tmp = strpos($headers['Cf-Connecting-Ip'], ',') !== false ? explode(',', $headers['Cf-Connecting-Ip']) : (array) $headers['Cf-Connecting-Ip'];
$ip_version = self::ip__validate(trim($tmp[0]));
if ($ip_version) {
$out = $ip_version === 'v6' && !$v4_only ? self::ip__v6_normalize(trim($tmp[0])) : trim($tmp[0]);
}
}
break;
// GTranslate
case 'gtranslate':
$headers = $headers ?: self::http__get_headers();
if (isset($headers['X-Gt-Clientip'], $headers['X-Gt-Viewer-Ip'])) {
$ip_version = self::ip__validate($headers['X-Gt-Viewer-Ip']);
if ($ip_version) {
$out = $ip_version === 'v6' && !$v4_only ? self::ip__v6_normalize($headers['X-Gt-Viewer-Ip']) : $headers['X-Gt-Viewer-Ip'];
}
}
break;
// ezoic
case 'ezoic':
$headers = $headers ?: self::http__get_headers();
if (isset($headers['X-Middleton'], $headers['X-Middleton-Ip'])) {
$ip_version = self::ip__validate($headers['X-Middleton-Ip']);
if ($ip_version) {
$out = $ip_version === 'v6' && !$v4_only ? self::ip__v6_normalize($headers['X-Middleton-Ip']) : $headers['X-Middleton-Ip'];
}
}
break;
// Sucury
case 'sucury':
$headers = $headers ?: self::http__get_headers();
if (isset($headers['X-Sucuri-Clientip'])) {
$ip_version = self::ip__validate($headers['X-Sucuri-Clientip']);
if ($ip_version) {
$out = $ip_version === 'v6' && !$v4_only ? self::ip__v6_normalize($headers['X-Sucuri-Clientip']) : $headers['X-Sucuri-Clientip'];
}
}
break;
// X-Forwarded-By
case 'x_forwarded_by':
$headers = $headers ?: self::http__get_headers();
if (isset($headers['X-Forwarded-By'], $headers['X-Client-Ip'])) {
$ip_version = self::ip__validate($headers['X-Client-Ip']);
if ($ip_version) {
$out = $ip_version === 'v6' && !$v4_only ? self::ip__v6_normalize($headers['X-Client-Ip']) : $headers['X-Client-Ip'];
}
}
break;
// Stackpath
case 'stackpath':
$headers = $headers ?: self::http__get_headers();
if (isset($headers['X-Sp-Edge-Host'], $headers['X-Sp-Forwarded-Ip'])) {
$ip_version = self::ip__validate($headers['X-Sp-Forwarded-Ip']);
if ($ip_version) {
$out = $ip_version === 'v6' && !$v4_only ? self::ip__v6_normalize($headers['X-Sp-Forwarded-Ip']) : $headers['X-Sp-Forwarded-Ip'];
}
}
break;
// Ico-X-Forwarded-For
case 'ico_x_forwarded_for':
$headers = $headers ?: self::http__get_headers();
if (isset($headers['Ico-X-Forwarded-For'], $headers['X-Forwarded-Host'])) {
$ip_version = self::ip__validate($headers['Ico-X-Forwarded-For']);
if ($ip_version) {
$out = $ip_version === 'v6' && !$v4_only ? self::ip__v6_normalize($headers['Ico-X-Forwarded-For']) : $headers['Ico-X-Forwarded-For'];
}
}
break;
// OVH
case 'ovh':
$headers = $headers ?: self::http__get_headers();
if (isset($headers['X-Cdn-Any-Ip'], $headers['Remote-Ip'])) {
$ip_version = self::ip__validate($headers['Remote-Ip']);
if ($ip_version) {
$out = $ip_version === 'v6' && !$v4_only ? self::ip__v6_normalize($headers['Remote-Ip']) : $headers['Remote-Ip'];
}
}
break;
// Incapsula proxy
case 'incapsula':
$headers = $headers ?: self::http__get_headers();
if (isset($headers['Incap-Client-Ip'], $headers['X-Forwarded-For'])) {
$ip_version = self::ip__validate($headers['Incap-Client-Ip']);
if ($ip_version) {
$out = $ip_version === 'v6' && !$v4_only ? self::ip__v6_normalize($headers['Incap-Client-Ip']) : $headers['Incap-Client-Ip'];
}
}
break;
// Remote addr
case 'remote_addr':
$ip_version = self::ip__validate(Server::get('REMOTE_ADDR'));
if ($ip_version) {
$out = $ip_version === 'v6' && !$v4_only ? self::ip__v6_normalize(Server::get('REMOTE_ADDR')) : Server::get('REMOTE_ADDR');
}
break;
// X-Forwarded-For
case 'x_forwarded_for':
$headers = $headers ?: self::http__get_headers();
if (isset($headers['X-Forwarded-For'])) {
$tmp = explode(',', trim($headers['X-Forwarded-For']));
$tmp = trim($tmp[0]);
$ip_version = self::ip__validate($tmp);
if ($ip_version) {
$out = $ip_version === 'v6' && !$v4_only ? self::ip__v6_normalize($tmp) : $tmp;
}
}
break;
// X-Real-Ip
case 'x_real_ip':
$headers = $headers ?: self::http__get_headers();
if (isset($headers['X-Real-Ip'])) {
$tmp = explode(",", trim($headers['X-Real-Ip']));
$tmp = trim($tmp[0]);
$ip_version = self::ip__validate($tmp);
if ($ip_version) {
$out = $ip_version === 'v6' && !$v4_only ? self::ip__v6_normalize($tmp) : $tmp;
}
}
break;
// Real
// 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.
case 'real':
// Detect IP type
$out = self::ip__get('cloud_flare', $v4_only, $headers);
$out = $out ?: self::ip__get('sucury', $v4_only, $headers);
$out = $out ?: self::ip__get('gtranslate', $v4_only, $headers);
$out = $out ?: self::ip__get('ezoic', $v4_only, $headers);
$out = $out ?: self::ip__get('stackpath', $v4_only, $headers);
$out = $out ?: self::ip__get('x_forwarded_by', $v4_only, $headers);
$out = $out ?: self::ip__get('ico_x_forwarded_for', $v4_only, $headers);
$out = $out ?: self::ip__get('ovh', $v4_only, $headers);
$out = $out ?: self::ip__get('incapsula', $v4_only, $headers);
$ip_version = self::ip__validate($out);
// Is private network
if (!$out || $out && (self::ip__is_private_network($out, $ip_version) || self::ip__mask_match($out, Server::get('SERVER_ADDR') . '/24', $ip_version))) {
//@todo Remove local IP from x-forwarded-for and x-real-ip
$out = $out ?: self::ip__get('x_forwarded_for', $v4_only, $headers);
$out = $out ?: self::ip__get('x_real_ip', $v4_only, $headers);
}
$out = $out ?: self::ip__get('remote_addr', $v4_only, $headers);
break;
default:
$out = self::ip__get('real', $v4_only, $headers);
}
// Final validating IP
$ip_version = self::ip__validate($out);
if (!$ip_version) {
return null;
}
elseif ($ip_version === 'v6' && $v4_only) {
return null;
}
else {
return $out;
}
}
/**
* 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
*/
public static 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;
}
if (!self::ip__validate($ip) || !self::cidr__validate($cidr)) {
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
*/
public static 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
}
/**
* Validate CIDR
*
* @param string $cidr expects string like 1.1.1.1/32
*
* @return bool
*/
public static function cidr__validate($cidr) {
$cidr = explode('/', $cidr);
return isset($cidr[0], $cidr[1]) && self::ip__validate($cidr[0]) && preg_match('@\\d{1,2}@', $cidr[1]);
}
/**
* Expand IPv6
*
* @param string $ip
*
* @return string IPv6
*/
public static 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
*/
public static 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
*/
public static 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
*/
public static 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
*/
public static 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
*/
public static 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;
}
/*
* Get data from submit recursively
*/
public static function get_fields_any($arr, $fields_exclusions = '', $message = array(), $email = null, $nickname = array(
'nick' => '',
'first' => '',
'last' => '',
), $subject = null, $contact = true, $prev_name = '') {
//Skip request if fields exists
$skip_params = array(
'ipn_track_id',
// PayPal IPN #
'txn_type',
// PayPal transaction type
'payment_status',
// PayPal payment status
'ccbill_ipn',
// CCBill IPN
'ct_checkjs',
// skip ct_checkjs field
'api_mode',
// DigiStore-API
'loadLastCommentId',
);
// Fields to replace with ****
$obfuscate_params = array(
'password',
'pass',
'pwd',
'pswd',
);
// Skip feilds with these strings and known service fields
$skip_fields_with_strings = array(
// Common
'ct_checkjs',
//Do not send ct_checkjs
'nonce',
//nonce for strings such as 'rsvp_nonce_name'
'security',
// 'action',
'http_referer',
'timestamp',
'captcha',
// Formidable Form
'form_key',
'submit_entry',
// Custom Contact Forms
'form_id',
'ccf_form',
'form_page',
// Qu Forms
'iphorm_uid',
'form_url',
'post_id',
'iphorm_ajax',
'iphorm_id',
// Fast SecureContact Froms
'fs_postonce_1',
'fscf_submitted',
'mailto_id',
'si_contact_action',
// Ninja Forms
'formData_id',
'formData_settings',
'formData_fields_\\d+_id',
'formData_fields_\\d+_files.*',
// E_signature
'recipient_signature',
'output_\\d+_\\w{0,2}',
// Contact Form by Web-Settler protection
'_formId',
'_returnLink',
// Social login and more
'_save',
'_facebook',
'_social',
'user_login-',
// Contact Form 7
'_wpcf7',
'avatar__file_image_data',
'task',
'page_url',
'page_title',
'Submit',
'formId',
'key',
'id',
'hiddenlists',
'ctrl',
'task',
'option',
'nextstep',
'acy_source',
'subid',
'ct_action',
'ct_method',
);
// Reset $message if we have a sign-up data
$skip_message_post = array(
'edd_action',
);
foreach ($skip_params as $value) {
if (@array_key_exists($value, $_GET) || @array_key_exists($value, $_POST)) {
$contact = false;
}
}
unset($value);
if (count($arr)) {
foreach ($arr as $key => $value) {
if (gettype($value) == 'string') {
$decoded_json_value = json_decode($value, true);
if ($decoded_json_value !== null) {
$value = $decoded_json_value;
}
}
if (!is_array($value) && !is_object($value)) {
//Add custom exclusions
if (is_string($fields_exclusions) && !empty($fields_exclusions)) {
$fields_exclusions = explode(",", $fields_exclusions);
if (is_array($fields_exclusions) && !empty($fields_exclusions)) {
foreach ($fields_exclusions as &$fields_exclusion) {
if (preg_match('/\\[*\\]/', $fields_exclusion)) {
// I have to do this to support exclusions like 'submitted[name]'
$fields_exclusion = str_replace(array(
'[',
']',
), array(
'_',
'',
), $fields_exclusion);
}
}
$skip_fields_with_strings = array_merge($skip_fields_with_strings, $fields_exclusions);
}
}
if (in_array($key, $skip_params, true) && $key != 0 && $key != '' || preg_match("/^ct_checkjs/", $key)) {
$contact = false;
}
if ($value === '') {
continue;
}
// Skipping fields names with strings from (array)skip_fields_with_strings
foreach ($skip_fields_with_strings as $needle) {
if (preg_match("/" . $needle . "/", $prev_name . $key) == 1) {
continue 2;
}
}
unset($needle);
// Obfuscating params
foreach ($obfuscate_params as $needle) {
if (strpos($key, $needle) !== false) {
$value = self::obfuscate_param($value);
continue 2;
}
}
unset($needle);
// Removes whitespaces
$value = urldecode(trim($value));
// Fully cleaned message
$value_for_email = trim($value);
// Removes shortcodes to do better spam filtration on server side.
// Email
if (!$email && preg_match("/^\\S+@\\S+\\.\\S+\$/", $value_for_email)) {
$email = $value_for_email;
// Names
}
elseif (preg_match("/name/i", $key)) {
preg_match("/((name.?)?(your|first|for)(.?name)?)\$/", $key, $match_forename);
preg_match("/((name.?)?(last|family|second|sur)(.?name)?)\$/", $key, $match_surname);
preg_match("/^(name.?)?(nick|user)(.?name)?\$/", $key, $match_nickname);
if (count($match_forename) > 1) {
$nickname['first'] = $value;
}
elseif (count($match_surname) > 1) {
$nickname['last'] = $value;
}
elseif (count($match_nickname) > 1) {
$nickname['nick'] = $value;
}
else {
$nickname[$prev_name . $key] = $value;
}
// Subject
}
elseif ($subject === null && preg_match("/subject/i", $key)) {
$subject = $value;
// Message
}
else {
$message[$prev_name . $key] = $value;
}
}
elseif (!is_object($value)) {
$prev_name_original = $prev_name;
$prev_name = $prev_name === '' ? $key . '_' : $prev_name . $key . '_';
$temp = self::get_fields_any($value, $fields_exclusions, $message, $email, $nickname, $subject, $contact, $prev_name);
$message = $temp['message'];
$email = $temp['email'] ? $temp['email'] : null;
$nickname = $temp['nickname'] ? $temp['nickname'] : null;
$subject = $temp['subject'] ? $temp['subject'] : null;
if ($contact === true) {
$contact = $temp['contact'] === false ? false : true;
}
$prev_name = $prev_name_original;
}
}
unset($key, $value);
}
foreach ($skip_message_post as $v) {
if (isset($_POST[$v])) {
$message = null;
break;
}
}
unset($v);
//If top iteration, returns compiled name field. Example: "Nickname Firtsname Lastname".
if ($prev_name === '') {
if (!empty($nickname)) {
$nickname_str = '';
foreach ($nickname as $value) {
$nickname_str .= $value ? $value . " " : "";
}
unset($value);
}
$nickname = $nickname_str;
}
$return_param = array(
'email' => $email,
'nickname' => $nickname,
'subject' => $subject,
'contact' => $contact,
'message' => $message,
);
return $return_param;
}
/**
* Masks a value with asterisks (*) Needed by the getFieldsAny()
* @return string
*/
public static function obfuscate_param($value = null) {
if ($value && (!is_object($value) || !is_array($value))) {
$length = strlen($value);
$value = str_repeat('*', $length);
}
return $value;
}
/**
* Print html form for external forms()
* @return string
*/
public static function print_form($arr, $k) {
foreach ($arr as $key => $value) {
if (!is_array($value)) {
if ($k == '') {
print '<textarea name="' . $key . '" style="display:none;">' . htmlspecialchars($value) . '</textarea>';
}
else {
print '<textarea name="' . $k . '[' . $key . ']" style="display:none;">' . htmlspecialchars($value) . '</textarea>';
}
}
}
}
/*
* Checking api_key
* returns (boolean)
*/
public static function key_is_correct($api_key = '') {
return preg_match('/^[a-z\\d]{3,15}$|^$/', $api_key);
}
/**
* 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))
* @todo Have to replace this method to the new class like HttpHelper
*/
public static function http__request($url, $data = array(), $presets = null, $opts = array()) {
if (function_exists('curl_init')) {
$ch = curl_init();
if (!empty($data)) {
// If $data scalar converting it to array
$data = is_string($data) || is_int($data) ? array(
$data => 1,
) : $data;
// Build query
$opts[CURLOPT_POSTFIELDS] = $data;
}
// Merging OBLIGATORY options with GIVEN options
$opts = self::array_merge__save_numeric_keys(array(
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CONNECTTIMEOUT_MS => 6000,
CURLOPT_FORBID_REUSE => true,
CURLOPT_USERAGENT => self::AGENT . '; ' . (isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : 'UNKNOWN_HOST'),
CURLOPT_POST => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => 0,
CURLOPT_HTTPHEADER => array(
'Expect:',
),
// Fix for large data and old servers http://php.net/manual/ru/function.curl-setopt.php#82418
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 5,
), $opts);
// Use presets
$presets = is_array($presets) ? $presets : explode(' ', $presets);
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] = 1000;
break;
case 'get':
$opts[CURLOPT_URL] .= $data ? '?' . str_replace("&", "&", http_build_query($data)) : '';
$opts[CURLOPT_CUSTOMREQUEST] = 'GET';
$opts[CURLOPT_POST] = false;
$opts[CURLOPT_POSTFIELDS] = null;
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) {
if (strpos($result, PHP_EOL) !== false && !in_array('dont_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'];
}
curl_close($ch);
$out = $result;
}
else {
$out = array(
'error' => curl_error($ch),
);
}
}
else {
$out = array(
'error' => 'CURL_NOT_INSTALLED',
);
}
/**
* Getting HTTP-response code without cURL
*/
if ($presets && ($presets == 'get_code' || is_array($presets) && in_array('get_code', $presets)) && isset($out['error']) && $out['error'] == 'CURL_NOT_INSTALLED') {
$headers = get_headers($url);
$out = (int) preg_replace('/.*(\\d{3}).*/', '$1', $headers[0]);
}
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
*/
public static function is_json($string) {
return is_string($string) && is_array(json_decode($string, true)) ? true : false;
}
/**
* Universal method to adding cookies
*
* @param $name
* @param string $value
* @param int $expires
* @param string $path
* @param null $domain
* @param bool $secure
* @param bool $httponly
* @param string $samesite
*
* @return void
*/
public static function apbct_cookie__set($name, $value = '', $expires = 0, $path = '', $domain = null, $secure = false, $httponly = false, $samesite = 'Lax') {
// For PHP 7.3+ and above
if (version_compare(phpversion(), '7.3.0', '>=')) {
$params = array(
'expires' => $expires,
'path' => $path,
'domain' => $domain,
'secure' => $secure,
'httponly' => $httponly,
);
if ($samesite) {
$params['samesite'] = $samesite;
}
setcookie($name, $value, $params);
// For PHP 5.6 - 7.2
}
else {
setcookie($name, $value, $expires, $path, $domain, $secure, $httponly);
}
}
public static function time__get_interval_start($interval = 300) {
return time() - (time() - strtotime(date('d F Y'))) % $interval;
}
/**
* Get mime type from file or data
*
* @param string $data Path to file or data
* @param string $type Default mime type. Returns if we failed to detect type
*
* @return string
*/
static function get_mime_type($data, $type = '') {
$data = str_replace(chr(0), '', $data);
// Clean input of null bytes
if (!empty($data) && @file_exists($data)) {
$type = mime_content_type($data);
}
elseif (function_exists('finfo_open')) {
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$type = finfo_buffer($finfo, $data);
finfo_close($finfo);
}
return $type;
}
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;
}
/**
* Pops line from buffer without formatting
*
* @param $csv
*
* @return false|string
*/
public static function buffer__csv__pop_line(&$csv) {
$pos = strpos($csv, "\n");
$line = substr($csv, 0, $pos);
$csv = substr_replace($csv, '', 0, $pos + 1);
return $line;
}
/**
* Pops line from the csv buffer and fromat it by map to array
*
* @param $csv
* @param array $map
*
* @return array|false
*/
public static function buffer__csv__get_map(&$csv) {
$line = static::buffer__csv__pop_line($csv);
return explode(',', $line);
}
/**
* Pops line from the csv buffer and fromat it by map to array
*
* @param $csv
* @param array $map
*
* @return array|false
*/
public static function buffer__csv__pop_line_to_array(&$csv, $map = array()) {
$line = trim(static::buffer__csv__pop_line($csv));
$line = strpos($line, '\'') === 0 ? str_getcsv($line, ',', '\'') : explode(',', $line);
if ($map) {
$line = array_combine($map, $line);
}
return $line;
}
/**
* Escapes MySQL params
*
* @param string|int $param
* @param string $quotes
*
* @return int|string
*/
public static function db__prepare_param($param, $quotes = '\'') {
if (is_array($param)) {
foreach ($param as &$par) {
$par = self::db__prepare_param($par);
}
}
switch (true) {
case is_numeric($param):
$param = intval($param);
break;
case is_string($param) && strtolower($param) == 'null':
$param = 'NULL';
break;
case is_string($param):
global $wpdb;
$param = $quotes . $wpdb
->_real_escape($param) . $quotes;
break;
}
return $param;
}
/**
* Gets every HTTP_ headers from $_SERVER
*
* If Apache web server is missing then making
* Patch for apache_request_headers()
*
* @return array
* @todo Have to replace this method to the new class like HttpHelper
*/
public static function http__get_headers() {
$headers = array();
foreach ($_SERVER as $key => $val) {
if (0 === stripos($key, 'http_')) {
$server_key = preg_replace('/^http_/i', '', $key);
$key_parts = explode('_', $server_key);
if (count($key_parts) > 0 and strlen($server_key) > 2) {
foreach ($key_parts as $part_index => $part) {
if (!empty($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;
}
/**
* Wrapper for http_request
* Requesting HTTP response code for $url
*
* @param string $url
*
* @return array|mixed|string
*/
public static function http__request__get_response_code($url) {
return static::http__request($url, array(), 'get_code');
}
/**
* Wrapper for http_request
* Requesting data via HTTP request with GET method
*
* @param string $url
*
* @return array|mixed|string
*/
public static function http__request__get_content($url) {
return static::http__request($url, array(), 'get dont_split_to_array');
}
/**
* Do the remote call to the host.
*
* @param string $rc_action
* @param array $request_params
* @param array $patterns
* @return array|bool
* @todo Have to replace this method to the new class like HttpHelper
*/
public static function http__request__rc_to_host($rc_action, $request_params, $patterns = array()) {
$request_params__default = array(
'spbc_remote_call_action' => $rc_action,
'plugin_name' => 'apbct',
);
$result__rc_check_website = static::http__request(static::getSiteUrl(), array_merge($request_params__default, $request_params, array(
'test' => 'test',
)), array(
'get',
));
if (empty($result__rc_check_website['error'])) {
if (is_string($result__rc_check_website) && preg_match('@^.*?OK$@', $result__rc_check_website)) {
static::http__request(static::getSiteUrl(), array_merge($request_params__default, $request_params), array_merge(array(
'get',
), $patterns));
}
else {
return array(
'error' => 'WRONG_SITE_RESPONSE ACTION: ' . $rc_action . ' RESPONSE: ' . htmlspecialchars(substr(!is_string($result__rc_check_website) ? print_r($result__rc_check_website, true) : $result__rc_check_website, 0, 400)),
);
}
}
else {
return array(
'error' => 'WRONG_SITE_RESPONSE TEST ACTION: ' . $rc_action . ' ERROR: ' . $result__rc_check_website['error'],
);
}
return true;
}
/**
* Get site url for remote calls.
*
* @return string@important This method can be overloaded in the CMS-based Helper class.
*
*/
private static function getSiteUrl() {
return (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://" . $_SERVER['HTTP_HOST'] . (isset($_SERVER['SCRIPT_URL']) ? $_SERVER['SCRIPT_URL'] : '');
}
/**
* Get fw stats from the storage.
*
* @return array
* @example array( 'firewall_updating' => false, 'firewall_updating_id' => md5(), 'firewall_update_percent' => 0, 'firewall_updating_last_start' => 0 )
* @important This method must be overloaded in the CMS-based Helper class.
*/
public static function getFwStats() {
die(__METHOD__ . ' method must be overloaded in the CMS-based Helper class');
}
/**
* Save fw stats on the storage.
*
* @param array $fw_stats
* @return bool
* @important This method must be overloaded in the CMS-based Helper class.
*/
public static function setFwStats($fw_stats) {
die(__METHOD__ . ' method must be overloaded in the CMS-based Helper class');
}
/**
* Implement here any actions after SFW updating finished.
*
* @return void
*/
public static function SfwUpdate_DoFinisnAction() {
// EXAMPLE
// global $apbct;
// $apbct->data['last_firewall_updated'] = current_time('timestamp');
// $apbct->save('data');
// $apbct->stats['sfw']['entries'] = $wpdb->get_var('SELECT COUNT(*) FROM ' . APBCT_TBL_FIREWALL_DATA );
// $apbct->stats['sfw']['last_update_time'] = time();
// $apbct->save('stats');
}
/**
* Its own implementation of the native method long2ip()
*
* @return string
* @psalm-suppress PossiblyUnusedMethod
*/
public static function ip__long2ip($ipl32) {
$ip[0] = $ipl32 >> 24 & 255;
$ip[1] = $ipl32 >> 16 & 255;
$ip[2] = $ipl32 >> 8 & 255;
$ip[3] = $ipl32 & 255;
return implode('.', $ip);
}
}
Members
Name![]() |
Modifiers | Type | Description | Overrides |
---|---|---|---|---|
Helper:: |
public static | property | * | |
Helper:: |
public static | property | * | |
Helper:: |
constant | * Default user agent for HTTP requests | ||
Helper:: |
public static | function | Universal method to adding cookies | |
Helper:: |
public static | function | * Merging arrays without reseting numeric keys * * | |
Helper:: |
public static | function | * Merging arrays without reseting numeric keys recursive * * | |
Helper:: |
public static | function | * Pops line from the csv buffer and fromat it by map to array * * | |
Helper:: |
public static | function | * Pops line from buffer without formatting * * | |
Helper:: |
public static | function | * Pops line from the csv buffer and fromat it by map to array * * | |
Helper:: |
static | function | ||
Helper:: |
static | function | ||
Helper:: |
public static | function | Validate CIDR | |
Helper:: |
public static | function | Escapes MySQL params | |
Helper:: |
public static | function | * Resolve DNS to IP * * | |
Helper:: |
public static | function | * Function convert from UTF8 * * | |
Helper:: |
public static | function | Get fw stats from the storage. | 1 |
Helper:: |
private static | function | Get site url for remote calls. | |
Helper:: |
public static | function | ||
Helper:: |
static | function | * Get mime type from file or data * * | |
Helper:: |
public static | function | Gets every HTTP_ headers from $_SERVER | |
Helper:: |
public static | function | * 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 * * @todo Have to replace this method… | |
Helper:: |
public static | function | Wrapper for http_request Requesting data via HTTP request with GET method | |
Helper:: |
public static | function | Wrapper for http_request Requesting HTTP response code for $url | |
Helper:: |
public static | function | Do the remote call to the host. | |
Helper:: |
public static | function | Getting arrays of IP (REMOTE_ADDR, X-Forwarded-For, X-Real-Ip, Cf_Connecting_Ip) | |
Helper:: |
public static | function | * Get URL form IP. Check if it's belong to cleantalk. * * | |
Helper:: |
static | function | * Checks if the IP is in private range * * | |
Helper:: |
public static | function | Its own implementation of the native method long2ip() | |
Helper:: |
public static | function | * Check if the IP belong to mask. Recursive. * Octet by octet for IPv4 * Hextet by hextet for IPv6 * * | |
Helper:: |
static | function | * Converts long mask like 4294967295 to number like 32 * * | |
Helper:: |
public static | function | * Get URL form IP * * | |
Helper:: |
public static | function | * Get URL form IP. Check if it's belong to cleantalk. * * | |
Helper:: |
public static | function | * Expand IPv6 * * | |
Helper:: |
public static | function | * Reduce IPv6 * * | |
Helper:: |
public static | function | * Validating IPv4, IPv6 * * | |
Helper:: |
public static | function | * Checks if the string is JSON type * * | |
Helper:: |
public static | function | ||
Helper:: |
public static | function | * Masks a value with asterisks (*) Needed by the getFieldsAny() * | |
Helper:: |
public static | function | * Print html form for external forms() * | |
Helper:: |
public static | function | * Function removing non UTF8 characters from array|string|object * * | |
Helper:: |
public static | function | Save fw stats on the storage. | 1 |
Helper:: |
public static | function | Implement here any actions after SFW updating finished. | 1 |
Helper:: |
public static | function | ||
Helper:: |
public static | function | * Function convert anything to UTF8 and removes non UTF8 characters * * |