function safe_feof($fp, &$start = NULL) { $start = microtime(true); return feof($fp); } function utf8_enc($param) { if (is_array($param)) { array_walk_recursive($param, function (&$item, $index) { $item = utf8_enc($item); }); return $param; } mb_detect_order('UTF-8, ISO-8859-15, ISO-8859-1, Windows-1252'); $param .= '_'; $currentCharset = mb_detect_encoding($param); if ($currentCharset != 'UTF-8') { $param = mb_convert_encoding($param, 'UTF-8', $currentCharset); } return substr($param, 0, strlen($param) - 1); } function pregme($char,$begi,$end,$last = false) { if ($last) {$pos1 = strrpos($char,$begi);} else {$pos1 = strpos($char,$begi);} if ($pos1 === false) return false; $take = substr($char,$pos1+strlen($begi)); $take1 = strpos($take,$end); if ($take1 === false) return false; return substr($take,0,$take1); } function get_cookies($out, $n_c ,$urlencode = 0) { if (($cookie = pregme($out, ': '.$n_c.'=',';'))) { return $n_c.'='.(($urlencode == 1)?urlencode($cookie):$cookie).'; '; } else { return ''; } } function xml_encode($mixed, $root = 'root', $domElement = NULL, $DOMDocument = NULL, $pre_index = '') { if (is_null($DOMDocument)) { $DOMDocument = new DOMDocument; $DOMDocument->formatOutput = true; $rootNode = $DOMDocument->createElement($root); $DOMDocument->appendChild($rootNode); xml_encode($mixed, $root, $rootNode, $DOMDocument); echo $DOMDocument->saveXML(); } else { if (is_array($mixed)) { foreach ($mixed as $index=>$mixedElement) { if (is_int($index)) { - $nodeName = 'entry_'.$index; } else { $nodeName = $index; } $node = $DOMDocument->createElement($nodeName); $domElement->appendChild($node); if (is_array($mixedElement)) { $pre_index .= $index.'_'; } xml_encode($mixedElement, $root, $node, $DOMDocument, $pre_index); } } else { $new_node = $DOMDocument->createTextNode($mixed); $domElement->appendChild($new_node); } } } function replace_hex_val($v) { $v = strtr($v[0], array('\\x' => '')); return hex2bin($v); } function replace_hex_vals($string) { return preg_replace_callback('/(?:\\\\x[0-9a-fA-Z]{2})+/', "replace_hex_val", $string); } function replace_ascii_val($v) { $v = strtr($v[0], array('\\u' => '')); return mb_convert_encoding(pack('H*', $v), 'UTF-8', 'UTF-16BE'); } function replace_ascii_vals($string) { return preg_replace_callback('/(?:\\\\u[0-9a-fA-Z]{4})+/', "replace_ascii_val", $string); } function objectToArray($d) { if (is_object($d)) { $d = get_object_vars($d); } if (is_array($d)) { return array_map(__FUNCTION__, $d); } else { return $d; } } function hextobin($hexstr) { $n = strlen($hexstr); $sbin = ""; $i = 0; while ($i<$n) { $a = substr($hexstr, $i, 2); $c = pack("H*", $a); if ($i == 0){ $sbin = $c; } else { $sbin.= $c; } $i += 2; } return $sbin; } function get_server_addr() { if (isset($_SERVER['SERVER_ADDR'])) { if ($_SERVER['SERVER_ADDR'] == '10.0.0.1' OR $_SERVER['SERVER_ADDR'] == '94.140.4.2' OR $_SERVER['SERVER_ADDR'] == '77.72.92.149' OR $_SERVER['SERVER_ADDR'] == '149.11.52.93' OR $_SERVER['SERVER_ADDR'] == '2a01:240:ab04::6' OR $_SERVER['SERVER_ADDR'] == '2001:978:2:61::6') { return '94.140.4.2'; } elseif ($_SERVER['SERVER_ADDR'] == '10.0.0.2' OR $_SERVER['SERVER_ADDR'] == '94.140.4.3' OR $_SERVER['SERVER_ADDR'] == '77.72.92.150' OR $_SERVER['SERVER_ADDR'] == '149.11.52.94' OR $_SERVER['SERVER_ADDR'] == '2a01:240:ab04::7' OR $_SERVER['SERVER_ADDR'] == '2001:978:2:61::7') { return '94.140.4.3'; } elseif ($_SERVER['SERVER_ADDR'] == '10.0.0.3' OR $_SERVER['SERVER_ADDR'] == '94.140.4.4' OR $_SERVER['SERVER_ADDR'] == '77.72.92.151' OR $_SERVER['SERVER_ADDR'] == '2a01:240:ab04::8' OR $_SERVER['SERVER_ADDR'] == '2001:978:2:61::8') { return '94.140.4.4'; } elseif ($_SERVER['SERVER_ADDR'] == '10.0.0.4' OR $_SERVER['SERVER_ADDR'] == '94.140.4.5' OR $_SERVER['SERVER_ADDR'] == '77.72.92.152' OR $_SERVER['SERVER_ADDR'] == '2a01:240:ab04::9' OR $_SERVER['SERVER_ADDR'] == '2001:978:2:61::9') { return '94.140.4.5'; } elseif ($_SERVER['SERVER_ADDR'] == '10.0.0.5' OR $_SERVER['SERVER_ADDR'] == '94.140.4.6' OR $_SERVER['SERVER_ADDR'] == '77.72.92.153' OR $_SERVER['SERVER_ADDR'] == '2a01:240:ab04::10' OR $_SERVER['SERVER_ADDR'] == '2001:978:2:61::10') { return '94.140.4.6'; } elseif ($_SERVER['SERVER_ADDR'] == '10.0.0.6' OR $_SERVER['SERVER_ADDR'] == '94.140.4.7' OR $_SERVER['SERVER_ADDR'] == '77.72.92.154' OR $_SERVER['SERVER_ADDR'] == '2a01:240:ab04::11' OR $_SERVER['SERVER_ADDR'] == '2001:978:2:61::11') { return '94.140.4.7'; } else { return $_SERVER['SERVER_ADDR']; } } elseif (isset($_SERVER['STY'])) { if (strpos($_SERVER['STY'], '.back1') !== false) { return '94.140.4.2'; } elseif (strpos($_SERVER['STY'], '.back2') !== false) { return '94.140.4.3'; } elseif (strpos($_SERVER['STY'], '.back3') !== false) { return '94.140.4.4'; } elseif (strpos($_SERVER['STY'], '.back4') !== false) { return '94.140.4.5'; } elseif (strpos($_SERVER['STY'], '.back5') !== false) { return '94.140.4.6'; } elseif (strpos($_SERVER['STY'], '.back6') !== false) { return '94.140.4.7'; } else { return 'N/A'; } } else { return 'N/A'; } } function get_server_internal_addr() { if (isset($_SERVER['SERVER_ADDR'])) { if ($_SERVER['SERVER_ADDR'] == '94.140.4.2' OR $_SERVER['SERVER_ADDR'] == '77.72.92.149' OR $_SERVER['SERVER_ADDR'] == '149.11.52.93' OR $_SERVER['SERVER_ADDR'] == '149.11.52.93' OR $_SERVER['SERVER_ADDR'] == '2a01:240:ab04::6' OR $_SERVER['SERVER_ADDR'] == '2001:978:2:61::6') { return '10.0.0.1'; } elseif ($_SERVER['SERVER_ADDR'] == '94.140.4.3' OR $_SERVER['SERVER_ADDR'] == '77.72.92.150' OR $_SERVER['SERVER_ADDR'] == '149.11.52.94' OR $_SERVER['SERVER_ADDR'] == '2a01:240:ab04::7' OR $_SERVER['SERVER_ADDR'] == '2001:978:2:61::7') { return '10.0.0.2'; } elseif ($_SERVER['SERVER_ADDR'] == '94.140.4.4' OR $_SERVER['SERVER_ADDR'] == '77.72.92.151' OR $_SERVER['SERVER_ADDR'] == '2a01:240:ab04::8' OR $_SERVER['SERVER_ADDR'] == '2001:978:2:61::8') { return '10.0.0.3'; } elseif ($_SERVER['SERVER_ADDR'] == '94.140.4.5' OR $_SERVER['SERVER_ADDR'] == '77.72.92.152' OR $_SERVER['SERVER_ADDR'] == '2a01:240:ab04::9' OR $_SERVER['SERVER_ADDR'] == '2001:978:2:61::9') { return '10.0.0.4'; } elseif ($_SERVER['SERVER_ADDR'] == '94.140.4.6' OR $_SERVER['SERVER_ADDR'] == '77.72.92.153' OR $_SERVER['SERVER_ADDR'] == '2a01:240:ab04::10' OR $_SERVER['SERVER_ADDR'] == '2001:978:2:61::10') { return '10.0.0.5'; } elseif ($_SERVER['SERVER_ADDR'] == '94.140.4.7' OR $_SERVER['SERVER_ADDR'] == '77.72.92.154' OR $_SERVER['SERVER_ADDR'] == '2a01:240:ab04::11' OR $_SERVER['SERVER_ADDR'] == '2001:978:2:61::11') { return '10.0.0.6'; } else { return $_SERVER['SERVER_ADDR']; } } elseif (isset($_SERVER['STY'])) { if (strpos($_SERVER['STY'], '.back1') !== false) { return '10.0.0.1'; } elseif (strpos($_SERVER['STY'], '.back2') !== false) { return '10.0.0.2'; } elseif (strpos($_SERVER['STY'], '.back3') !== false) { return '10.0.0.3'; } elseif (strpos($_SERVER['STY'], '.back4') !== false) { return '10.0.0.4'; } elseif (strpos($_SERVER['STY'], '.back5') !== false) { return '10.0.0.5'; } elseif (strpos($_SERVER['STY'], '.back6') !== false) { return '10.0.0.6'; } elseif (strpos($_SERVER['STY'], '.backend-sos') !== false) { return '10.10.1.1'; } else { return 'N/A'; } } else { return 'N/A'; } } function get_server_name($full = false) { if (get_server_addr() == '94.140.4.2' OR (isset($_SERVER['STY']) AND strpos($_SERVER['STY'], '.back1') !== false)) { if ($full) { return 'back-1'; } else { return 'B1'; } } elseif (get_server_addr() == '94.140.4.3' OR (isset($_SERVER['STY']) AND strpos($_SERVER['STY'], '.back2') !== false)) { if ($full) { return 'back-2'; } else { return 'B2'; } } elseif (get_server_addr() == '94.140.4.4' OR (isset($_SERVER['STY']) AND strpos($_SERVER['STY'], '.back3') !== false)) { if ($full) { return 'back-3'; } else { return 'B3'; } } elseif (get_server_addr() == '94.140.4.5' OR (isset($_SERVER['STY']) AND strpos($_SERVER['STY'], '.back4') !== false)) { if ($full) { return 'back-4'; } else { return 'B4'; } } elseif (get_server_addr() == '94.140.4.6' OR (isset($_SERVER['STY']) AND strpos($_SERVER['STY'], '.back5') !== false)) { if ($full) { return 'back-5'; } else { return 'B5'; } } elseif (get_server_addr() == '94.140.4.7' OR (isset($_SERVER['STY']) AND strpos($_SERVER['STY'], '.back6') !== false)) { if ($full) { return 'back-6'; } else { return 'B6'; } } else { return 'n/a'; } } function rand_out_ip($list = 0) { if (get_server_addr() == '94.140.4.2' OR (isset($_SERVER['STY']) AND strpos($_SERVER['STY'], '.back1') !== false)) { $rand_ips = array("94.140.4.154", "94.140.4.155", "94.140.4.156", "94.140.4.157", "94.140.4.158", "94.140.4.159", "94.140.4.160", "94.140.4.161", "94.140.4.162", "94.140.4.163", "94.140.4.40", "94.140.4.41", "94.140.4.42", "94.140.4.43", "94.140.4.44", "94.140.4.45", "94.140.4.46", "94.140.4.47", "94.140.4.48", "94.140.4.49", "94.140.4.50", "94.140.4.51", "94.140.4.52", "94.140.4.53", "94.140.4.54", "94.140.4.55", "94.140.4.56", "94.140.4.57", "94.140.4.58", "94.140.4.59", "94.140.4.60"); } elseif (get_server_addr() == '94.140.4.3' OR (isset($_SERVER['STY']) AND strpos($_SERVER['STY'], '.back2') !== false)) { $rand_ips = array("94.140.4.61", "94.140.4.62", "94.140.4.63", "94.140.4.64", "94.140.4.65", "94.140.4.66", "94.140.4.67", "94.140.4.68", "94.140.4.69", "94.140.4.70", "94.140.4.71", "94.140.4.72", "94.140.4.73", "94.140.4.74", "94.140.4.75", "94.140.4.76", "94.140.4.77", "94.140.4.78", "94.140.4.79", "94.140.4.80", "94.140.4.81", "94.140.4.82", "94.140.4.83", "94.140.4.84", "94.140.4.85", "94.140.4.86", "94.140.4.87", "94.140.4.88", "94.140.4.89", "94.140.4.90", "94.140.4.91"); } elseif (get_server_addr() == '94.140.4.4' OR (isset($_SERVER['STY']) AND strpos($_SERVER['STY'], '.back3') !== false)) { $rand_ips = array("94.140.4.92", "94.140.4.93", "94.140.4.94", "94.140.4.95", "94.140.4.96", "94.140.4.97", "94.140.4.98", "94.140.4.99", "94.140.4.100", "94.140.4.101", "94.140.4.102", "94.140.4.103", "94.140.4.104", "94.140.4.105", "94.140.4.106", "94.140.4.107", "94.140.4.108", "94.140.4.109", "94.140.4.110", "94.140.4.111", "94.140.4.112", "94.140.4.113", "94.140.4.114", "94.140.4.115", "94.140.4.116", "94.140.4.117", "94.140.4.118", "94.140.4.119", "94.140.4.120", "94.140.4.121", "94.140.4.122"); } elseif (get_server_addr() == '94.140.4.5' OR (isset($_SERVER['STY']) AND strpos($_SERVER['STY'], '.back4') !== false)) { $rand_ips = array("94.140.4.123", "94.140.4.124", "94.140.4.125", "94.140.4.126", "94.140.4.127", "94.140.4.128", "94.140.4.129", "94.140.4.130", "94.140.4.131", "94.140.4.132", "94.140.4.133", "94.140.4.134", "94.140.4.135", "94.140.4.136", "94.140.4.137", "94.140.4.138", "94.140.4.139", "94.140.4.140", "94.140.4.141", "94.140.4.142", "94.140.4.143", "94.140.4.144", "94.140.4.145", "94.140.4.146", "94.140.4.147", "94.140.4.148", "94.140.4.149", "94.140.4.150", "94.140.4.151", "94.140.4.152", "94.140.4.153"); } elseif (get_server_addr() == '94.140.4.6' OR (isset($_SERVER['STY']) AND strpos($_SERVER['STY'], '.back5') !== false)) { $rand_ips = array("94.140.4.194", "94.140.4.164", "94.140.4.165", "94.140.4.166", "94.140.4.167", "94.140.4.168", "94.140.4.169", "94.140.4.170", "94.140.4.171", "94.140.4.172", "94.140.4.173", "94.140.4.174", "94.140.4.175", "94.140.4.176", "94.140.4.177", "94.140.4.178", "94.140.4.179", "94.140.4.180", "94.140.4.181", "94.140.4.182", "94.140.4.183", "94.140.4.184", "94.140.4.185", "94.140.4.186", "94.140.4.187", "94.140.4.188", "94.140.4.189", "94.140.4.190", "94.140.4.191", "94.140.4.192", "94.140.4.193"); } elseif (get_server_addr() == '94.140.4.7' OR (isset($_SERVER['STY']) AND strpos($_SERVER['STY'], '.back6') !== false)) { $rand_ips = array("94.140.4.195", "94.140.4.196", "94.140.4.197", "94.140.4.198", "94.140.4.199", "94.140.4.200", "94.140.4.201", "94.140.4.202", "94.140.4.203", "94.140.4.204", "94.140.4.205", "94.140.4.206", "94.140.4.207", "94.140.4.208", "94.140.4.209", "94.140.4.210", "94.140.4.211", "94.140.4.212", "94.140.4.213", "94.140.4.214", "94.140.4.215", "94.140.4.216", "94.140.4.217", "94.140.4.218", "94.140.4.219", "94.140.4.220", "94.140.4.221", "94.140.4.222", "94.140.4.223", "94.140.4.224", "94.140.4.225"); } else { return ''; } if ($list == 0) { $nbIPs = count($rand_ips); if ($nbIPs > 0) { return $rand_ips[mt_rand(0, $nbIPs - 1)]; } else { return ''; } } else { return $rand_ips; } } function recursive_mkdir($path, $mode = 0775) { $dirs = explode('/', $path); $count = count($dirs); $path = ''; for ($i = 0; $i < $count; ++$i) { $path .= '/' . $dirs[$i]; if (!is_dir($path)) { mkdir($path, $mode); chmod($path, $mode); } } return true; } function valid_ip_address($ip) { $pattern1 = '([A-Fa-f0-9]{1,4}:){7}[A-Fa-f0-9]{1,4}'; $pattern2 = '[A-Fa-f0-9]{1,4}::([A-Fa-f0-9]{1,4}:){0,5}[A-Fa-f0-9]{1,4}'; $pattern3 = '([A-Fa-f0-9]{1,4}:){2}:([A-Fa-f0-9]{1,4}:){0,4}[A-Fa-f0-9]{1,4}'; $pattern4 = '([A-Fa-f0-9]{1,4}:){3}:([A-Fa-f0-9]{1,4}:){0,3}[A-Fa-f0-9]{1,4}'; $pattern5 = '([A-Fa-f0-9]{1,4}:){4}:([A-Fa-f0-9]{1,4}:){0,2}[A-Fa-f0-9]{1,4}'; $pattern6 = '([A-Fa-f0-9]{1,4}:){5}:([A-Fa-f0-9]{1,4}:){0,1}[A-Fa-f0-9]{1,4}'; $pattern7 = '([A-Fa-f0-9]{1,4}:){6}:[A-Fa-f0-9]{1,4}'; $pattern8 = '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'; $full = "/^(".$pattern1.")$|^(".$pattern2.")$|^(".$pattern3.")$|^(".$pattern4.")$|^(".$pattern5.")$|^(".$pattern6.")$|^(".$pattern7.")$|^(".$pattern8.")$/"; if (!preg_match($full, $ip)) return false; return true; } /** * PHP 5 compatible, regex-only detection of reserved/special IPs (IPv4 + IPv6). * Covers common RFC ranges used in practice. * * IPv4: * - 10.0.0.0/8 * - 172.16.0.0/12 * - 192.168.0.0/16 * - 127.0.0.0/8 (loopback) * - 169.254.0.0/16 (link-local) * - 224.0.0.0/4 (multicast) * - 240.0.0.0/4 (reserved/future) + 255.255.255.255 * - 0.0.0.0/8 (unspecified block) * * IPv6 (prefix checks; pragmatic, not a full validator): * - :: (unspecified) * - ::1 (loopback) * - fc00::/7 (ULA: fcxx::/ fdxx::/) * - fe80::/10 (link-local: fe8x..febx) * - ff00::/8 (multicast) * - 2001:db8::/32 (documentation) * - 100::/64 (discard prefix) * - ::ffff:0:0/96 (IPv4-mapped forms) */ function is_reserved_ip($ip) { if (is_reserved_ipv4($ip)) { return true; } // Lowercase once for simpler IPv6 prefix checks return is_reserved_ipv6(strtolower($ip)); } function is_reserved_ipv4($ip) { // IPv4 validation (regex only) if (!preg_match('/^(25[0-5]|2[0-4]\d|1?\d{1,2})(\.(25[0-5]|2[0-4]\d|1?\d{1,2})){3}$/', $ip)) { return false; // not IPv4 } $parts = explode('.', $ip); $a = (int)$parts[0]; $b = (int)$parts[1]; if ($a === 10) return true; // 10/8 if ($a === 127) return true; // 127/8 loopback if ($a === 169 && $b === 254) return true; // 169.254/16 link-local if ($a === 192 && $b === 168) return true; // 192.168/16 if ($a === 172 && $b >= 16 && $b <= 31) return true; // 172.16/12 if ($a >= 224 && $a <= 239) return true; // 224/4 multicast if ($a >= 240 && $a <= 255) return true; // 240/4 + broadcast if ($a === 0) return true; // 0/8 (unspecified block) return false; } function is_reserved_ipv6($s) { // Quick reject if it doesn't look like IPv6 text if (strpos($s, ':') === false) { return false; } // Exact specials if ($s === '::' || $s === '::1') { return true; // unspecified or loopback } // Unique Local (fc00::/7 -> fcxx or fdxx) if (preg_match('/^f[cd][0-9a-f]{2}:/i', $s)) { return true; } // Link-local (fe80::/10 -> fe8x..febx) if (preg_match('/^fe[89ab][0-9a-f]:/i', $s)) { return true; } // Multicast (ff00::/8) if (preg_match('/^ff[0-9a-f]{2}:/i', $s)) { return true; } // Documentation (2001:db8::/32) if (preg_match('/^2001:db8(?::|::)/i', $s)) { return true; } // Discard prefix (100::/64) — common textual form starts with "100:" if (preg_match('/^100:/', $s)) { return true; } // IPv4-mapped (::ffff:0:0/96) — common textual forms if (preg_match('/^::ffff:(?:\d{1,3}\.){3}\d{1,3}$/', $s)) { return true; // ::ffff:W.X.Y.Z } // Permissive alternative mapped/compressed forms if (preg_match('/^(?:0*:)*:ffff:(?:[0-9a-f]{1,4}:){1,2}[0-9a-f]{1,4}$/i', $s)) { return true; } return false; } function base64urldecode($data) { $data .= substr('==', (2 - strlen($data) * 3) % 4); $data = str_replace(array('-', '_', ','), array('+', '/', ''), $data); return base64_decode($data); } function base64urlencode($data) { return str_replace(array('+', '/', '='), array('-', '_', ''), base64_encode($data)); } class Browser { private $_agent = ''; private $_browser_name = ''; private $_version = ''; private $_platform = ''; private $_os = ''; private $_is_aol = false; private $_is_mobile = false; private $_is_tablet = false; private $_is_robot = false; private $_is_facebook = false; private $_aol_version = ''; const BROWSER_UNKNOWN = 'unknown'; const VERSION_UNKNOWN = 'unknown'; const BROWSER_OPERA = 'Opera'; // http://www.opera.com/ const BROWSER_OPERA_MINI = 'Opera Mini'; // http://www.opera.com/mini/ const BROWSER_WEBTV = 'WebTV'; // http://www.webtv.net/pc/ const BROWSER_IE = 'Internet Explorer'; // http://www.microsoft.com/ie/ const BROWSER_IEMOBILE = 'Pocket Internet Explorer'; // http://en.wikipedia.org/wiki/Internet_Explorer_Mobile const BROWSER_KONQUEROR = 'Konqueror'; // http://www.konqueror.org/ const BROWSER_ICAB = 'iCab'; // http://www.icab.de/ const BROWSER_OMNIWEB = 'OmniWeb'; // http://www.omnigroup.com/applications/omniweb/ const BROWSER_FIREBIRD = 'Firebird'; // http://www.ibphoenix.com/ const BROWSER_FIREFOX = 'Firefox'; // http://www.mozilla.com/en-US/firefox/firefox.html const BROWSER_ICEWEASEL = 'Iceweasel'; // http://www.geticeweasel.org/ const BROWSER_SHIRETOKO = 'Shiretoko'; // http://wiki.mozilla.org/Projects/shiretoko const BROWSER_MOZILLA = 'Mozilla'; // http://www.mozilla.com/en-US/ const BROWSER_AMAYA = 'Amaya'; // http://www.w3.org/Amaya/ const BROWSER_LYNX = 'Lynx'; // http://en.wikipedia.org/wiki/Lynx const BROWSER_SAFARI = 'Safari'; // http://apple.com const BROWSER_IPHONE = 'iPhone'; // http://apple.com const BROWSER_IPOD = 'iPod'; // http://apple.com const BROWSER_IPAD = 'iPad'; // http://apple.com const BROWSER_CHROME = 'Chrome'; // http://www.google.com/chrome const BROWSER_ANDROID = 'Android'; // http://www.android.com/ const BROWSER_GOOGLEBOT = 'GoogleBot'; // http://en.wikipedia.org/wiki/Googlebot const BROWSER_SLURP = 'Yahoo! Slurp'; // http://en.wikipedia.org/wiki/Yahoo!_Slurp const BROWSER_W3CVALIDATOR = 'W3C Validator'; // http://validator.w3.org/ const BROWSER_BLACKBERRY = 'BlackBerry'; // http://www.blackberry.com/ const BROWSER_ICECAT = 'IceCat'; // http://en.wikipedia.org/wiki/GNU_IceCat const BROWSER_NOKIA_S60 = 'Nokia S60 OSS Browser'; // http://en.wikipedia.org/wiki/Web_Browser_for_S60 const BROWSER_NOKIA = 'Nokia Browser'; // * all other WAP-based browsers on the Nokia Platform const BROWSER_MSN = 'MSN Browser'; // http://explorer.msn.com/ const BROWSER_MSNBOT = 'MSN Bot'; // http://search.msn.com/msnbot.htm const BROWSER_BINGBOT = 'Bing Bot'; // http://en.wikipedia.org/wiki/Bingbot const BROWSER_JDOWNLOADER = 'JDownloader'; const BROWSER_SYNOLOGY = 'Synology'; const BROWSER_KODI = 'Kodi'; const BROWSER_VLC = 'VLC'; const BROWSER_STREMIO = 'Stremio'; const BROWSER_SAMSUNG = 'Samsung Browser'; const BROWSER_KSPLAYER = 'KSPlayer'; const BROWSER_ANDROID_APP = 'App for Android'; const BROWSER_WEBOS = 'LG webOS'; const BROWSER_ROKU = 'Roku'; const BROWSER_FFMPEG = 'FFmpeg'; const BROWSER_NETSCAPE_NAVIGATOR = 'Netscape Navigator'; // http://browser.netscape.com/ (DEPRECATED) const BROWSER_GALEON = 'Galeon'; // http://galeon.sourceforge.net/ (DEPRECATED) const BROWSER_NETPOSITIVE = 'NetPositive'; // http://en.wikipedia.org/wiki/NetPositive (DEPRECATED) const BROWSER_PHOENIX = 'Phoenix'; // http://en.wikipedia.org/wiki/History_of_Mozilla_Firefox (DEPRECATED) const PLATFORM_UNKNOWN = 'unknown'; const PLATFORM_WINDOWS = 'Windows'; const PLATFORM_WINDOWS_CE = 'Windows CE'; const PLATFORM_APPLE = 'Apple'; const PLATFORM_LINUX = 'Linux'; const PLATFORM_OS2 = 'OS/2'; const PLATFORM_BEOS = 'BeOS'; const PLATFORM_IPHONE = 'iPhone'; const PLATFORM_IPOD = 'iPod'; const PLATFORM_IPAD = 'iPad'; const PLATFORM_BLACKBERRY = 'BlackBerry'; const PLATFORM_NOKIA = 'Nokia'; const PLATFORM_FREEBSD = 'FreeBSD'; const PLATFORM_OPENBSD = 'OpenBSD'; const PLATFORM_NETBSD = 'NetBSD'; const PLATFORM_SUNOS = 'SunOS'; const PLATFORM_OPENSOLARIS = 'OpenSolaris'; const PLATFORM_ANDROID = 'Android'; const PLATFORM_WEBOS = 'LG webOS TV'; const PLATFORM_ROKU = 'Roku'; const OPERATING_SYSTEM_UNKNOWN = 'unknown'; public function __construct($userAgent = "") { $this->reset(); if ($userAgent != "") { $this->setUserAgent($userAgent); } else { $this->determine(); } } /** * Reset all properties */ public function reset() { $this->_agent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : ""; $this->_browser_name = self::BROWSER_UNKNOWN; $this->_version = self::VERSION_UNKNOWN; $this->_platform = self::PLATFORM_UNKNOWN; $this->_os = self::OPERATING_SYSTEM_UNKNOWN; $this->_is_aol = false; $this->_is_mobile = false; $this->_is_tablet = false; $this->_is_robot = false; $this->_is_facebook = false; $this->_aol_version = self::VERSION_UNKNOWN; } /** * Check to see if the specific browser is valid * @param string $browserName * @return bool True if the browser is the specified browser */ function isBrowser($browserName) { return (0 == strcasecmp($this->_browser_name, trim($browserName))); } /** * The name of the browser. All return types are from the class contants * @return string Name of the browser */ public function getBrowser() { return $this->_browser_name; } /** * Set the name of the browser * @param $browser string The name of the Browser */ public function setBrowser($browser) { $this->_browser_name = $browser; } /** * The name of the platform. All return types are from the class contants * @return string Name of the browser */ public function getPlatform() { return $this->_platform; } /** * Set the name of the platform * @param string $platform The name of the Platform */ public function setPlatform($platform) { $this->_platform = $platform; } /** * The version of the browser. * @return string Version of the browser (will only contain alpha-numeric characters and a period) */ public function getVersion() { return $this->_version; } /** * Set the version of the browser * @param string $version The version of the Browser */ public function setVersion($version) { $this->_version = preg_replace('/[^0-9,.,a-z,A-Z-]/', '', $version); } /** * The version of AOL. * @return string Version of AOL (will only contain alpha-numeric characters and a period) */ public function getAolVersion() { return $this->_aol_version; } /** * Set the version of AOL * @param string $version The version of AOL */ public function setAolVersion($version) { $this->_aol_version = preg_replace('/[^0-9,.,a-z,A-Z]/', '', $version); } /** * Is the browser from AOL? * @return boolean True if the browser is from AOL otherwise false */ public function isAol() { return $this->_is_aol; } /** * Is the browser from a mobile device? * @return boolean True if the browser is from a mobile device otherwise false */ public function isMobile() { return $this->_is_mobile; } /** * Is the browser from a tablet device? * @return boolean True if the browser is from a tablet device otherwise false */ public function isTablet() { return $this->_is_tablet; } /** * Is the browser from a robot (ex Slurp,GoogleBot)? * @return boolean True if the browser is from a robot otherwise false */ public function isRobot() { return $this->_is_robot; } /** * Is the browser from facebook? * @return boolean True if the browser is from facebook otherwise false */ public function isFacebook() { return $this->_is_facebook; } /** * Set the browser to be from AOL * @param $isAol */ public function setAol($isAol) { $this->_is_aol = $isAol; } /** * Set the Browser to be mobile * @param boolean $value is the browser a mobile browser or not */ protected function setMobile($value = true) { $this->_is_mobile = $value; } /** * Set the Browser to be tablet * @param boolean $value is the browser a tablet browser or not */ protected function setTablet($value = true) { $this->_is_tablet = $value; } /** * Set the Browser to be a robot * @param boolean $value is the browser a robot or not */ protected function setRobot($value = true) { $this->_is_robot = $value; } /** * Set the Browser to be a Facebook request * @param boolean $value is the browser a robot or not */ protected function setFacebook($value = true) { $this->_is_facebook = $value; } /** * Get the user agent value in use to determine the browser * @return string The user agent from the HTTP header */ public function getUserAgent() { return $this->_agent; } /** * Set the user agent value (the construction will use the HTTP header value - this will overwrite it) * @param string $agent_string The value for the User Agent */ public function setUserAgent($agent_string) { $this->reset(); $this->_agent = $agent_string; $this->determine(); } /** * Used to determine if the browser is actually "chromeframe" * @since 1.7 * @return boolean True if the browser is using chromeframe */ public function isChromeFrame() { return (strpos($this->_agent, "chromeframe") !== false); } /** * Returns a formatted string with a summary of the details of the browser. * @return string formatted string with a summary of the browser */ public function __toString() { return "Browser Name: {$this->getBrowser()}
\n" . "Browser Version: {$this->getVersion()}
\n" . "Browser User Agent String: {$this->getUserAgent()}
\n" . "Platform: {$this->getPlatform()}
"; } /** * Protected routine to calculate and determine what the browser is in use (including platform) */ protected function determine() { $this->checkPlatform(); $this->checkBrowsers(); $this->checkForAol(); } /** * Protected routine to determine the browser type * @return boolean True if the browser was detected otherwise false */ protected function checkBrowsers() { return ( // well-known, well-used // Special Notes: // (1) Opera must be checked before FireFox due to the odd // user agents used in some older versions of Opera // (2) WebTV is strapped onto Internet Explorer so we must // check for WebTV before IE // (3) (deprecated) Galeon is based on Firefox and needs to be // tested before Firefox is tested // (4) OmniWeb is based on Safari so OmniWeb check must occur // before Safari // (5) Netscape 9+ is based on Firefox so Netscape checks // before FireFox are necessary $this->checkBrowserWebTv() || $this->checkBrowserKodi() || $this->checkBrowserWebOS() || $this->checkBrowserExoMedia() || $this->checkBrowserAndroidApp() || $this->checkBrowserInternetExplorer() || $this->checkBrowserOpera() || $this->checkBrowserGaleon() || $this->checkBrowserNetscapeNavigator9Plus() || $this->checkBrowserFirefox() || $this->checkBrowserChrome() || $this->checkBrowserOmniWeb() || $this->checkBrowserJDownloader() || $this->checkBrowserSynology() || $this->checkBrowserSamsungAgent() || $this->checkBrowserVLC() || $this->checkBrowserLavf() || $this->checkBrowserLibmpv() || $this->checkBrowserKSPlayer() || $this->checkBrowserRoku() || // common mobile $this->checkBrowserAndroid() || $this->checkBrowseriPad() || $this->checkBrowseriPod() || $this->checkBrowseriPhone() || $this->checkBrowserBlackBerry() || $this->checkBrowserNokia() || // common bots $this->checkBrowserGoogleBot() || $this->checkBrowserMSNBot() || $this->checkBrowserBingBot() || $this->checkBrowserSlurp() || // check for facebook external hit when loading URL $this->checkFacebookExternalHit() || // WebKit base check (post mobile and others) $this->checkBrowserSafari() || // everyone else $this->checkBrowserNetPositive() || $this->checkBrowserFirebird() || $this->checkBrowserKonqueror() || $this->checkBrowserIcab() || $this->checkBrowserPhoenix() || $this->checkBrowserAmaya() || $this->checkBrowserLynx() || $this->checkBrowserShiretoko() || $this->checkBrowserIceCat() || $this->checkBrowserIceweasel() || $this->checkBrowserW3CValidator() || $this->checkBrowserMozilla() || /* Mozilla is such an open standard that you must check it last */ $this->checkGeneral() ); } /** * Determine if the user is using a BlackBerry (last updated 1.7) * @return boolean True if the browser is the BlackBerry browser otherwise false */ protected function checkBrowserBlackBerry() { if (stripos($this->_agent, 'blackberry') !== false) { $aresult = explode("/", stristr($this->_agent, "BlackBerry")); if (isset($aresult[1])) { $aversion = explode(' ', $aresult[1]); $this->setVersion($aversion[0]); $this->_browser_name = self::BROWSER_BLACKBERRY; $this->setMobile(true); return true; } } return false; } /** * Determine if the user is using a BlackBerry (last updated 1.7) * @return boolean True if the browser is the BlackBerry browser otherwise false */ protected function checkBrowserJDownloader() { if (stripos($this->_agent, 'JDownloader') !== false) { $aresult = explode("/", stristr($this->_agent, "JDownloader")); if (isset($aresult[1])) { $aversion = explode(' ', $aresult[1]); $this->setVersion($aversion[0]); $this->_browser_name = self::BROWSER_JDOWNLOADER; return true; } else { $this->setVersion("2.0"); $this->_browser_name = self::BROWSER_JDOWNLOADER; return true; } } return false; } protected function checkBrowserKodi() { if (stripos($this->_agent, 'Kodi') === false) { return false; } // Browser name $this->_browser_name = self::BROWSER_KODI; // Kodi version: prefer "Kodi/", fallback to "Version/" if (preg_match('/Kodi\/([^\s]+)/i', $this->_agent, $m)) { $this->setVersion(preg_replace('/^v/i', '', $m[1])); } elseif (preg_match('/Version\/([^\s]+)/i', $this->_agent, $m)) { $this->setVersion($m[1]); } else { $this->setVersion(self::VERSION_UNKNOWN); } // Android device model as platform: between "Android ;" and "Build/" if ( stripos($this->_agent, 'Android') !== false && preg_match('/Android\s+[0-9.]+;\s*(.+?)\s+Build\//i', $this->_agent, $m) ) { $model = trim($m[1]); if (method_exists($this, 'normalizeAndroidDeviceModel')) { $model = $this->normalizeAndroidDeviceModel($model); } $this->setPlatform($model); } elseif (stripos($this->_agent, 'Linux') !== false && $this->getPlatform() === self::PLATFORM_UNKNOWN) { // Non-Android Kodi on Linux $this->setPlatform(self::PLATFORM_LINUX); } // Heuristics: Android TV devices aren’t phones/tablets if (stripos($this->_agent, 'Android TV') !== false) { $this->setMobile(false); $this->setTablet(false); } return true; } protected function checkBrowserSynology() { if (stripos($this->_agent, 'Synology') !== false) { $aresult = explode("/", stristr($this->_agent, "Synology")); $this->setVersion("0.1"); $this->_browser_name = self::BROWSER_SYNOLOGY; return true; } return false; } /** * Determine if the user is using an AOL User Agent (last updated 1.7) * @return boolean True if the browser is from AOL otherwise false */ protected function checkForAol() { $this->setAol(false); $this->setAolVersion(self::VERSION_UNKNOWN); if (stripos($this->_agent, 'aol') !== false) { $aversion = explode(' ', stristr($this->_agent, 'AOL')); if (isset($aversion[1])) { $this->setAol(true); $this->setAolVersion(preg_replace('/[^0-9\.a-z]/i', '', $aversion[1])); return true; } } return false; } /** * Determine if the browser is the GoogleBot or not (last updated 1.7) * @return boolean True if the browser is the GoogletBot otherwise false */ protected function checkBrowserGoogleBot() { if (stripos($this->_agent, 'googlebot') !== false) { $aresult = explode('/', stristr($this->_agent, 'googlebot')); if (isset($aresult[1])) { $aversion = explode(' ', $aresult[1]); $this->setVersion(str_replace(';', '', $aversion[0])); $this->_browser_name = self::BROWSER_GOOGLEBOT; $this->setRobot(true); return true; } } return false; } /** * Determine if the browser is the MSNBot or not (last updated 1.9) * @return boolean True if the browser is the MSNBot otherwise false */ protected function checkBrowserMSNBot() { if (stripos($this->_agent, "msnbot") !== false) { $aresult = explode("/", stristr($this->_agent, "msnbot")); if (isset($aresult[1])) { $aversion = explode(" ", $aresult[1]); $this->setVersion(str_replace(";", "", $aversion[0])); $this->_browser_name = self::BROWSER_MSNBOT; $this->setRobot(true); return true; } } return false; } /** * Determine if the browser is the BingBot or not (last updated 1.9) * @return boolean True if the browser is the BingBot otherwise false */ protected function checkBrowserBingBot() { if (stripos($this->_agent, "bingbot") !== false) { $aresult = explode("/", stristr($this->_agent, "bingbot")); if (isset($aresult[1])) { $aversion = explode(" ", $aresult[1]); $this->setVersion(str_replace(";", "", $aversion[0])); $this->_browser_name = self::BROWSER_BINGBOT; $this->setRobot(true); return true; } } return false; } /** * Determine if the browser is the W3C Validator or not (last updated 1.7) * @return boolean True if the browser is the W3C Validator otherwise false */ protected function checkBrowserW3CValidator() { if (stripos($this->_agent, 'W3C-checklink') !== false) { $aresult = explode('/', stristr($this->_agent, 'W3C-checklink')); if (isset($aresult[1])) { $aversion = explode(' ', $aresult[1]); $this->setVersion($aversion[0]); $this->_browser_name = self::BROWSER_W3CVALIDATOR; return true; } } else if (stripos($this->_agent, 'W3C_Validator') !== false) { // Some of the Validator versions do not delineate w/ a slash - add it back in $ua = str_replace("W3C_Validator ", "W3C_Validator/", $this->_agent); $aresult = explode('/', stristr($ua, 'W3C_Validator')); if (isset($aresult[1])) { $aversion = explode(' ', $aresult[1]); $this->setVersion($aversion[0]); $this->_browser_name = self::BROWSER_W3CVALIDATOR; return true; } } else if (stripos($this->_agent, 'W3C-mobileOK') !== false) { $this->_browser_name = self::BROWSER_W3CVALIDATOR; $this->setMobile(true); return true; } return false; } /** * Determine if the browser is the Yahoo! Slurp Robot or not (last updated 1.7) * @return boolean True if the browser is the Yahoo! Slurp Robot otherwise false */ protected function checkBrowserSlurp() { if (stripos($this->_agent, 'slurp') !== false) { $aresult = explode('/', stristr($this->_agent, 'Slurp')); if (isset($aresult[1])) { $aversion = explode(' ', $aresult[1]); $this->setVersion($aversion[0]); $this->_browser_name = self::BROWSER_SLURP; $this->setRobot(true); $this->setMobile(false); return true; } } return false; } /** * Determine if the browser is Internet Explorer or not (last updated 1.7) * @return boolean True if the browser is Internet Explorer otherwise false */ protected function checkBrowserInternetExplorer() { // Test for IE11 if ( stripos($this->_agent,'Trident/7.0; rv:11.0') !== false ) { $this->setBrowser(self::BROWSER_IE); $this->setVersion('11.0'); return true; } // Test for v1 - v1.5 IE else if (stripos($this->_agent, 'microsoft internet explorer') !== false) { $this->setBrowser(self::BROWSER_IE); $this->setVersion('1.0'); $aresult = stristr($this->_agent, '/'); if (preg_match('/308|425|426|474|0b1/i', $aresult)) { $this->setVersion('1.5'); } return true; } // Test for versions > 1.5 else if (stripos($this->_agent, 'msie') !== false && stripos($this->_agent, 'opera') === false) { // See if the browser is the odd MSN Explorer if (stripos($this->_agent, 'msnb') !== false) { $aresult = explode(' ', stristr(str_replace(';', '; ', $this->_agent), 'MSN')); if (isset($aresult[1])) { $this->setBrowser(self::BROWSER_MSN); $this->setVersion(str_replace(array('(', ')', ';'), '', $aresult[1])); return true; } } $aresult = explode(' ', stristr(str_replace(';', '; ', $this->_agent), 'msie')); if (isset($aresult[1])) { $this->setBrowser(self::BROWSER_IE); $this->setVersion(str_replace(array('(', ')', ';'), '', $aresult[1])); if (stripos($this->_agent, 'IEMobile') !== false) { $this->setBrowser(self::BROWSER_IEMOBILE); $this->setMobile(true); } return true; } } // Test for versions > IE 10 else if (stripos($this->_agent, 'trident') !== false) { $this->setBrowser(self::BROWSER_IE); $result = explode('rv:', $this->_agent); if (isset($result[1])) { $this->setVersion(preg_replace('/[^0-9.]+/', '', $result[1])); $this->_agent = str_replace(array("Mozilla", "Gecko"), "MSIE", $this->_agent); } } // Test for Pocket IE else if (stripos($this->_agent, 'IEMobile') !== false || stripos($this->_agent, 'pocket') !== false) { $aresult = explode(' ', stristr($this->_agent, 'IEMobile')); if (isset($aresult[1])) { $this->setPlatform(self::PLATFORM_WINDOWS_CE); $this->setBrowser(self::BROWSER_IEMOBILE); $this->setMobile(true); if (stripos($this->_agent, 'mspie') !== false) { $this->setVersion($aresult[1]); } else { $aversion = explode('/', $this->_agent); if (isset($aversion[1])) { $this->setVersion($aversion[1]); } } return true; } } return false; } /** * Determine if the browser is Opera or not (last updated 1.7) * @return boolean True if the browser is Opera otherwise false */ protected function checkBrowserOpera() { if (stripos($this->_agent, 'opera mini') !== false) { $resultant = stristr($this->_agent, 'opera mini'); if (preg_match('/\//', $resultant)) { $aresult = explode('/', $resultant); if (isset($aresult[1])) { $aversion = explode(' ', $aresult[1]); $this->setVersion($aversion[0]); } } else { $aversion = explode(' ', stristr($resultant, 'opera mini')); if (isset($aversion[1])) { $this->setVersion($aversion[1]); } } $this->_browser_name = self::BROWSER_OPERA_MINI; $this->setMobile(true); return true; } else if (stripos($this->_agent, 'opera') !== false) { $resultant = stristr($this->_agent, 'opera'); if (preg_match('/Version\/(1*.*)$/', $resultant, $matches)) { $this->setVersion($matches[1]); } else if (preg_match('/\//', $resultant)) { $aresult = explode('/', str_replace("(", " ", $resultant)); if (isset($aresult[1])) { $aversion = explode(' ', $aresult[1]); $this->setVersion($aversion[0]); } } else { $aversion = explode(' ', stristr($resultant, 'opera')); $this->setVersion(isset($aversion[1]) ? $aversion[1] : ""); } if (stripos($this->_agent, 'Opera Mobi') !== false) { $this->setMobile(true); } $this->_browser_name = self::BROWSER_OPERA; return true; } else if (stripos($this->_agent, 'OPR') !== false) { $resultant = stristr($this->_agent, 'OPR'); if (preg_match('/\//', $resultant)) { $aresult = explode('/', str_replace("(", " ", $resultant)); if (isset($aresult[1])) { $aversion = explode(' ', $aresult[1]); $this->setVersion($aversion[0]); } } if (stripos($this->_agent, 'Mobile') !== false) { $this->setMobile(true); } $this->_browser_name = self::BROWSER_OPERA; return true; } return false; } /** * Determine if the browser is Chrome or not (last updated 1.7) * @return boolean True if the browser is Chrome otherwise false */ protected function checkBrowserChrome() { if (stripos($this->_agent, 'Chrome') !== false) { $aresult = explode('/', stristr($this->_agent, 'Chrome')); if (isset($aresult[1])) { $aversion = explode(' ', $aresult[1]); $this->setVersion($aversion[0]); $this->setBrowser(self::BROWSER_CHROME); //Chrome on Android if (stripos($this->_agent, 'Android') !== false) { if (stripos($this->_agent, 'Mobile') !== false) { $this->setMobile(true); } else { $this->setTablet(true); } } return true; } } return false; } /** * Determine if the browser is WebTv or not (last updated 1.7) * @return boolean True if the browser is WebTv otherwise false */ protected function checkBrowserWebTv() { if (stripos($this->_agent, 'webtv') !== false) { $aresult = explode('/', stristr($this->_agent, 'webtv')); if (isset($aresult[1])) { $aversion = explode(' ', $aresult[1]); $this->setVersion($aversion[0]); $this->setBrowser(self::BROWSER_WEBTV); return true; } } return false; } /** * Determine if the browser is NetPositive or not (last updated 1.7) * @return boolean True if the browser is NetPositive otherwise false */ protected function checkBrowserNetPositive() { if (stripos($this->_agent, 'NetPositive') !== false) { $aresult = explode('/', stristr($this->_agent, 'NetPositive')); if (isset($aresult[1])) { $aversion = explode(' ', $aresult[1]); $this->setVersion(str_replace(array('(', ')', ';'), '', $aversion[0])); $this->setBrowser(self::BROWSER_NETPOSITIVE); return true; } } return false; } /** * Determine if the browser is Galeon or not (last updated 1.7) * @return boolean True if the browser is Galeon otherwise false */ protected function checkBrowserGaleon() { if (stripos($this->_agent, 'galeon') !== false) { $aresult = explode(' ', stristr($this->_agent, 'galeon')); $aversion = explode('/', $aresult[0]); if (isset($aversion[1])) { $this->setVersion($aversion[1]); $this->setBrowser(self::BROWSER_GALEON); return true; } } return false; } /** * Determine if the browser is Konqueror or not (last updated 1.7) * @return boolean True if the browser is Konqueror otherwise false */ protected function checkBrowserKonqueror() { if (stripos($this->_agent, 'Konqueror') !== false) { $aresult = explode(' ', stristr($this->_agent, 'Konqueror')); $aversion = explode('/', $aresult[0]); if (isset($aversion[1])) { $this->setVersion($aversion[1]); $this->setBrowser(self::BROWSER_KONQUEROR); return true; } } return false; } /** * Determine if the browser is iCab or not (last updated 1.7) * @return boolean True if the browser is iCab otherwise false */ protected function checkBrowserIcab() { if (stripos($this->_agent, 'icab') !== false) { $aversion = explode(' ', stristr(str_replace('/', ' ', $this->_agent), 'icab')); if (isset($aversion[1])) { $this->setVersion($aversion[1]); $this->setBrowser(self::BROWSER_ICAB); return true; } } return false; } /** * Determine if the browser is OmniWeb or not (last updated 1.7) * @return boolean True if the browser is OmniWeb otherwise false */ protected function checkBrowserOmniWeb() { if (stripos($this->_agent, 'omniweb') !== false) { $aresult = explode('/', stristr($this->_agent, 'omniweb')); $aversion = explode(' ', isset($aresult[1]) ? $aresult[1] : ""); $this->setVersion($aversion[0]); $this->setBrowser(self::BROWSER_OMNIWEB); return true; } return false; } /** * Determine if the browser is Phoenix or not (last updated 1.7) * @return boolean True if the browser is Phoenix otherwise false */ protected function checkBrowserPhoenix() { if (stripos($this->_agent, 'Phoenix') !== false) { $aversion = explode('/', stristr($this->_agent, 'Phoenix')); if (isset($aversion[1])) { $this->setVersion($aversion[1]); $this->setBrowser(self::BROWSER_PHOENIX); return true; } } return false; } /** * Determine if the browser is Firebird or not (last updated 1.7) * @return boolean True if the browser is Firebird otherwise false */ protected function checkBrowserFirebird() { if (stripos($this->_agent, 'Firebird') !== false) { $aversion = explode('/', stristr($this->_agent, 'Firebird')); if (isset($aversion[1])) { $this->setVersion($aversion[1]); $this->setBrowser(self::BROWSER_FIREBIRD); return true; } } return false; } /** * Determine if the browser is Netscape Navigator 9+ or not (last updated 1.7) * NOTE: (http://browser.netscape.com/ - Official support ended on March 1st, 2008) * @return boolean True if the browser is Netscape Navigator 9+ otherwise false */ protected function checkBrowserNetscapeNavigator9Plus() { if (stripos($this->_agent, 'Firefox') !== false && preg_match('/Navigator\/([^ ]*)/i', $this->_agent, $matches)) { $this->setVersion($matches[1]); $this->setBrowser(self::BROWSER_NETSCAPE_NAVIGATOR); return true; } else if (stripos($this->_agent, 'Firefox') === false && preg_match('/Netscape6?\/([^ ]*)/i', $this->_agent, $matches)) { $this->setVersion($matches[1]); $this->setBrowser(self::BROWSER_NETSCAPE_NAVIGATOR); return true; } return false; } /** * Determine if the browser is Shiretoko or not (https://wiki.mozilla.org/Projects/shiretoko) (last updated 1.7) * @return boolean True if the browser is Shiretoko otherwise false */ protected function checkBrowserShiretoko() { if (stripos($this->_agent, 'Mozilla') !== false && preg_match('/Shiretoko\/([^ ]*)/i', $this->_agent, $matches)) { $this->setVersion($matches[1]); $this->setBrowser(self::BROWSER_SHIRETOKO); return true; } return false; } /** * Determine if the browser is Ice Cat or not (http://en.wikipedia.org/wiki/GNU_IceCat) (last updated 1.7) * @return boolean True if the browser is Ice Cat otherwise false */ protected function checkBrowserIceCat() { if (stripos($this->_agent, 'Mozilla') !== false && preg_match('/IceCat\/([^ ]*)/i', $this->_agent, $matches)) { $this->setVersion($matches[1]); $this->setBrowser(self::BROWSER_ICECAT); return true; } return false; } /** * Determine if the browser is Nokia or not (last updated 1.7) * @return boolean True if the browser is Nokia otherwise false */ protected function checkBrowserNokia() { if (preg_match("/Nokia([^\/]+)\/([^ SP]+)/i", $this->_agent, $matches)) { $this->setVersion($matches[2]); if (stripos($this->_agent, 'Series60') !== false || strpos($this->_agent, 'S60') !== false) { $this->setBrowser(self::BROWSER_NOKIA_S60); } else { $this->setBrowser(self::BROWSER_NOKIA); } $this->setMobile(true); return true; } return false; } /** * Determine if the browser is Firefox or not (last updated 1.7) * @return boolean True if the browser is Firefox otherwise false */ protected function checkBrowserFirefox() { if (stripos($this->_agent, 'safari') === false) { if (preg_match("/Firefox[\/ \(]([^ ;\)]+)/i", $this->_agent, $matches)) { $this->setVersion($matches[1]); $this->setBrowser(self::BROWSER_FIREFOX); //Firefox on Android if (stripos($this->_agent, 'Android') !== false) { if (stripos($this->_agent, 'Mobile') !== false) { $this->setMobile(true); } else { $this->setTablet(true); } } return true; } else if (preg_match("/Firefox$/i", $this->_agent, $matches)) { $this->setVersion(""); $this->setBrowser(self::BROWSER_FIREFOX); return true; } } return false; } /** * Determine if the browser is Firefox or not (last updated 1.7) * @return boolean True if the browser is Firefox otherwise false */ protected function checkBrowserIceweasel() { if (stripos($this->_agent, 'Iceweasel') !== false) { $aresult = explode('/', stristr($this->_agent, 'Iceweasel')); if (isset($aresult[1])) { $aversion = explode(' ', $aresult[1]); $this->setVersion($aversion[0]); $this->setBrowser(self::BROWSER_ICEWEASEL); return true; } } return false; } protected function checkGeneral() { if (preg_match('/^([A-Za-z0-9\-]+)[ \/-]([0-9][0-9.,]*)/', $this->_agent, $match)) { $this->setVersion(str_replace(',', '.', $match[3])); $this->setBrowser($match[1]); return true; } return false; } /** * Determine if the browser is Mozilla or not (last updated 1.7) * @return boolean True if the browser is Mozilla otherwise false */ protected function checkBrowserMozilla() { if (stripos($this->_agent, 'mozilla') !== false && preg_match('/rv:[0-9].[0-9][a-b]?/i', $this->_agent) && stripos($this->_agent, 'netscape') === false) { $aversion = explode(' ', stristr($this->_agent, 'rv:')); preg_match('/rv:[0-9].[0-9][a-b]?/i', $this->_agent, $aversion); $this->setVersion(str_replace('rv:', '', $aversion[0])); $this->setBrowser(self::BROWSER_MOZILLA); return true; } else if (stripos($this->_agent, 'mozilla') !== false && preg_match('/rv:[0-9]\.[0-9]/i', $this->_agent) && stripos($this->_agent, 'netscape') === false) { $aversion = explode('', stristr($this->_agent, 'rv:')); $this->setVersion(str_replace('rv:', '', $aversion[0])); $this->setBrowser(self::BROWSER_MOZILLA); return true; } else if (stripos($this->_agent, 'mozilla') !== false && preg_match('/mozilla\/([^ ]*)/i', $this->_agent, $matches) && stripos($this->_agent, 'netscape') === false) { $this->setVersion($matches[1]); $this->setBrowser(self::BROWSER_MOZILLA); return true; } return false; } /** * Determine if the browser is Lynx or not (last updated 1.7) * @return boolean True if the browser is Lynx otherwise false */ protected function checkBrowserLynx() { if (stripos($this->_agent, 'lynx') !== false) { $aresult = explode('/', stristr($this->_agent, 'Lynx')); $aversion = explode(' ', (isset($aresult[1]) ? $aresult[1] : "")); $this->setVersion($aversion[0]); $this->setBrowser(self::BROWSER_LYNX); return true; } return false; } /** * Determine if the browser is Amaya or not (last updated 1.7) * @return boolean True if the browser is Amaya otherwise false */ protected function checkBrowserAmaya() { if (stripos($this->_agent, 'amaya') !== false) { $aresult = explode('/', stristr($this->_agent, 'Amaya')); if (isset($aresult[1])) { $aversion = explode(' ', $aresult[1]); $this->setVersion($aversion[0]); $this->setBrowser(self::BROWSER_AMAYA); return true; } } return false; } /** * Determine if the browser is Safari or not (last updated 1.7) * @return boolean True if the browser is Safari otherwise false */ protected function checkBrowserSafari() { if (stripos($this->_agent, 'Safari') !== false && stripos($this->_agent, 'iPhone') === false && stripos($this->_agent, 'iPod') === false) { $aresult = explode('/', stristr($this->_agent, 'Version')); if (isset($aresult[1])) { $aversion = explode(' ', $aresult[1]); $this->setVersion($aversion[0]); } else { $this->setVersion(self::VERSION_UNKNOWN); } $this->setBrowser(self::BROWSER_SAFARI); return true; } return false; } /** * Detect if URL is loaded from FacebookExternalHit * @return boolean True if it detects FacebookExternalHit otherwise false */ protected function checkFacebookExternalHit() { if (stristr($this->_agent,'FacebookExternalHit')) { $this->setRobot(true); $this->setFacebook(true); return true; } return false; } /** * Detect if URL is being loaded from internal Facebook browser * @return boolean True if it detects internal Facebook browser otherwise false */ protected function checkForFacebookIos() { if (stristr($this->_agent,'FBIOS')) { $this->setFacebook(true); return true; } return false; } /** * Detect Version for the Safari browser on iOS devices * @return boolean True if it detects the version correctly otherwise false */ protected function getSafariVersionOnIos() { $aresult = explode('/',stristr($this->_agent,'Version')); if ( isset($aresult[1]) ) { $aversion = explode(' ',$aresult[1]); $this->setVersion($aversion[0]); return true; } return false; } /** * Detect Version for the Chrome browser on iOS devices * @return boolean True if it detects the version correctly otherwise false */ protected function getChromeVersionOnIos() { $aresult = explode('/',stristr($this->_agent,'CriOS')); if ( isset($aresult[1]) ) { $aversion = explode(' ',$aresult[1]); $this->setVersion($aversion[0]); $this->setBrowser(self::BROWSER_CHROME); return true; } return false; } /** * Determine if the browser is iPhone or not (last updated 1.7) * @return boolean True if the browser is iPhone otherwise false */ protected function checkBrowseriPhone() { if ( stripos($this->_agent,'iPhone') !== false ) { $this->setVersion(self::VERSION_UNKNOWN); $this->setBrowser(self::BROWSER_IPHONE); $this->getSafariVersionOnIos(); $this->getChromeVersionOnIos(); $this->checkForFacebookIos(); $this->setMobile(true); return true; } return false; } /** * Determine if the browser is iPad or not (last updated 1.7) * @return boolean True if the browser is iPad otherwise false */ protected function checkBrowseriPad() { if ( stripos($this->_agent,'iPad') !== false ) { $this->setVersion(self::VERSION_UNKNOWN); $this->setBrowser(self::BROWSER_IPAD); $this->getSafariVersionOnIos(); $this->getChromeVersionOnIos(); $this->checkForFacebookIos(); $this->setTablet(true); return true; } return false; } /** * Determine if the browser is iPod or not (last updated 1.7) * @return boolean True if the browser is iPod otherwise false */ protected function checkBrowseriPod() { if ( stripos($this->_agent,'iPod') !== false ) { $this->setVersion(self::VERSION_UNKNOWN); $this->setBrowser(self::BROWSER_IPOD); $this->getSafariVersionOnIos(); $this->getChromeVersionOnIos(); $this->checkForFacebookIos(); $this->setMobile(true); return true; } return false; } /** * Determine if the browser is Android or not (last updated 1.7) * @return boolean True if the browser is Android otherwise false */ protected function checkBrowserAndroid() { if (stripos($this->_agent, 'Android') !== false) { $aresult = explode(' ', stristr($this->_agent, 'Android')); if (isset($aresult[1])) { $aversion = explode(' ', $aresult[1]); $this->setVersion($aversion[0]); } else { $this->setVersion(self::VERSION_UNKNOWN); } if (stripos($this->_agent, 'Mobile') !== false) { $this->setMobile(true); } else { $this->setTablet(true); } $this->setBrowser(self::BROWSER_ANDROID); return true; } return false; } /** * Detect Samsung internal player: "samsung-agent/1.1 (...)" */ protected function checkBrowserSamsungAgent() { if (stripos($this->_agent, 'samsung-agent') !== false) { // Prefer "samsung-agent/" $aresult = explode('/', stristr($this->_agent, 'samsung-agent')); if (isset($aresult[1])) { $aversion = explode(' ', $aresult[1]); $this->setVersion(str_replace(';', '', $aversion[0])); } else { $this->setVersion(self::VERSION_UNKNOWN); } $this->setBrowser(self::BROWSER_SAMSUNG); $this->setPlatform(self::PLATFORM_ANDROID); return true; } return false; } /** * Detect FFmpeg/libavformat user agents like "Lavf/60.16.100". */ protected function checkBrowserLavf() { if (stripos($this->_agent, 'Lavf') === false) { return false; } // Prefer "Lavf/" if (preg_match('/\bLavf\/([0-9.]+)/i', $this->_agent, $m)) { $this->setVersion($m[1]); } else { $this->setVersion(self::VERSION_UNKNOWN); } $this->setBrowser(self::BROWSER_FFMPEG); // Library client — not a phone/tablet $this->setMobile(false); $this->setTablet(false); // Leave platform as-is (often unknown). If you want, you can // set to Linux when token present: if ($this->getPlatform() === self::PLATFORM_UNKNOWN && stripos($this->_agent, 'Linux') !== false) { $this->setPlatform(self::PLATFORM_LINUX); } return true; } /** * Detect VLC-based players: "VLC/3.0.16 LibVLC/3.0.16" or "LibVLC/3.x" * We report as VLC and use the VLC/ if available; otherwise fall back to LibVLC/. */ protected function checkBrowserVLC() { if (stripos($this->_agent, 'VLC/') !== false || stripos($this->_agent, 'LibVLC') !== false) { // Try VLC/ first if (stripos($this->_agent, 'VLC/') !== false) { $aresult = explode('/', stristr($this->_agent, 'VLC/')); if (isset($aresult[1])) { $aversion = explode(' ', $aresult[1]); $this->setVersion(str_replace(';', '', $aversion[0])); } else { $this->setVersion(self::VERSION_UNKNOWN); } $this->setBrowser(self::BROWSER_VLC); return true; } // Fallback: LibVLC/ if (stripos($this->_agent, 'LibVLC') !== false) { $aresult = explode('/', stristr($this->_agent, 'LibVLC')); if (isset($aresult[1])) { $aversion = explode(' ', $aresult[1]); $this->setVersion(str_replace(';', '', $aversion[0])); } else { $this->setVersion(self::VERSION_UNKNOWN); } $this->setBrowser(self::BROWSER_VLC); return true; } } return false; } /** * Detect libmpv-based clients: "libmpv" or "libmpv/0.35" etc. */ protected function checkBrowserLibmpv() { if (stripos($this->_agent, 'libmpv') !== false) { // Accept "libmpv/" or bare "libmpv" $aresult = explode('/', stristr($this->_agent, 'libmpv')); if (isset($aresult[1])) { $aversion = explode(' ', $aresult[1]); $this->setVersion(str_replace(';', '', $aversion[0])); } else { $this->setVersion(self::VERSION_UNKNOWN); } $this->setBrowser(self::BROWSER_STREMIO); $this->setVersion(''); $this->setPlatform(self::PLATFORM_WINDOWS); return true; } return false; } /** * Detect LG webOS Smart TV browsers. * Examples: * - "Mozilla/5.0 (Web0S; Linux/SmartTV) ... WebAppManager" * - "Mozilla/5.0 (Web0S/2.2.1; LG SmartTV) ..." * - "Mozilla/5.0 (WebOS; Linux/SmartTV) ..." */ protected function checkBrowserWebOS() { if (stripos($this->_agent, 'Web0S') !== false || stripos($this->_agent, 'WebOS') !== false) { // Try Web0S/WebOS version first if (preg_match('/Web0S\/([0-9.]+)/i', $this->_agent, $m) || preg_match('/WebOS\/([0-9.]+)/i', $this->_agent, $m)) { $this->setVersion($m[1]); } // Otherwise fall back to embedded Chrome version elseif (preg_match('/Chrome\/([0-9.]+)/i', $this->_agent, $m)) { $this->setVersion($m[1]); } else { $this->setVersion(self::VERSION_UNKNOWN); } $this->setBrowser(self::BROWSER_WEBOS); $this->setPlatform(self::PLATFORM_WEBOS); $this->setMobile(false); $this->setTablet(false); return true; } return false; } /** * Detect KSPlayer (iOS video player). * Examples: * "KSPlayer/2.0.1 ..." * "KSPlayer (iPhone; iOS 16.5) ..." * "KSPlayer" */ protected function checkBrowserKSPlayer() { if (stripos($this->_agent, 'KSPlayer') !== false) { // Prefer "KSPlayer/" $aresult = explode('/', stristr($this->_agent, 'KSPlayer')); if (isset($aresult[1])) { $aversion = explode(' ', $aresult[1]); $this->setVersion(str_replace(';', '', $aversion[0])); } else { $this->setVersion(''); } $this->setBrowser(self::BROWSER_KSPLAYER); // Always set platform to Apple (iOS app) $this->setPlatform(self::PLATFORM_APPLE); // Flag device type if (stripos($this->_agent, 'iPad') !== false) { $this->setTablet(true); } else { // Default to mobile for iPhone/iPod/other iOS $this->setMobile(true); } return true; } return false; } /** * Detect ExoMedia Android app UAs like: * "ExoMedia 3.1.0r (58) / Android 7.1.2 / AFTMM" * We classify as "Android App", keep Android OS version, and set platform to device model. */ protected function checkBrowserExoMedia() { if (stripos($this->_agent, 'ExoMedia') === false) { return false; } // Version = Android OS version (not the ExoMedia lib version) if (preg_match('/Android\s+([0-9.]+)/i', $this->_agent, $m)) { $this->setVersion($m[1]); } else { $this->setVersion(self::VERSION_UNKNOWN); } $this->setBrowser(self::BROWSER_ANDROID_APP); // Platform = device model // 1) Try the standard Android "... Android X; Build/..." pattern if (preg_match('/Android\s+[0-9.]+;\s*(.+?)\s+Build\//i', $this->_agent, $m)) { $model = trim($m[1]); } else { // 2) Handle ExoMedia’s slash format: "... / Android X / " // Grab everything after "/ Android /" $model = preg_replace('~^.*?/\s*Android\s+[0-9.]+\s*/\s*~i', '', $this->_agent); $model = trim($model); // If there are trailing tokens, keep them—normalizeAndroidDeviceModel() can extract AFT codes within. // Optionally, strip trailing comments: $model = preg_replace('/\s*[;()].*$/', '', $model); } if ($model !== '') { if (method_exists($this, 'normalizeAndroidDeviceModel')) { $model = $this->normalizeAndroidDeviceModel($model); } $this->setPlatform($model); } else { $this->setPlatform(self::PLATFORM_ANDROID); } // Android TV-style devices aren’t phones/tablets if (stripos($this->_agent, 'AFT') !== false || stripos($this->_agent, 'Android TV') !== false) { $this->setMobile(false); $this->setTablet(false); } return true; } protected function normalizeAndroidDeviceModel($model) { // Try to capture an SM- code inside the model string if (preg_match('/\b(SM-[A-Z0-9]+)/i', $model, $m)) { $code = strtoupper($m[1]); return $this->normalizeAndroidDeviceModelSamsung($code); } // Try to capture an MiTV- code inside the model string if (preg_match('/\b(MiTV-[A-Z0-9]+)/i', $model, $m) OR preg_match('/\b(XiaomiTV-[A-Z0-9]+)/i', $model, $m) OR preg_match('/\b(RedmiTV-[A-Z0-9]+)/i', $model, $m)) { $code = $m[1]; return $this->normalizeAndroidDeviceModelXiaomi($code); } // Try to capture an AFT code inside the model string if (preg_match('/\b(AFT[A-Z0-9]+)/i', $model, $m)) { $code = strtoupper($m[1]); return $this->normalizeAndroidDeviceModelAmazon($code); } // Fallback return $model; } protected function normalizeAndroidDeviceModelXiaomi($code) { $map = [ // ------------------------- // Xiaomi MiTV (Android identifiers) // ------------------------- 'MiTV-AFKR0' => 'Xiaomi Mi TV (Korea Variant)', 'MiTV-AXFR0' => 'Xiaomi Mi TV (Global Variant)', 'MiTV-AFSC0' => 'Xiaomi Mi TV (China Variant)', 'MiTV-AFFA0' => 'Xiaomi Mi TV 4A', 'MiTV-AFAA0' => 'Xiaomi Mi TV 4A Global', 'MiTV-AFSA0' => 'Xiaomi Mi TV 4S', 'MiTV-AFLC0' => 'Xiaomi Mi TV L Series', 'MiTV-AFLE0' => 'Xiaomi Mi TV L (Europe)', 'MiTV-AFRC0' => 'Xiaomi Mi TV (Retail/Costco Variant)', 'MiTV-AFGL0' => 'Mi TV Global', 'MiTV-AFSG0' => 'Mi TV SG', 'MiTV-AFHK0' => 'Mi TV HK', // ------------------------- // Xiaomi TV (new naming since 2022) // ------------------------- 'XiaomiTV-A2' => 'Xiaomi TV A2 Series', 'XiaomiTV-A2K' => 'Xiaomi TV A2 Korean Variant', 'XiaomiTV-A2G' => 'Xiaomi TV A2 Global', 'XiaomiTV-A32' => 'Xiaomi TV A 2023 32"', 'XiaomiTV-A43' => 'Xiaomi TV A 2023 43"', 'XiaomiTV-A55' => 'Xiaomi TV A 2023 55"', 'XiaomiTV-A65' => 'Xiaomi TV A 2023 65"', // ------------------------- // Xiaomi TV Q / P / X series (Android ids) // ------------------------- 'XiaomiTV-Q1' => 'Xiaomi TV Q1', 'XiaomiTV-Q1E' => 'Xiaomi TV Q1E', 'XiaomiTV-Q2' => 'Xiaomi TV Q2 Series', 'XiaomiTV-P1' => 'Xiaomi TV P1 Series', 'XiaomiTV-P1E' => 'Xiaomi TV P1E', 'XiaomiTV-X' => 'Xiaomi TV X Series', 'XiaomiTV-XE' => 'Xiaomi TV X Enhanced Edition', // ------------------------- // Redmi TV (Android identifiers) // ------------------------- 'RedmiTV-MAX98' => 'Redmi TV MAX 98"', 'RedmiTV-MAX86' => 'Redmi TV MAX 86"', 'RedmiTV-A55' => 'Redmi TV A55', 'RedmiTV-A50' => 'Redmi TV A50', 'RedmiTV-A43' => 'Redmi TV A43', 'RedmiTV-A32' => 'Redmi TV A32', 'RedmiTV-X55' => 'Redmi TV X55', 'RedmiTV-X65' => 'Redmi TV X65', 'RedmiTV-X75' => 'Redmi TV X75', ]; if (isset($map[$code])) { return $map[$code]; } // generic fallback return 'Xiaomi TV (' . $code . ')'; } protected function normalizeAndroidDeviceModelSamsung($code) { $code = strtoupper(trim($code)); $baseMap = [ // ----------------------------- // Galaxy Z (Fold / Flip) // ----------------------------- 'SM-F956' => 'Samsung Galaxy Z Fold 6', 'SM-F741' => 'Samsung Galaxy Z Flip 6', 'SM-F946' => 'Samsung Galaxy Z Fold 5', 'SM-F731' => 'Samsung Galaxy Z Flip 5', 'SM-F936' => 'Samsung Galaxy Z Fold 4', 'SM-F721' => 'Samsung Galaxy Z Flip 4', 'SM-F926' => 'Samsung Galaxy Z Fold 3', 'SM-F711' => 'Samsung Galaxy Z Flip 3', // ----------------------------- // Galaxy S Series // ----------------------------- 'SM-S928' => 'Samsung Galaxy S24 Ultra', 'SM-S926' => 'Samsung Galaxy S24+', 'SM-S921' => 'Samsung Galaxy S24', 'SM-S918' => 'Samsung Galaxy S23 Ultra', 'SM-S916' => 'Samsung Galaxy S23+', 'SM-S911' => 'Samsung Galaxy S23', // S23 FE 'SM-S711' => 'Samsung Galaxy S23 FE', 'SM-S908' => 'Samsung Galaxy S22 Ultra', 'SM-S906' => 'Samsung Galaxy S22+', 'SM-S901' => 'Samsung Galaxy S22', 'SM-G998' => 'Samsung Galaxy S21 Ultra', 'SM-G996' => 'Samsung Galaxy S21+', 'SM-G991' => 'Samsung Galaxy S21', // S21 FE 'SM-G990' => 'Samsung Galaxy S21 FE', 'SM-G988' => 'Samsung Galaxy S20 Ultra', 'SM-G986' => 'Samsung Galaxy S20+', 'SM-G981' => 'Samsung Galaxy S20', 'SM-G973' => 'Samsung Galaxy S10', 'SM-G975' => 'Samsung Galaxy S10+', 'SM-G977' => 'Samsung Galaxy S10 5G', // ----------------------------- // Galaxy Note // ----------------------------- 'SM-N986' => 'Samsung Galaxy Note 20 Ultra', 'SM-N981' => 'Samsung Galaxy Note 20', 'SM-N976' => 'Samsung Galaxy Note 10+ 5G', 'SM-N975' => 'Samsung Galaxy Note 10+', 'SM-N970' => 'Samsung Galaxy Note 10', 'SM-N960' => 'Samsung Galaxy Note 9', 'SM-N950' => 'Samsung Galaxy Note 8', // ----------------------------- // Galaxy A Series // ----------------------------- 'SM-A556' => 'Samsung Galaxy A55 5G', 'SM-A546' => 'Samsung Galaxy A54 5G', 'SM-A536' => 'Samsung Galaxy A53 5G', 'SM-A528' => 'Samsung Galaxy A52s 5G', 'SM-A526' => 'Samsung Galaxy A52 5G', 'SM-A525' => 'Samsung Galaxy A52', 'SM-A346' => 'Samsung Galaxy A34 5G', 'SM-A336' => 'Samsung Galaxy A33 5G', 'SM-A146' => 'Samsung Galaxy A14 5G', 'SM-A145' => 'Samsung Galaxy A14', 'SM-A135' => 'Samsung Galaxy A13', 'SM-A125' => 'Samsung Galaxy A12', 'SM-A115' => 'Samsung Galaxy A11', 'SM-A715' => 'Samsung Galaxy A71', 'SM-A705' => 'Samsung Galaxy A70', // ----------------------------- // Galaxy M // ----------------------------- 'SM-M546' => 'Samsung Galaxy M54', 'SM-M536' => 'Samsung Galaxy M53', 'SM-M526' => 'Samsung Galaxy M52', 'SM-M336' => 'Samsung Galaxy M33', 'SM-M326' => 'Samsung Galaxy M32', 'SM-M315' => 'Samsung Galaxy M31', 'SM-M215' => 'Samsung Galaxy M21', 'SM-M115' => 'Samsung Galaxy M11', // ----------------------------- // Galaxy F // ----------------------------- 'SM-E546' => 'Samsung Galaxy F54', 'SM-E426' => 'Samsung Galaxy F42 5G', 'SM-F127' => 'Samsung Galaxy F12', // ----------------------------- // Tablets // ----------------------------- 'SM-X916' => 'Samsung Galaxy Tab S9 Ultra', 'SM-X816' => 'Samsung Galaxy Tab S9+', 'SM-X716' => 'Samsung Galaxy Tab S9', 'SM-X910' => 'Samsung Galaxy Tab S9 Ultra Wi-Fi', 'SM-X810' => 'Samsung Galaxy Tab S9+ Wi-Fi', 'SM-X710' => 'Samsung Galaxy Tab S9 Wi-Fi', 'SM-X906' => 'Samsung Galaxy Tab S8 Ultra', 'SM-X806' => 'Samsung Galaxy Tab S8+', 'SM-X706' => 'Samsung Galaxy Tab S8', 'SM-X900' => 'Samsung Galaxy Tab S8 Ultra Wi-Fi', 'SM-X800' => 'Samsung Galaxy Tab S8+ Wi-Fi', 'SM-X700' => 'Samsung Galaxy Tab S8 Wi-Fi', 'SM-X205' => 'Samsung Galaxy Tab A8 2022', 'SM-X' => 'Samsung Galaxy Tab', 'SM-T736' => 'Samsung Galaxy Tab S7 FE', 'SM-T976' => 'Samsung Galaxy Tab S7+', 'SM-T730' => 'Samsung Galaxy Tab S7 FE', 'SM-T970' => 'Samsung Galaxy Tab S7+', 'SM-T970' => 'Samsung Galaxy Tab S7', 'SM-T575' => 'Samsung Galaxy Tab Active 3', 'SM-T510' => 'Samsung Galaxy Tab A 10.1', 'SM-T290' => 'Samsung Galaxy Tab A 8.0', 'SM-T' => 'Samsung Galaxy Tab', // ----------------------------- // Watches // ----------------------------- 'SM-R960' => 'Samsung Galaxy Watch 6 Classic 47mm', 'SM-R965' => 'Samsung Galaxy Watch 6 Classic 43mm', 'SM-R940' => 'Samsung Galaxy Watch 6 44mm', 'SM-R935' => 'Samsung Galaxy Watch 6 40mm', 'SM-R890' => 'Samsung Galaxy Watch 4 Classic', 'SM-R880' => 'Samsung Galaxy Watch 4', // ----------------------------- // Misc // ----------------------------- 'SM-G780' => 'Samsung Galaxy S20 FE', 'SM-G781' => 'Samsung Galaxy S20 FE 5G', 'SM-A042' => 'Samsung Galaxy A04e', 'SM-A032' => 'Samsung Galaxy A03', 'SM-A022' => 'Samsung Galaxy A02', ]; foreach ($baseMap as $prefix => $name) { if (substr($code, 0, strlen($prefix)) === $prefix) { return $name; } } return 'Samsung (' . $code . ')'; } protected function normalizeAndroidDeviceModelAmazon($code) { // Full mapping based on WikiDevi & Amazon docs $map = [ 'AFTB' => 'Amazon Fire TV (1st Gen)', 'AFTN' => 'Amazon Fire TV (3rd Gen)', 'AFTS' => 'Amazon Fire TV (2nd Gen)', 'AFTA' => 'Amazon Fire TV Cube (1st Gen)', 'AFTM' => 'Fire TV Stick (1st Gen)', 'AFTT' => 'Fire TV Stick (2nd / Basic Edition)', 'AFTMM' => 'Fire TV Stick 4K (1st Gen)', 'AFTKA' => 'Fire TV Stick 4K Max (1st Gen)', 'AFTKM' => 'Amazon FireStick (Fire TV Stick 4K, 2nd Gen)', // your custom naming 'AFTKRT' => 'Fire TV Stick 4K Max (2nd Gen)', 'AFTSSS' => 'Fire TV Stick (3rd Gen)', 'AFTSS' => 'Fire TV Stick Lite (1st Gen)', 'AFTGAZL' => 'Fire TV Cube (3rd Gen)', 'AFTR' => 'Fire TV Cube (2nd Gen)', 'AFTRS' => 'Fire TV / Box version (variant)', 'AFTMD001' => 'TCL Fire TV (S4/Q6, 2023)', 'AFTMD002' => 'TCL Fire TV (S3, 2023)', 'AFTSHN02' => 'TCL Fire TV (32/40 FHD, 2023)', 'AFTHA001' => 'Toshiba 4K UHD Fire TV (2022)', 'AFTHA003' => 'Toshiba 4K Far-field Fire TV (2021)', 'AFTHA004' => 'Toshiba 4K UHD Fire TV (2022)', 'AFTPR001' => 'AmazonBasics 4K Fire TV (2020)', 'AFTBU001' => 'AmazonBasics HD / FHD Fire TV (2020)', 'AFTEUFF014' => 'Grundig OLED 4K Fire TV', 'AFTEU014' => 'Grundig Vision 7 4K Fire TV', 'AFTEU011' => 'Grundig Vision 6 HD Fire TV', 'AFTSO001' => 'JVC 4K Fire TV (Freeview Play, 2021)', 'AFTLE' => 'Onida HD Fire TV (2019)', 'AFTWMST22' => 'JVC 2K Fire TV (2020)', 'AFTHA002' => 'Toshiba V35 / Fire TV (variant)', 'AFTDCT31' => 'Toshiba 4K Fire TV (2020)', 'AFTBAMR311' => 'Toshiba HD Fire TV variant', 'AFTKMST12' => 'Toshiba 4K Fire TV (2018-19 variant)', 'AFTEAMR311' => 'Insignia HD / Fire TV (2018-20)', 'AFTJMST12' => 'Insignia 4K Fire TV (2018)', 'AFTMON001' => 'Funai 4K Fire TV (2022 variant)', 'AFTMON002' => 'Funai 4K Fire TV variant', 'AFT6E0FA' => 'Toshiba HD Ready Smart File TV', 'AFTMA475B1' => 'Fire TV TCL 4K Smart / QLED series', 'AFTWI001' => 'Fire TV (OK 4K, 2020 variant)', 'AFTTIFF55' => 'Onida 4K Fire TV (2020)', 'AFTTIFF43' => 'Fire TV Omni / QLED series', 'AFTWYM01' => 'Fire TV Panasonic (WYM series)', 'AFTTOR001' => 'Fire TV Panasonic Z-series', 'AFTLBT962E2' => 'BMW Fire TV (in-car, 2022)', 'AFTANNA0' => 'Xiaomi / OK Fire TV', ]; if (isset($map[$code])) { return $map[$code]; } // fallback patterns if (preg_match('/^AFTK/', $code)) return 'Fire TV 4K Family (' . $code . ')'; if (preg_match('/^AFTS/', $code)) return 'Fire TV Family (' . $code . ')'; if (preg_match('/^AFTM/', $code)) return 'Fire TV Family (' . $code . ')'; if (preg_match('/^AFTN/', $code)) return 'Fire TV Box Family (' . $code . ')'; if (preg_match('/^AFTC/', $code)) return 'Fire TV Cube / Streaming Player (' . $code . ')'; if (preg_match('/^AFTHA/', $code)) return 'Fire TV (Toshiba / HA series) (' . $code . ')'; if (preg_match('/^AFTMD/', $code)) return 'Fire TV (TCL / MD series) (' . $code . ')'; if (preg_match('/^AFTMA/', $code)) return 'Fire TV (TCL / MA series) (' . $code . ')'; // generic fallback return 'Amazon Fire TV (' . $code . ')'; } /** * Detect generic Android App requests using Dalvik/OkHttp style UAs. * Matches strings that contain "Android" and "Build/". * Example: * "Dalvik/2.1.0 (Linux; U; Android 14; Chromecast Build/UTTC...)" * "Dalvik/2.1.0 (Linux; U; Android 9; AGT419 Build/PI)" */ protected function checkBrowserAndroidApp() { if (stripos($this->_agent, 'Android') !== false && stripos($this->_agent, 'Build/') !== false) { // Version = Android OS version if (preg_match('/Android\s+([0-9.]+)/i', $this->_agent, $m)) { $this->setVersion($m[1]); } else { $this->setVersion(self::VERSION_UNKNOWN); } // Browser = Android App $this->setBrowser(self::BROWSER_ANDROID_APP); // Platform = device model (string between Android X; and Build/) if (preg_match('/Android\s+[0-9.]+;\s*(.+?)\s+Build\//i', $this->_agent, $m)) { $model = trim($m[1]); $this->setPlatform($this->normalizeAndroidDeviceModel($model)); } else { $this->setPlatform(self::PLATFORM_ANDROID); } // Default: not a phone/tablet $this->setMobile(false); $this->setTablet(false); return true; } return false; } /** * Detect Roku devices. * Example: * "Roku/DVP-14.6 (14.6.4.9914-94)" */ protected function checkBrowserRoku() { if (stripos($this->_agent, 'Roku/') === false) { return false; } $this->setBrowser(self::BROWSER_ROKU); // Prefer detailed build inside parentheses, e.g. "14.6.4.9914-94" if (preg_match('/\(([^)]+)\)/', $this->_agent, $m)) { // Pick the first numeric-like token inside the parens if (preg_match('/\d+(?:\.\d+)*(?:-[0-9A-Za-z-]+)?/', $m[1], $vm)) { $this->setVersion($vm[0]); } } // Fallback: DVP- after Roku/ if ($this->getVersion() === self::VERSION_UNKNOWN) { if (preg_match('#Roku/[^-\s]+-([0-9.]+)#i', $this->_agent, $m)) { $this->setVersion($m[1]); } } // Platform & device type $this->setPlatform(self::PLATFORM_ROKU); $this->setMobile(false); $this->setTablet(false); return true; } /** * Determine the user's platform (last updated 1.7) */ protected function checkPlatform() { if (stripos($this->_agent, 'windows') !== false) { $this->_platform = self::PLATFORM_WINDOWS; } else if (stripos($this->_agent, 'iPad') !== false) { $this->_platform = self::PLATFORM_IPAD; } else if (stripos($this->_agent, 'iPod') !== false) { $this->_platform = self::PLATFORM_IPOD; } else if (stripos($this->_agent, 'iPhone') !== false) { $this->_platform = self::PLATFORM_IPHONE; } elseif (stripos($this->_agent, 'mac') !== false) { $this->_platform = self::PLATFORM_APPLE; } elseif (stripos($this->_agent, 'android') !== false) { $this->_platform = self::PLATFORM_ANDROID; } elseif (stripos($this->_agent, 'linux') !== false) { $this->_platform = self::PLATFORM_LINUX; } else if (stripos($this->_agent, 'Nokia') !== false) { $this->_platform = self::PLATFORM_NOKIA; } else if (stripos($this->_agent, 'BlackBerry') !== false) { $this->_platform = self::PLATFORM_BLACKBERRY; } elseif (stripos($this->_agent, 'FreeBSD') !== false) { $this->_platform = self::PLATFORM_FREEBSD; } elseif (stripos($this->_agent, 'OpenBSD') !== false) { $this->_platform = self::PLATFORM_OPENBSD; } elseif (stripos($this->_agent, 'NetBSD') !== false) { $this->_platform = self::PLATFORM_NETBSD; } elseif (stripos($this->_agent, 'OpenSolaris') !== false) { $this->_platform = self::PLATFORM_OPENSOLARIS; } elseif (stripos($this->_agent, 'SunOS') !== false) { $this->_platform = self::PLATFORM_SUNOS; } elseif (stripos($this->_agent, 'OS\/2') !== false) { $this->_platform = self::PLATFORM_OS2; } elseif (stripos($this->_agent, 'BeOS') !== false) { $this->_platform = self::PLATFORM_BEOS; } elseif (stripos($this->_agent, 'win') !== false) { $this->_platform = self::PLATFORM_WINDOWS; } } } class DeviceCode { private $key; private $b32; function __construct($key, $b32) { $this->key = $key; $this->b32 = $b32; } public function encode($id) { $id = (int)$id; if ($id < 0) { return false; } $tag = $this->tag($id); // [32-bit id][8-bit tag] = 40 bits $x = (($id & 0xFFFFFFFF) << 8) | $tag; $x = $this->permute($x); return strtoupper($this->b32->fromBin($this->toBits40($x))); } public function decode($code) { $code = strtoupper(trim($code)); if (strlen($code) !== 8) { return false; } $bits = $this->b32->toBin($code); if ($bits === false) { return false; } // keep only first 40 bits in case your class pads $bits = substr($bits, 0, 40); if (strlen($bits) !== 40 || preg_match('/[^01]/', $bits)) { return false; } $x = $this->fromBits40($bits); $x = $this->unpermute($x); $id = ($x >> 8) & 0xFFFFFFFF; $tag = $x & 0xFF; if ($tag !== $this->tag($id)) { return false; } return $id; } private function tag($id) { $mac = hash_hmac('sha256', pack('N', $id), $this->key, true); return ord($mac[0]); } private function permute($x) { $l = ($x >> 20) & 0xFFFFF; $r = $x & 0xFFFFF; $i = 0; while ($i < 4) { $f = $this->round($r, $i); $tmp = $r; $r = ($l ^ $f) & 0xFFFFF; $l = $tmp; $i++; } return (($l << 20) | $r); } private function unpermute($x) { $l = ($x >> 20) & 0xFFFFF; $r = $x & 0xFFFFF; $i = 3; while ($i >= 0) { $f = $this->round($l, $i); $tmp = $l; $l = ($r ^ $f) & 0xFFFFF; $r = $tmp; $i--; } return (($l << 20) | $r); } private function round($v, $i) { $mac = hash_hmac('sha256', chr($i) . pack('N', $v), $this->key, true); $u = unpack('N', substr($mac, 0, 4)); return $u[1] & 0xFFFFF; } private function toBits40($x) { $out = ''; $i = 39; while ($i >= 0) { $out .= (($x >> $i) & 1) ? '1' : '0'; $i--; } return $out; } private function fromBits40($bits) { $x = 0; $len = strlen($bits); $i = 0; while ($i < $len) { $x = ($x << 1) | ($bits[$i] === '1' ? 1 : 0); $i++; } return $x; } } class Base32 { /** * csRFC3548 * * The character set as defined by RFC3548 * @link http://www.ietf.org/rfc/rfc3548.txt */ const csRFC3548 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'; /** * csSafe * * This character set is designed to be more human friendly * For example: i, I, L, l and 1 all map to 1 * Also: there is no U - to help prevent offencive output * @link http://www.crockford.com/wrmg/base32.html * */ const csSafe = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'; /** * cs09AV * * This character set follows the example of the hex * character set and is included to make this class * compatible with MIME::Base32 * @link http://search.cpan.org/~danpeder/MIME-Base32-1.01/Base32.pm * */ const cs09AV = '0123456789ABCDEFGHIJKLMNOPQRSTUV'; /** * _charset * * Internal holder of the current character set. * * @access protected * @var string */ protected $_charset; /** * Constructor * * Call to create a new object. * * @param string $charset (optional) The character set to use * @see setCharset */ public function __construct($charset = self::csRFC3548) { $this->setCharset($charset); } /** * str2bin * * Converts any ascii string to a binary string * * @param string $str The string you want to convert * @return string String of 0's and 1's */ public function str2bin($str) { $chrs = unpack('C*', $str); return vsprintf(str_repeat('%08b', count($chrs)), $chrs); } /** * bin2str * * Converts a binary string to an ascii string * * @param string $str The string of 0's and 1's you want to convert * @return string The ascii output * @throws Exception */ public function bin2str($str) { if ($str == '') return ''; if (strlen($str) % 8 > 0) throw new Exception('Length must be divisible by 8'); if (!preg_match('/^[01]+$/', $str)) throw new Exception('Only 0\'s and 1\'s are permitted'); preg_match_all('/.{8}/', $str, $chrs); $chrs = array_map('bindec', $chrs[0]); // I'm just being slack here array_unshift($chrs, 'C*'); return call_user_func_array('pack', $chrs); } /** * fromBin * * Converts a correct binary string to base32 * * @param string $str The string of 0's and 1's you want to convert * @return string String encoded as base32 * @throws exception */ public function fromBin($str) { if (strlen($str) % 8 > 0) throw new Exception('Length must be divisible by 8'); if (!preg_match('/^[01]+$/', $str)) throw new Exception('Only 0\'s and 1\'s are permitted'); // Base32 works on the first 5 bits of a byte, so we insert blanks to pad it out $str = preg_replace('/(.{5})/', '000$1', $str); // We need a string divisible by 5 $length = strlen($str); $rbits = $length & 7; if ($rbits > 0) { // Excessive bits need to be padded $ebits = substr($str, $length - $rbits); $str = substr($str, 0, $length - $rbits); $str .= "000$ebits".str_repeat('0', 5 - strlen($ebits)); } preg_match_all('/.{8}/', $str, $chrs); $chrs = array_map(array($this, '_mapcharset'), $chrs[0]); return join('', $chrs); } /** * toBin * * Accepts a base32 string and returns an ascii binary string * * @param string $str The base32 string to convert * @return string Ascii binary string */ public function toBin($str) { if (!preg_match('/^['.$this->_charset.']+$/', $str)) return ''; // Convert the base32 string back to a binary string $str = join('',array_map(array($this, '_mapbin'), str_split($str))); // Remove the extra 0's we added $str = preg_replace('/000(.{5})/', '$1', $str); // Unpad if nessicary $length = strlen($str); $rbits = $length & 7; if ($rbits > 0) $str = substr($str, 0, $length - $rbits); return $str; } /** * fromString * * Convert any string to a base32 string * This should be binary safe... * * @param string $str The string to convert * @return string The converted base32 string */ public function fromString($str) { return $this->fromBin($this->str2bin($str)); } /** * toString * * Convert any base32 string to a normal sctring * This should be binary safe... * * @param string $str The base32 string to convert * @return string The normal string */ public function toString($str) { $str = strtoupper($str); // csSave actually has to be able to consider extra characters if ($this->_charset == self::csSafe) { $str = str_replace('O','0',$str); $str = str_replace(array('I','L'),'1',$str); } return $this->bin2str($this->tobin($str)); } /** * _mapcharset * * Used with array_map to map the bits from a binary string * directly into a base32 character set * * @access private * @param string $str The string of 0's and 1's you want to convert * @return char Resulting base32 character */ private function _mapcharset($str) { return $this->_charset[bindec($str)]; } /** * _mapbin * * Used with array_map to map the characters from a base32 * character set directly into a binary string * * @access private * @param char $chr The caracter to map * @return str String of 0's and 1's */ private function _mapbin($chr) { return sprintf('%08b',strpos($this->_charset,$chr)); } /** * setCharset * * Used to set the internal _charset variable * I've left it so that people can arbirtrarily set their * own charset * * Can be called with: * * Base32::csRFC3548 * * Base32::csSafe * * Base32::cs09AV * * @param string $charset The character set you want to use * @throws Exception */ public function setCharset($charset = self::csRFC3548) { if (strlen($charset) == 32) { $this->_charset = strtoupper($charset); } else { throw new Exception('Length must be exactly 32'); } } } class Encryptor { protected $_key; protected $_method; protected $_cipher; protected $_decipher; protected $_bytesToKeyResults = array(); protected static $_cachedTables = array(); protected static $_encryptTable = array(); protected static $_decryptTable = array(); protected $_cipherIv; protected $_ivSent; protected static $_methodSupported = array( 'aes-128-cfb'=> array(16, 16), 'aes-192-cfb'=> array(24, 16), 'aes-256-cfb'=> array(32, 16), 'bf-cfb'=> array(16, 8), 'camellia-128-cfb'=> array(16, 16), 'camellia-192-cfb'=> array(24, 16), 'camellia-256-cfb'=> array(32, 16), 'cast5-cfb'=> array(16, 8), 'des-cfb'=> array(8, 8), 'idea-cfb'=>array(16, 8), 'rc2-cfb'=> array(16, 8), 'rc4'=> array(16, 0), 'rc4-md5'=> array(16, 16), 'seed-cfb'=> array(16, 16) ); public static function initTable($key) { $_ref = self::getTable($key); self::$_encryptTable = $_ref[0]; self::$_decryptTable = $_ref[1]; } public function __construct($key, $method) { $this->_key = $key; $this->_method = $method; if($this->_method == 'table') { $this->_method = null; } if($this->_method) { $iv_size = openssl_cipher_iv_length($this->_method); $iv = openssl_random_pseudo_bytes($iv_size); $this->_cipher = $this->getcipher($this->_key, $this->_method, 1, $iv); } else { if(!self::$_encryptTable) { $_ref = self::getTable($this->_key); self::$_encryptTable = $_ref[0]; self::$_decryptTable = $_ref[1]; } } } protected static function getTable($key) { if (isset(self::$_cachedTables[$key])) { return self::$_cachedTables[$key]; } $int32Max = pow(2, 32); $table = array(); $decrypt_table = array(); $hash = md5($key, true); $tmp = unpack('V2', $hash); $al = $tmp[1]; $ah = $tmp[2]; $i = 0; while ($i < 256) { $table[$i] = $i; $i++; } $i = 1; while ($i < 1024) { $table = merge_sort($table, function($x, $y)use($ah, $al, $i, $int32Max) { return (($ah % ($x + $i)) * $int32Max + $al) % ($x + $i) - (($ah % ($y + $i)) * $int32Max + $al) % ($y + $i); }); $i++; } $table = array_values($table); $i = 0; while ($i < 256) { $decrypt_table[$table[$i]] = $i; ++$i; } ksort($decrypt_table); $decrypt_table = array_values($decrypt_table); $result = array($table, $decrypt_table); self::$_cachedTables[$key] = $result; return $result; } public static function substitute($table, $buf) { $i = 0; $len = strlen($buf); while ($i < $len) { $buf[$i] = chr($table[ord($buf[$i])]); $i++; } return $buf; } protected function getCipher($password, $method, $op, $iv) { $method = strtolower($method); $m = $this->getCipherLen($method); if($m) { $ref = $this->EVPBytesToKey($password, $m[0], $m[1]); $key = $ref[0]; $iv_ = $ref[1]; if ($iv == null) { $iv = $iv_; } if ($op === 1) { $this->_cipherIv = substr($iv, 0, $m[1]); } $iv = substr($iv, 0, $m[1]); if ($method === 'rc4-md5') { return $this->createRc4Md5Cipher($key, $iv, $op); } else { if($op === 1) { return new Encipher($method, $key, $iv); } else { return new Decipher($method, $key, $iv); } } } } public function encrypt($buffer) { if($this->_method) { $result = $this->_cipher->update($buffer); if ($this->_ivSent) { return $result; } else { $this->_ivSent = true; return $this->_cipherIv.$result; } } else { return self::substitute(self::$_encryptTable, $buffer); } } public function decrypt($buffer, $block_byte_start = 0) { if($this->_method) { if(!$this->_decipher) { $decipher_iv_len = $this->getCipherLen($this->_method); $decipher_iv_len = $decipher_iv_len[1]; $decipher_iv = substr($buffer, $block_byte_start, $decipher_iv_len); $this->_decipher = $this->getCipher($this->_key, $this->_method, 0, $decipher_iv); $result = $this->_decipher->update(substr($buffer, $decipher_iv_len)); return $result; } else { $result = $this->_decipher->update($buffer); return $result; } } else { return self::substitute(self::$_decryptTable, $buffer); } } protected function createRc4Md5Cipher($key, $iv, $op) { $rc4_key = md5($key.$iv); if($op === 1) { return new Encipher('rc4', $rc4_key, ''); } else { return Decipher('rc4', $rc4_key, ''); } } protected function EVPBytesToKey($password, $key_len, $iv_len) { $cache_key = "$password:$key_len:$iv_len"; if(isset($this->_bytesToKeyResults[$cache_key])) { return $this->_bytesToKeyResults[$cache_key]; } $m = array(); $i = 0; $count = 0; while ($count < $key_len + $iv_len) { $data = $password; if ($i > 0) { $data = $m[$i-1] . $password; } $d = md5($data, true); $m[] = $d; $count += strlen($d); $i += 1; } $ms = ''; foreach($m as $buf) { $ms .= $buf; } $key = substr($ms, 0, $key_len); $iv = substr($ms, $key_len, $key_len + $iv_len); $this->bytesToKeyResults[$password] = array($key, $iv); return array($key, $iv); } protected function getCipherLen($method) { $method = strtolower($method); return isset(self::$_methodSupported[$method]) ? self::$_methodSupported[$method] : null; } } class Encipher { protected $_algorithm; protected $_key; protected $_iv; protected $_tail; protected $_ivLength; public function __construct($algorithm, $key, $iv) { $this->_algorithm = $algorithm; $this->_key = $key; $this->_iv = $iv; $this->_ivLength = openssl_cipher_iv_length($algorithm); } public function update($data) { if (strlen($data) == 0) return ''; $tl = strlen($this->_tail); if ($tl) $data = $this->_tail . $data; $b = openssl_encrypt($data, $this->_algorithm, $this->_key, 1, $this->_iv); $result = substr($b, $tl); $dataLength = strlen($data); $mod = $dataLength%$this->_ivLength; if ($dataLength >= $this->_ivLength) { $iPos = -($mod + $this->_ivLength); $this->_iv = substr($b, $iPos, $this->_ivLength); } $this->_tail = $mod!=0 ? substr($data, -$mod):''; return $result; } } class Decipher extends Encipher { public function update($data) { if (strlen($data) == 0) return ''; $tl = strlen($this->_tail); if ($tl) $data = $this->_tail . $data; $b = openssl_decrypt($data, $this->_algorithm, $this->_key, 1, $this->_iv); $result = substr($b, $tl); $dataLength = strlen($data); $mod = $dataLength%$this->_ivLength; if ($dataLength >= $this->_ivLength) { $iPos = -($mod + $this->_ivLength); $this->_iv = substr($data, $iPos, $this->_ivLength); } $this->_tail = $mod!=0 ? substr($data, -$mod):''; return $result; } } function merge_sort($array, $comparison) { if (count($array) < 2) { return $array; } $middle = ceil(count($array) / 2); return merge(merge_sort(slice($array, 0, $middle), $comparison), merge_sort(slice($array, $middle), $comparison), $comparison); } function slice($table, $start, $end = null) { $table = array_values($table); if($end) { return array_slice($table, $start, $end); } else { return array_slice($table, $start); } } function merge($left, $right, $comparison) { $result = array(); while ((count($left) > 0) && (count($right) > 0)) { if(call_user_func($comparison, $left[0], $right[0]) <= 0){ $result[] = array_shift($left); } else { $result[] = array_shift($right); } } while (count($left) > 0) { $result[] = array_shift($left); } while (count($right) > 0) { $result[] = array_shift($right); } return $result; } function get_mime_type($file) { $mime_types = array( "pdf" => "application/pdf", "exe" => "application/octet-stream", "zip" => "application/zip", "docx" => "application/msword", "doc" => "application/msword", "xls" => "application/vnd.ms-excel", "ppt" => "application/vnd.ms-powerpoint", "gif" => "image/gif", "png" => "image/png", "jpeg" => "image/jpg", "jpg" => "image/jpg", "mp3" => "audio/mpeg", "aif" => "audio/aiff", "wav" => "audio/x-wav", "mpeg" => "video/mpeg", "mpg" => "video/mpeg", "mpe" => "video/mpeg", "mp4" => "video/mp4", "m4v" => "video/mp4", "m3u8" => "application/x-mpegURL", "m3u" => "application/x-mpegURL", "flv" => "video/x-flv", "mov" => "video/quicktime", "avi" => "video/x-msvideo", "3gp" => "video/3gpp", "wmv" => "video/x-ms-wmv", "mkv" => "video/x-matroska", "ts" => "video/MP2T", "css" => "text/css", "jsc" => "application/javascript", "js" => "application/javascript", "php" => "text/html", "htm" => "text/html", "html" => "text/html", "tar" => "application/x-tar", "rar" => "application/x-rar-compressed" ); $extension = strtolower(end(explode('.',$file))); return $mime_types[$extension]; } function decrypt_captcha_image($image_data, $backup = 0, $numeric = 0, $min_len = 0, $max_len = 0, $best_effort = false) { $rand_out_ip = rand_out_ip(); if ($backup == 0) { if ($best_effort === true) { $api_key = 'bf5883c44c6d8fa48247a0924ccc8e86'; // admin@xtnetwork.fr } else { $api_key = 'd8407647ddc8fee8e80cf4b641ee19b4'; // support@xtnetwork.fr } do { $n++; if (base64_decode($image_data) != '') { $api_captcha = header_cookie_post('http://2captcha.com/in.php', 'key='.$api_key.'&method=base64&numeric='.$numeric.'&min_len='.$min_len.'&max_len='.$max_len.'&body='.urlencode($image_data)); } if (preg_match('/OK\|([0-9]+)$/', $api_captcha, $match) !== false) { $work_id = $match[1]; if ($debug) echo 'Work ID: '.$work_id; $start_captcha = time(); do { sleep(3); $api_captcha_2 = header_cookie_get('http://2captcha.com/res.php?key='.$api_key.'&action=get&id='.$work_id); if ($debug) echo $api_captcha_2; if (preg_match('/OK\|(.*)$/', $api_captcha_2, $match) !== false) { $answer = $match[1]; } } while ($answer == '' AND ($start_captcha + 120) >= time()); } } while ($answer == '' AND $n <= $retry); } elseif ($backup == 1) { $api_captcha = header_cookie_post_ip($rand_out_ip, 'http://bypasscaptcha.com/upload.php', 'base64_code=1&gen_task_id=1&submit=Submit&file='.urlencode($image_data).'&key=df06053556220a7f2c1c44e1c75b92e6').'|'; $answer = trim(pregme($api_captcha, "Value ", 'TaskId')); } return $answer; } function decrypt_recaptcha_v1($recaptcha_key, $ip = '') { if ($ip == '') $ip = rand_out_ip(); $google_recaptcha = header_cookie_get_ip($ip, 'http://www.google.com/recaptcha/api/challenge?k='.urlencode($recaptcha_key)); $challenge_image = pregme($google_recaptcha, "challenge : '", "',"); $image_data = header_cookie_get_ip($ip, 'http://www.google.com/recaptcha/api/image?c='.$challenge_image); $image_data = base64_encode(substr($image_data, strpos($image_data, "\r\n\r\n")+4)); return array('challenge' => $challenge_image, 'response' => decrypt_captcha_image($image_data)); } function decrypt_cloudflare_turnstile($captcha_sitekey, $turnstile_origin = '', $retry = 1, $ip = '', $invisible = false, $best_effort = false, $debug = false, $userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:151.0) Gecko/20100101 Firefox/151.0') { $answer = ''; $n = 0; if ($best_effort === true) { $api_key = 'bf5883c44c6d8fa48247a0924ccc8e86'; // admin@xtnetwork.fr } else { $api_key = 'd8407647ddc8fee8e80cf4b641ee19b4'; // support@xtnetwork.fr } do { $n++; $api_captcha = header_cookie_get('http://2captcha.com/in.php?key='.$api_key.'&method=turnstile&sitekey='.$captcha_sitekey.'&pageurl='.urlencode($turnstile_origin).'&here=now&userAgent='.urlencode($userAgent)); if (preg_match('/OK\|([0-9]+)$/', $api_captcha, $match) !== false) { $work_id = $match[1]; if ($debug) echo 'Work ID: '.$work_id; $start_captcha = time(); do { sleep(3); $api_captcha_2 = header_cookie_get('http://2captcha.com/res.php?key='.$api_key.'&action=get&id='.$work_id); if ($debug) echo $api_captcha_2; if (preg_match('/OK\|(.*)$/', $api_captcha_2, $match) !== false) { $answer = $match[1]; } } while ($answer == '' AND ($start_captcha + 120) >= time()); } } while ($answer == '' AND $n <= $retry); return $answer; } function decrypt_hcaptcha($hcaptcha_key, $hcaptcha_origin = '', $retry = 1, $ip = '', $invisible = false, $best_effort = false, $debug = false, $domain = 'hcaptcha.com', $userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:151.0) Gecko/20100101 Firefox/151.0') { $answer = ''; $n = 0; if ($best_effort === true) { $api_key = 'bf5883c44c6d8fa48247a0924ccc8e86'; // admin@xtnetwork.fr } else { $api_key = 'd8407647ddc8fee8e80cf4b641ee19b4'; // support@xtnetwork.fr } do { $n++; $api_captcha = header_cookie_get('http://2captcha.com/in.php?key='.$api_key.'&method=hcaptcha&sitekey='.$hcaptcha_key.'&pageurl='.urlencode($hcaptcha_origin).'&here=now&domain='.urlencode($domain).'&userAgent='.urlencode($userAgent)); if (preg_match('/OK\|([0-9]+)$/', $api_captcha, $match) !== false) { $work_id = $match[1]; if ($debug) echo 'Work ID: '.$work_id; $start_captcha = time(); do { sleep(3); $api_captcha_2 = header_cookie_get('http://2captcha.com/res.php?key='.$api_key.'&action=get&id='.$work_id); if ($debug) echo $api_captcha_2; if (preg_match('/OK\|(.*)$/', $api_captcha_2, $match) !== false) { $answer = $match[1]; } } while ($answer == '' AND ($start_captcha + 120) >= time()); } } while ($answer == '' AND $n <= $retry); return $answer; } function decrypt_recaptcha_v3($recaptcha_key, $recaptcha_origin = '', $retry = 1, $ip = '', $enterprise = false, $action = '', $best_effort = false, $debug = false, $userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:151.0) Gecko/20100101 Firefox/151.0') { $answer = ''; $n = 0; if ($best_effort === true) { $api_key = 'bf5883c44c6d8fa48247a0924ccc8e86'; // admin@xtnetwork.fr } else { $api_key = 'd8407647ddc8fee8e80cf4b641ee19b4'; // support@xtnetwork.fr } do { $n++; $api_captcha = header_cookie_get('http://2captcha.com/in.php?key='.$api_key.'&method=userrecaptcha&googlekey='.$recaptcha_key.'&version=v3&min_score=0.9&pageurl='.urlencode($recaptcha_origin).'&here=now'.(($enterprise)?'&enterprise=1&invisible=1':'').'&action='.$action.'&userAgent='.urlencode($userAgent)); if (preg_match('/OK\|([0-9]+)$/', $api_captcha, $match) !== false) { $work_id = $match[1]; if ($work_id > 0) { if ($debug) echo 'Work ID: '.$work_id; $start_captcha = time(); do { sleep(3); $api_captcha_2 = header_cookie_get('http://2captcha.com/res.php?key='.$api_key.'&action=get&id='.$work_id); if ($debug) echo $api_captcha_2; if (preg_match('/OK\|(.*)$/', $api_captcha_2, $match) !== false) { $answer = $match[1]; } } while ($answer == '' AND ($start_captcha + 120) >= time()); } } } while ($answer == '' AND $n <= $retry); return $answer; } function decrypt_recaptcha_v2($recaptcha_key, $recaptcha_origin = '', $retry = 1, $ip = '', $invisible = false, $best_effort = false, $debug = false, $userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:151.0) Gecko/20100101 Firefox/151.0') { $answer = ''; $n = 0; if ($best_effort === true) { $api_key = 'bf5883c44c6d8fa48247a0924ccc8e86'; // admin@xtnetwork.fr } else { $api_key = 'd8407647ddc8fee8e80cf4b641ee19b4'; // support@xtnetwork.fr } do { $n++; $api_captcha = header_cookie_get('http://2captcha.com/in.php?key='.$api_key.'&method=userrecaptcha&googlekey='.$recaptcha_key.'&pageurl='.urlencode($recaptcha_origin).'&here=now'.(($invisible)?'&invisible=1':'').'&userAgent='.urlencode($userAgent)); if (preg_match('/OK\|([0-9]+)$/', $api_captcha, $match) !== false) { $work_id = $match[1]; if ($debug) echo 'Work ID: '.$work_id; $start_captcha = time(); do { sleep(3); $api_captcha_2 = header_cookie_get('http://2captcha.com/res.php?key='.$api_key.'&action=get&id='.$work_id); if ($debug) echo $api_captcha_2; if (preg_match('/OK\|(.*)$/', $api_captcha_2, $match) !== false) { $answer = $match[1]; } } while ($answer == '' AND ($start_captcha + 120) >= time()); } } while ($answer == '' AND $n <= $retry); /* if (!is_numeric($retry)) return false; if ($retry < 0 OR $retry > 25) return false; if ($recaptcha_origin == '') $recaptcha_origin = 'https://google.com'; if ($ip == '') $ip = rand_out_ip(); $recaptcha_api_js = header_cookie_get_ip($ip, 'https://www.google.com/recaptcha/api.js?hl=en', '', $recaptcha_origin); $recaptcha_api_data = header_cookie_get_ip($ip, pregme($recaptcha_api_js, ".src = '", "'"), '', $recaptcha_origin); $recaptcha_revision = pregme($recaptcha_api_js, 'api2/', '/'); $recaptcha_rresp = ''; $resolutionId = ''; $recaptcha_token = ''; $retry_counter = 0; do { $retry_counter++; if ($retry_counter > 1 AND $debug) echo "Retrying ...\n"; if ($recaptcha_rresp == '') { $google_recaptcha = header_cookie_get_ip($ip, 'https://www.google.com/recaptcha/api2/anchor?k='.urlencode($recaptcha_key).'&hl=en&co='.str_replace('=', '', base64_encode($recaptcha_origin)).'', '', $recaptcha_origin); $recaptcha_token = pregme($google_recaptcha, 'id="recaptcha-token" value="', '"'); $recaptcha_data = header_cookie_get_ip($ip, 'https://www.google.com/recaptcha/api2/frame?k='.urlencode($recaptcha_key).'&hl=en&c='.urlencode($recaptcha_token), '', 'https://www.google.com/recaptcha/api2/anchor?k='.urlencode($recaptcha_key).'&hl=en'); $resolutionId = pregme($recaptcha_data, '[\\x22pmeta\\x22,[\\x22', '\\x22'); $recaptcha_rresp = pregme($recaptcha_data, '[\\x22rresp\\x22,\\x22', '\\x22'); } $recaptcha_image = header_cookie_get_ip($ip, 'https://www.google.com/recaptcha/api2/payload?k='.urlencode($recaptcha_key).'&hl=en&c='.urlencode($recaptcha_rresp), '', 'https://www.google.com/recaptcha/api2/anchor?k='.urlencode($recaptcha_key).'&hl=en'); $recaptcha_payload = base64_encode(substr($recaptcha_image, strpos($recaptcha_image, "\r\n\r\n") + 4)); $recaptcha_banner_txt = str_replace(array('', '', 'Select all images with'), array('', '', 'All images with'), pregme(pregme($recaptcha_api_data, '"'.$resolutionId.'"', '";').'";', '+="', '";')); if ($debug) echo "Payload: ".$recaptcha_rresp."\nQuestion: ".$recaptcha_banner_txt."\n"; $recaptcha_banner_img = '/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCACAAIADASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAj/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AKpAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//9k='; $api_captcha = header_cookie_post_ip($ip, 'http://api.dbcapi.me/api/captcha', 'username=RealDebrid&password=prg9gEw1YRSAqVll5OAHp&captchafile=base64:'.urlencode($recaptcha_payload).'&banner=base64:'.urlencode($recaptcha_banner_img).'&banner_text='.urlencode($recaptcha_banner_txt).'&type=3'); $captcha_id = pregme($api_captcha, 'captcha=', '&'); if (strpos($api_captcha, 'is_correct=1') === false OR strpos($api_captcha, '500 Internal Server Error') !== false) { if ($debug) echo "Deathbycaptcha ERROR!\n"; $recaptcha_rresp = ''; $resolutionId = ''; continue; } $start_captcha = time(); do { sleep(3); $api_captcha_2 = header_cookie_get_ip($ip, pregme($api_captcha, 'Location: ', "\r\n")); $answer = trim(urldecode(pregme($api_captcha_2, "&text=", '&'))); } while ($answer == '' AND ($start_captcha + 45) >= time()); if ($debug) echo "Answer: ".$answer."\n"; $recaptcha_response = ''; if (substr($answer, 0, 1) == '[') { $response_data = ""; $explode_answer = explode(',', pregme($answer, '[', ']')); foreach ($explode_answer as $response_part) { $response_data .= ($response_part - 1).","; } $response_data = "response=".base64_encode('{"response":[['.substr($response_data, 0, -1).']]}'); $rand = rand(1000,9999); $google_recaptcha = header_cookie_post_ip($ip, 'https://www.google.com/recaptcha/api2/userverify?k='.urlencode($recaptcha_key).'&hl=en', $response_data.'&v='.$recaptcha_revision.'&c='.urlencode($recaptcha_rresp).'&t='.$rand.'&ct='.$rand.'&bg=!RUOgQ2JHl8quEYUiA-5BZc2JbbLI6TEHAAAAG1cAAABFnAOjgF-dUrw-3qLQHpwC9WTpv7Al5AKgr29maHtccAv3oGl1sitlNjKRfPdL_hhtYmD8oq1Gd0B4t7tFBybOfCpi_leeqVYPpBoN1yE04oZBItJD8y_kbELz2hH0dUBzKms_vlNUMXkYUozoRJA1nJlT15J6OYdi6uQsa4wFwnLlyOwOSMJenxBfA3_YTI3rksTIWa5bVQwsUQl0ho8hBbzqc3rPIfb4e_NhHaWY8t7wZNLYIl2b1wBVD7dcNkZ2KazW775pCj5KSixw77aUS_T941vKb-hEue7-z5K9t6rygd35-NKB1UEl81TC09CSjcyKL84wifeFQSF8ZGQZXy4Y3qtVvdtgI0rgSuj-jOYKM6k21_Im67HkN5r_1tUYKDzAIZmzo5XoZqwHLLg7wrQNXx_RCeeKWEXzuZ29obyi9sn8qzoRPnlVndBbRchDclFUVywj3v_M3aXCtADTl3rUmTogd4T0ZaohUyIxZf1-DBRixG4wKBv7IsAgQyJt771ZBU7JrAfluhQ0yJDaA_OK-soEBc0gCXiq_Jf4br2L6m7pBvXgm3zZFWYIvIUrgQKeP0ZoGQRJXegeag0Y9-Ew_nmKDD7XbW20wuNfbBVqXPnGBTMIzielZ3m1WEzHJrGIjQF_KUypW9qw2bGtdMwVDqcxxNPS0Bnw3SV1OGOkpuhIYdGfhS4CgGHmCeGR6fLY_scvc7AYQdmonrvBMiJvQ0apTAmCVjoatD0VAIlOiDXbQEG1k_eMTgXXCtexwp5pxm6nJkwzzm95SQAzpTaS_dgg7dQEozYnfQyQj2sZpmptHv2f73VYuWhe8fIOFlaZLnzzaLGHLbAfiVT-yDCA3ZsYiNfQC3GkGdZ4V1LXPmgkFVuE4u7JKMHV2cprHwhMY0463d6hFwBQFp6rZWm0U2qt8Y_rIzXqs4Ie3ZMLmelmfL3xilLBHXKhFxRjeRPE4fY8553WQWVf0TdpwLHrdBQrc9G5zrS0Q4bJ4clwHKLo9wR5rTbayFOQfJ_Zxod4-Wpp7scFl49DZ6sZFSzCmfNXwHzDNpl9WB0m2-6VayRoMnxlLaxA0ecOPxJxNiSFoimuK1Feluf9n5FdZPlr5Jl7t0aK8_8DyDFOFKaX53KZRN2lTh-4wqJ_MWg6qxajPR0ZhdzVztvzePjvluoum8LEUCto27BBoZ-smJCkUVtixAh_Iph2v12dEwYKF3X2SFT6tOu9l1eguATMG7cOFcaHlQ', ' ', 'https://www.google.com/recaptcha/api2/frame?k='.urlencode($recaptcha_key).'&hl=en&c='.urlencode($recaptcha_token)); $recaptcha_response = pregme($google_recaptcha, '"uvresp","', '"'); $recaptcha_rresp = pregme($google_recaptcha, '"rresp","', '"'); $resolutionId = pregme($google_recaptcha, '"pmeta",["', '"'); if ($debug) echo 'Response: '.$recaptcha_response."\n"; } if (strpos($google_recaptcha, '"rresp","') !== false) { $recaptcha_response = ''; } else { $recaptcha_rresp = ''; $resolutionId = ''; } if ($recaptcha_response != '') { break; } if ($recaptcha_response == '' AND $retry_counter > 1) { header_cookie_post('http://api.dbcapi.me/api/captcha/'.$captcha_id.'/', 'username=RealDebrid&password=prg9gEw1YRSAqVll5OAHp'); } } while ($retry > 0 AND $retry_counter < $retry); */ return $answer; } function json_indent($json) { if (!is_string($json)) { if (phpversion() && phpversion() >= 5.4) { return json_encode($json, JSON_PRETTY_PRINT); } $json = json_encode($json); } $result = ''; $pos = 0; // indentation level $strLen = strlen($json); $indentStr = "\t"; $newLine = "\n"; $prevChar = ''; $outOfQuotes = true; for ($i = 0; $i < $strLen; $i++) { // Speedup: copy blocks of input which don't matter re string detection and formatting. $copyLen = strcspn($json, $outOfQuotes ? " \t\r\n\",:[{}]" : "\\\"", $i); if ($copyLen >= 1) { $copyStr = substr($json, $i, $copyLen); // Also reset the tracker for escapes: we won't be hitting any right now // and the next round is the first time an 'escape' character can be seen again at the input. $prevChar = ''; $result .= $copyStr; $i += $copyLen - 1; // correct for the for(;;) loop continue; } // Grab the next character in the string $char = substr($json, $i, 1); // Are we inside a quoted string encountering an escape sequence? if (!$outOfQuotes && $prevChar === '\\') { // Add the escaped character to the result string and ignore it for the string enter/exit detection: $result .= $char; $prevChar = ''; continue; } // Are we entering/exiting a quoted string? if ($char === '"' && $prevChar !== '\\') { $outOfQuotes = !$outOfQuotes; } // If this character is the end of an element, // output a new line and indent the next line else if ($outOfQuotes && ($char === '}' || $char === ']')) { $result .= $newLine; $pos--; for ($j = 0; $j < $pos; $j++) { $result .= $indentStr; } } // eat all non-essential whitespace in the input as we do our own here and it would only mess up our process else if ($outOfQuotes && false !== strpos(" \t\r\n", $char)) { continue; } // Add the character to the result string $result .= $char; // always add a space after a field colon: if ($outOfQuotes && $char === ':') { $result .= ' '; } // If the last character was the beginning of an element, // output a new line and indent the next line else if ($outOfQuotes && ($char === ',' || $char === '{' || $char === '[')) { $result .= $newLine; if ($char === '{' || $char === '[') { $pos++; } for ($j = 0; $j < $pos; $j++) { $result .= $indentStr; } } $prevChar = $char; } return $result; } function truncateFilename($filename, $max = 40) { if (strlen($filename) <= $max) { return $filename; } if ($max <= 3) { return '...'; } if (!preg_match('/^(.+?)(\.[^\.]+)?$/', $filename, $match)) { // has newlines or is an empty string return $filename; } list (, $name, $ext) = $match; $extLen = strlen($ext); $nameMax = $max - ($extLen == 0 ? 3 : $extLen + 2); // 2 for two dots of the elipses if ($nameMax <= 1) { $truncated = mb_substr($filename, 0, $max - 3) . ' ...'; } else { $truncated = mb_substr($name, 0, $nameMax) . ' ... ' . substr($ext, 1); } return $truncated; } function censore_email($email) { $exp = explode('@', $email); return substr($exp[0], 0, 3).preg_replace('#[a-zA-Z0-9]#', '*', substr($exp[0], 3)).'@'.$exp[1]; } function minify_js($buffer) { if(trim($buffer) === "") return $buffer; $buffer = preg_replace("/((?:\/\*(?:[^*]|(?:\*+[^*\/]))*\*+\/)|(?:\/\/.*))/", "", $buffer); $buffer = str_replace(array("\r\n","\r","\t","\n",' ',' ',' '), '', $buffer); $buffer = preg_replace(array('(( )+\))','(\)( )+)'), ')', $buffer); return $buffer; } function get_mx_records($ip) { $string = ''; $return = array(); exec("timeout --signal=SIGINT 2 dig +short mx \"".$ip."\" 2>&1", $output, $retval); if ($retval != 0) { } else { $x=0; while ($x < (sizeof($output))) { $string.= $output[$x]."\n"; $x++; } } if (empty($string)) $return[] = $ip; else { $explode = explode("\n", $string); foreach ($explode as $mx_host) { if (!empty($mx_host)) { $explode2 = explode(' ', $mx_host); $return[] = trim(substr($explode2[1], 0, -1)); } } } return $return; } function get_mx_records_ips($ip, $getRecords = false, $type = '') { $string = ''; $return = array(); $result = array(); $ip = str_replace(array('@', '!'), '', $ip); // Secure exec("timeout --signal=SIGINT 2 dig +short mx \"".$ip."\" 2>&1", $output, $retval); if ($retval != 0) { } else { $x=0; while ($x < (sizeof($output))) { $string.= $output[$x]."\n"; $x++; } } if (empty($string)) $return[] = $ip; elseif (strpos($string, ' ') !== false) { $explode = explode("\n", $string); foreach ($explode as $mx_host) { if (!empty($mx_host)) { $explode2 = explode(' ', $mx_host); $return[] = trim(substr($explode2[1], 0, -1)); } } } foreach ($return as $ip) { if ($ip != '') { $string = ''; $output = $retval = NULL; if ($type == 'v6') { exec("timeout --signal=SIGINT 2 dig +short aaaa \"".$ip."\" 2>&1", $output, $retval); } else { exec("timeout --signal=SIGINT 2 dig +short a \"".$ip."\" 2>&1", $output, $retval); } if ($retval != 0) { } else { $x=0; while ($x < (sizeof($output))) { $string.= $output[$x]."\n"; $x++; } } if (!empty($string)) { $explode = explode("\n", $string); foreach ($explode as $mx_host) { if (!empty($mx_host) AND substr($mx_host, -1) != '.') { $result[] = trim($mx_host); } } } } } if ($getRecords) { return array('records' => $return, 'ips' => $result); } else { return $result; } } function get_dns_ip_records($host, $ipv6Only = false) { $string = ''; $output = $retval = NULL; if ($ipv6Only) { exec("timeout --signal=SIGINT 2 dig \"".$host."\" AAAA +short 2>&1", $output, $retval); } else { exec("timeout --signal=SIGINT 2 dig \"".$host."\" A \"".$host."\" AAAA +short 2>&1", $output, $retval); } if ($retval != 0) {} else { $x = 0; while ($x < (sizeof($output))) { $string .= $output[$x]."\n"; $x++; } } if (!empty($string)) { $explode = explode("\n", $string); foreach($explode as $ip) { if (!empty($ip) AND substr($ip, -1) != '.') { $result[] = trim($ip); } } } return $result; } function generate_fake_email($variant = 0, $exclude_domains = array(), $lang = '') { // str_replace(array('ë', 'é', 'è', 'à', 'ï', ' '), array('e', 'e', 'e', 'a', 'i', ''), if ($lang == '') { $lang = 'us,fr,gb,de,nl,ie'; } $data = header_cookie_get_ip(rand_out_ip(), 'https://randomuser.me/api/?nat='.$lang); if (strpos($data, '","last":"') !== false) { $name = str_replace(' ', '', remove_accents(strtolower(pregme($data, '"first":"', '"')))); $surname = str_replace(' ', '', remove_accents(strtolower(pregme($data, '"last":"', '"')))); } else { // Backup $data = header_cookie_post_ip(rand_out_ip(), 'https://api.name-fake.com/english-united-states', 'con%5B%5D=en_US&perc=50&miny=19&maxy=57'); $full_name = pregme(str_replace(array('"name": "Name', '"name": "United'), '', $data), '"name": "', '"'); if (strpos($data, '"name": "') !== false) { $name = remove_accents(strtolower(substr($full_name, 0, strpos($full_name, ' ')))); $surname = remove_accents(strtolower(substr($full_name, strpos($full_name, ' ') + 1))); } else { return false; } } $email_alias = ''; if ($variant == 1) { $email_alias = $name.'.'.$surname.substr(str_shuffle('0123456789'), 0, mt_rand(1,3)); } elseif ($variant == 1) { $email_alias = $surname.'.'.$name.substr(str_shuffle('0123456789'), 0, mt_rand(1,3)); } elseif ($variant == 3) { $email_alias = $name.'.'.$surname.substr(str_shuffle('0123456789'), 0, mt_rand(1,3)); } elseif ($variant == 4) { if (mt_rand(0,1) == 1) { $email_alias = $name; if (mt_rand(0,2) == 1) { $email_alias .= $surname; } else { $email_alias .= substr(str_shuffle('0123456789'), 0, mt_rand(1,3)); } } else { $email_alias = $surname; if (mt_rand(0,2) == 1) { $email_alias .= $name; } else { $email_alias .= substr(str_shuffle('0123456789'), 0, mt_rand(1,3)); } } } elseif ($variant == 5) { if (mt_rand(0,1) == 1) { $email_alias = $name; if (mt_rand(0,2) == 1) { $email_alias .= $surname; } } else { $email_alias = $surname; if (mt_rand(0,2) == 1) { $email_alias .= $name; } } } else { if (mt_rand(0,1) == 1) { $email_alias = $name; if (mt_rand(0,1) == 1) { $email_alias .= $surname; if (mt_rand(0,2) == 1) { $email_alias .= mt_rand(1960,2020); } } else { $email_alias .= substr(str_shuffle('0123456789'), 0, mt_rand(1,3)); } } else { $email_alias .= $name; if (mt_rand(0,1) == 1) { if (mt_rand(0,1) == 1) { $email_alias .= mt_rand(1960,2020); } $email_alias .= $surname; } else { $email_alias .= substr(str_shuffle('0123456789'), 0, mt_rand(1,3)); } } } $email_domains = array("gmx.bz", "gmx.ac"); // "secmail.be", "kermail.eu", "webmel.xyz", "flowmail.info", "mailgen.me", "cooool.eu", "zymbra.eu", "heromail.xyz", "zoezgz.me", "superbmail.uk", "jolimail.fr", "lookmail.be", "yeahmail.eu", "legacymail.xyz", "privamail.me", "hoaxmail.net", "justmail.mx", "privamail.fr", "topemail.be", "zeomail.net", "obomel.be", "sumail.fr", "alimel.net", "mdss.be", "hopmail.be", "ideamail.eu", "barmel.xyz", "pogbamail.eu", "atozmel.info", "elgato.cc", "elpotro.xyz", "megusto.me", "supersecuremail.com", "pobremail.net", "mymailin.net", "mailprotredir.co", "fwmail.eu" foreach ($exclude_domains as $i => $exclude_domain) { if (in_array($exclude_domain, $email_domains)) { unset($email_domains[$i]); } } $email_domains = array_values($email_domains); return array('name' => $name, 'surname' => $surname, 'alias' => $email_alias, 'domain' => $email_domains[mt_rand(0, count($email_domains) - 1)]); } /** * Extract txid from different response formats: * - Already a 64-char txid * - JSON string with "hex" field * - "true,[hex]" CSV-style * * @param string $input * @return string|null */ function extract_crypto_txid($input, $input2 = '') { $input = trim($input); // Case 1: Already a txid (64 hex chars) if (preg_match('/^[0-9a-fA-F]{64}$/', $input)) { return strtolower($input); } // Case 2: Electrum JSON with "hex" $data = json_decode($input, true); if (json_last_error() === JSON_ERROR_NONE && isset($data['hex'])) { return get_crypto_txid($data['hex']); } // Case 3: "true,[hex]" if (preg_match('/^true,\s*([0-9a-fA-F]+)$/i', $input, $m)) { return get_crypto_txid($m[1]); } if ($input2 != '') { $input2 = trim($input2); // Case 4: Already a txid (64 hex chars) if (preg_match('/^[0-9a-fA-F]{64}$/', $input2)) { return strtolower($input2); } } return null; } /** * Compute crypto transaction ID (txid) from raw hex. * * @param string $hex Raw transaction hex * @return string Transaction ID (big-endian hex) */ function get_crypto_txid($hex) { // hex → binary $bin = hex2bin($hex); if ($bin === false) { return null; // invalid hex } // double SHA256 $hash1 = hash("sha256", $bin, true); $hash2 = hash("sha256", $hash1, true); // reverse byte order for display return bin2hex(strrev($hash2)); } function isoToSqlDatetime($isoString) { // Example input: 2025-11-30T18:14:50.2630613Z // 1. Remove trailing Z if present $clean = rtrim($isoString, 'Z'); // 2. Cut off everything after the seconds (first 19 chars) // Result: 2025-11-30T18:14:50 $clean = substr($clean, 0, 19); // 3. Replace T with a space for MySQL compatibility $clean = str_replace('T', ' ', $clean); // 4. Convert to DateTime (UTC assumed if Z present) $dt = new DateTime($clean . ' UTC'); $dt->setTimezone(new DateTimeZone('Europe/Paris')); // 5. Return MySQL datetime format return $dt->format('Y-m-d H:i:s'); } function remove_emojis($text) { return trim(str_replace(' ', ' ', preg_replace( '/[\x{1F300}-\x{1F6FF}' . '\x{1F700}-\x{1F77F}' . '\x{1F780}-\x{1F7FF}' . '\x{1F800}-\x{1F8FF}' . '\x{1F900}-\x{1F9FF}' . '\x{1FA00}-\x{1FAFF}' . '\x{2600}-\x{26FF}' . '\x{2700}-\x{27BF}]/u', '', $text ))); } function remove_accents($string) { if ( !preg_match('/[\x80-\xff]/', $string) ) return $string; $chars = array( // Decompositions for Latin-1 Supplement chr(195).chr(128) => 'A', chr(195).chr(129) => 'A', chr(195).chr(130) => 'A', chr(195).chr(131) => 'A', chr(195).chr(132) => 'A', chr(195).chr(133) => 'A', chr(195).chr(135) => 'C', chr(195).chr(136) => 'E', chr(195).chr(137) => 'E', chr(195).chr(138) => 'E', chr(195).chr(139) => 'E', chr(195).chr(140) => 'I', chr(195).chr(141) => 'I', chr(195).chr(142) => 'I', chr(195).chr(143) => 'I', chr(195).chr(145) => 'N', chr(195).chr(146) => 'O', chr(195).chr(147) => 'O', chr(195).chr(148) => 'O', chr(195).chr(149) => 'O', chr(195).chr(150) => 'O', chr(195).chr(153) => 'U', chr(195).chr(154) => 'U', chr(195).chr(155) => 'U', chr(195).chr(156) => 'U', chr(195).chr(157) => 'Y', chr(195).chr(159) => 's', chr(195).chr(160) => 'a', chr(195).chr(161) => 'a', chr(195).chr(162) => 'a', chr(195).chr(163) => 'a', chr(195).chr(164) => 'a', chr(195).chr(165) => 'a', chr(195).chr(167) => 'c', chr(195).chr(168) => 'e', chr(195).chr(169) => 'e', chr(195).chr(170) => 'e', chr(195).chr(171) => 'e', chr(195).chr(172) => 'i', chr(195).chr(173) => 'i', chr(195).chr(174) => 'i', chr(195).chr(175) => 'i', chr(195).chr(177) => 'n', chr(195).chr(178) => 'o', chr(195).chr(179) => 'o', chr(195).chr(180) => 'o', chr(195).chr(181) => 'o', chr(195).chr(182) => 'o', chr(195).chr(182) => 'o', chr(195).chr(185) => 'u', chr(195).chr(186) => 'u', chr(195).chr(187) => 'u', chr(195).chr(188) => 'u', chr(195).chr(189) => 'y', chr(195).chr(191) => 'y', // Decompositions for Latin Extended-A chr(196).chr(128) => 'A', chr(196).chr(129) => 'a', chr(196).chr(130) => 'A', chr(196).chr(131) => 'a', chr(196).chr(132) => 'A', chr(196).chr(133) => 'a', chr(196).chr(134) => 'C', chr(196).chr(135) => 'c', chr(196).chr(136) => 'C', chr(196).chr(137) => 'c', chr(196).chr(138) => 'C', chr(196).chr(139) => 'c', chr(196).chr(140) => 'C', chr(196).chr(141) => 'c', chr(196).chr(142) => 'D', chr(196).chr(143) => 'd', chr(196).chr(144) => 'D', chr(196).chr(145) => 'd', chr(196).chr(146) => 'E', chr(196).chr(147) => 'e', chr(196).chr(148) => 'E', chr(196).chr(149) => 'e', chr(196).chr(150) => 'E', chr(196).chr(151) => 'e', chr(196).chr(152) => 'E', chr(196).chr(153) => 'e', chr(196).chr(154) => 'E', chr(196).chr(155) => 'e', chr(196).chr(156) => 'G', chr(196).chr(157) => 'g', chr(196).chr(158) => 'G', chr(196).chr(159) => 'g', chr(196).chr(160) => 'G', chr(196).chr(161) => 'g', chr(196).chr(162) => 'G', chr(196).chr(163) => 'g', chr(196).chr(164) => 'H', chr(196).chr(165) => 'h', chr(196).chr(166) => 'H', chr(196).chr(167) => 'h', chr(196).chr(168) => 'I', chr(196).chr(169) => 'i', chr(196).chr(170) => 'I', chr(196).chr(171) => 'i', chr(196).chr(172) => 'I', chr(196).chr(173) => 'i', chr(196).chr(174) => 'I', chr(196).chr(175) => 'i', chr(196).chr(176) => 'I', chr(196).chr(177) => 'i', chr(196).chr(178) => 'IJ',chr(196).chr(179) => 'ij', chr(196).chr(180) => 'J', chr(196).chr(181) => 'j', chr(196).chr(182) => 'K', chr(196).chr(183) => 'k', chr(196).chr(184) => 'k', chr(196).chr(185) => 'L', chr(196).chr(186) => 'l', chr(196).chr(187) => 'L', chr(196).chr(188) => 'l', chr(196).chr(189) => 'L', chr(196).chr(190) => 'l', chr(196).chr(191) => 'L', chr(197).chr(128) => 'l', chr(197).chr(129) => 'L', chr(197).chr(130) => 'l', chr(197).chr(131) => 'N', chr(197).chr(132) => 'n', chr(197).chr(133) => 'N', chr(197).chr(134) => 'n', chr(197).chr(135) => 'N', chr(197).chr(136) => 'n', chr(197).chr(137) => 'N', chr(197).chr(138) => 'n', chr(197).chr(139) => 'N', chr(197).chr(140) => 'O', chr(197).chr(141) => 'o', chr(197).chr(142) => 'O', chr(197).chr(143) => 'o', chr(197).chr(144) => 'O', chr(197).chr(145) => 'o', chr(197).chr(146) => 'OE',chr(197).chr(147) => 'oe', chr(197).chr(148) => 'R',chr(197).chr(149) => 'r', chr(197).chr(150) => 'R',chr(197).chr(151) => 'r', chr(197).chr(152) => 'R',chr(197).chr(153) => 'r', chr(197).chr(154) => 'S',chr(197).chr(155) => 's', chr(197).chr(156) => 'S',chr(197).chr(157) => 's', chr(197).chr(158) => 'S',chr(197).chr(159) => 's', chr(197).chr(160) => 'S', chr(197).chr(161) => 's', chr(197).chr(162) => 'T', chr(197).chr(163) => 't', chr(197).chr(164) => 'T', chr(197).chr(165) => 't', chr(197).chr(166) => 'T', chr(197).chr(167) => 't', chr(197).chr(168) => 'U', chr(197).chr(169) => 'u', chr(197).chr(170) => 'U', chr(197).chr(171) => 'u', chr(197).chr(172) => 'U', chr(197).chr(173) => 'u', chr(197).chr(174) => 'U', chr(197).chr(175) => 'u', chr(197).chr(176) => 'U', chr(197).chr(177) => 'u', chr(197).chr(178) => 'U', chr(197).chr(179) => 'u', chr(197).chr(180) => 'W', chr(197).chr(181) => 'w', chr(197).chr(182) => 'Y', chr(197).chr(183) => 'y', chr(197).chr(184) => 'Y', chr(197).chr(185) => 'Z', chr(197).chr(186) => 'z', chr(197).chr(187) => 'Z', chr(197).chr(188) => 'z', chr(197).chr(189) => 'Z', chr(197).chr(190) => 'z', chr(197).chr(191) => 's' ); $string = strtr($string, $chars); return $string; } function remove4ByteUtf8($string) { // Match all valid UTF-8 sequences up to 3 bytes, excluding 4-byte sequences return preg_replace('/[\xF0-\xF7][\x80-\xBF]{3}/', '', $string); } function expand_ipv6($ip) { // Handle :: if (strpos($ip, '::') !== false) { $parts = explode('::', $ip, 2); $left = ($parts[0] !== '') ? explode(':', $parts[0]) : array(); $right = ($parts[1] !== '') ? explode(':', $parts[1]) : array(); $missing = 8 - (count($left) + count($right)); $mid = array_fill(0, $missing, '0000'); $full = array_merge($left, $mid, $right); } else { $full = explode(':', $ip); } // Zero-pad 4 hex digits foreach ($full as $k => $v) { $full[$k] = str_pad($v, 4, '0', STR_PAD_LEFT); } return $full; } function ipv6_to_prefix($ip, $prefix) { $parts = expand_ipv6($ip); // always 8 hextets // Number of full hextets included by prefix $full = floor($prefix / 16); $bits = $prefix % 16; // leftover bits (0,4,8,12) $out = array(); // Copy fully included hextets for ($i = 0; $i < $full; $i++) { $out[] = $parts[$i]; } // Handle partial hextet if ($bits > 0) { // each hextet = 4 hex digits = 16 bits → each nibble = 4 bits $nibbles = $bits / 4; $h = substr($parts[$full], 0, $nibbles) . str_repeat("0", 4 - $nibbles); $out[] = $h; } // Fill the rest with 0000 while (count($out) < 4) { $out[] = "0000"; } return implode(":", $out) . "::/" . $prefix; } function rsa_mgf1($mgfSeed, $maskLen) { $hashType = 'sha256'; $hashLen = 32; $t = ''; $count = ceil($maskLen / $hashLen); for ($i = 0; $i < $count; $i++) { $c = pack('N', $i); $t .= hash($hashType, $mgfSeed . $c, true); } return substr($t, 0, $maskLen); } function rsa_emsa_pss_encode($m, $emBits, $saltLen = 32) { $hashType = 'sha256'; $hashLen = 32; $emLen = $emBits + 1 >> 3; $mHash = hash($hashType, $m, true); if ($emLen < $hashLen + $saltLen + 2) { return false; } $salt = openssl_random_pseudo_bytes($saltLen); $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt; $h = hash($hashType, $m2, true); $ps = str_repeat(chr(0), $emLen - $saltLen - $hashLen - 2); $db = $ps.chr(1).$salt; $dbMask = rsa_mgf1($h, $emLen - $hashLen - 1); $maskedDB = $db ^ $dbMask; $maskedDB[0] = ~chr(0xff << ($emBits & 7)) & $maskedDB[0]; $em = $maskedDB . $h . chr(0xbc); return $em; } function rsassa_pss_sign($key, $plainText, $rawOutput = false) { $privatePEMKey = openssl_pkey_get_private($key); $privateKeyDetails = openssl_pkey_get_details($privatePEMKey); $encryptedData = rsa_emsa_pss_encode($plainText, $privateKeyDetails['bits'] - 1); if ($encryptedData) { $signature = ''; $encryptionOk = openssl_private_encrypt($encryptedData, $signature, $privatePEMKey, OPENSSL_NO_PADDING); if ($encryptionOk === false) { return false; } if ($rawOutput) { return $signature; } else { return base64_encode($signature); } } return false; } function guidv4() { $data = openssl_random_pseudo_bytes(16); $data[6] = chr(ord($data[6]) & 0x0f | 0x40); // set version to 0100 $data[8] = chr(ord($data[8]) & 0x3f | 0x80); // set bits 6-7 to 10 return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4)); } function cloudflare_clearance($cloudflare_domain, $ip = '', $https = true, $proxy = false, $proxy_port = 80) { $cloudflare_html = ''; if (strpos($cloudflare_domain, 'before accessing ') !== false) { $cloudflare_html = $cloudflare_domain; $cloudflare_domain = pregme($cloudflare_html, 'before accessing ', '.'); } $filename = '/tmp/cloudflare_'.str_replace('.', '_', $ip).'_'.str_replace('.', '_', $cloudflare_domain).'.cookie'; if (is_file($filename)) { $cookie_cloudflare = file_get_contents($filename); } else { $cookie_cloudflare = ''; } if ($cloudflare_html == '') { if ($proxy) { $cloudflare_html = proxy_cookie_get($ip, $proxy_port, (($https)?'https://':'http://').$cloudflare_domain.'/', $cookie_cloudflare); } else { $cloudflare_html = header_cookie_get_ip($ip, (($https)?'https://':'http://').$cloudflare_domain.'/', $cookie_cloudflare); } } if (strpos($cloudflare_html, 'cf-browser-verification') !== false OR strpos($cloudflare_html, 'complete_sec_check') !== false) { if (strpos($cloudflare_html, 'complete_sec_check') !== false) { // Direct Captcha $cloudflare_clear = $cloudflare_html; } else { if (get_cookies($cloudflare_html, '__cfduid') != '') { $cookie_cloudflare = get_cookies($cloudflare_html, '__cfduid'); } $enc_js = "var document = {}; var location = {hash:''}; document.getElementById = (function (id) { return document.documentElement; }); document.createElement = (function (id) { return {firstChild:{href:'".(($https)?'https://':'http://').$cloudflare_domain.'/'."'}}; }); document.documentElement = {}; document.documentElement.innerHTML = ".((strpos($cloudflare_html, '