�PNG  IHDR22?�� pHYs  �� OiCCPPhotoshop ICC profilexڝSgTS�=���BK���KoR RB���&*! J�!��Q�EEȠ�����Q, � ��!���������{�kּ������>���� �H3Q5� �B�������.@� $p�d!s�#�~<<+"��x� �M��0���B�\���t�8K�@z�B�@F���&S�`�cb�P-`'������{[�!�� e�Dh;��V�EX0fK�9�-0IWfH���� � 0Q��){`�##x��F�W<�+��*x��<�$9E�[-qWW.(�I+6aa�@.�y�2�4���������x����6��_-��"bb���ϫp@�t~��,/��;�m��%�h^ �u��f�@����W�p�~<�5�j>{�-�]c�K'Xt����o��(�h���w��?�G�%�fI�q^D$.Tʳ?�D��*�A��,���� �`6�B$��BB d�r`)��B(�Ͱ*`/�@4�Qh��p.�U�=p�a��(�� A�a!ڈb�X#����!�H�$ ɈQ"K�5H1R�T UH�=r9�\F��;�2����G1���Q=� �C��7�F� �dt1�����r�=�6��Ыhڏ>C�0��3�l0.��B�8, �c˱"� ���V����cϱw�E� 6wB aAHXLXN�H� $4� 7 �Q�'"��K�&���b21�XH,#��/{�C�7$�C2'��I��T��F�nR#�,��4H#���dk�9�, +ȅ����3��!�[ �b@q��S�(R�jJ��4�e�2AU��Rݨ�T5�ZB���R�Q��4u�9̓IK�����hh�i��t�ݕN��W���G���w ��Ljg(�gw��L�Ӌ�T071���oUX*�*|�� �J�&�*/T����ު U�U�T��^S}�FU3S� Ԗ�U��P�SSg�;���g�oT?�~Y��Y�L�OC�Q��_�� c�x,!k ��u�5�&���|v*�����=���9C3J3W�R�f?�q��tN �(���~���)�)�4L�1e\k����X�H�Q�G�6������E�Y��A�J'\'Gg����S�Sݧ �M=:��.�k���Dw�n��^��Lo��y���}/�T�m���G X� $� �<�5qo</���QC]�@C�a�a�ᄑ��<��F�F�i�\�$�m�mƣ&&!&KM�M�RM��)�;L;L���͢�֙5�=1�2��כ߷`ZxZ,����eI��Z�Yn�Z9Y�XUZ]�F���%ֻ�����N�N���gð�ɶ�����ۮ�m�}agbg�Ů��}�}��= ���Z~s�r:V:ޚΜ�?}����/gX���3��)�i�S��Ggg�s�󈋉K��.�>.���Ƚ�Jt�q]�z���������ۯ�6�i�ܟ�4�)�Y3s���C�Q��? ��0k߬~OCO�g��#/c/�W�װ��w��a�>�>r��>�<7�2�Y_�7��ȷ�O�o�_��C#�d�z����%g��A�[��z|!��?:�e����A���AA�������!h�쐭!��Α�i�P~���a�a��~ '���W�?�p�X�1�5w��Cs�D�D�Dޛg1O9�-J5*>�.j<�7�4�?�.fY��X�XIlK9.*�6nl������� �{�/�]py�����.,:�@L�N8��A*��%�w%� y��g"/�6ш�C\*N�H*Mz�쑼5y$�3�,幄'���L Lݛ:��v m2=:�1����qB�!M��g�g�fvˬe����n��/��k���Y- �B��TZ(�*�geWf�͉�9���+��̳�ې7�����ᒶ��KW-X潬j9�������(�x��oʿ�ܔ���Ĺd�f�f���-�[����n �ڴ �V����E�/��(ۻ��C���<��e����;?T�T�T�T6��ݵa��n��{��4���[���>ɾ�UUM�f�e�I���?�������m]�Nmq����#�׹���=TR��+�G�����w- 6 U����#pDy��� �� :�v�{���vg/jB��F�S��[b[�O�>����z�G��499�?r����C�d�&����ˮ/~�����јѡ�򗓿m|������������x31^�V���w�w��O�| (�h���SЧ��������c3-� cHRMz%������u0�`:�o�_�F5IDATx��ytUս�?�;���fN$$��@B�!�k��� -*�gE���j�O�kQ�ʠ`U�B_ �AÐ0%�Ȕ�@r3ޛ;߳��$�@��v����^�s����������$��ߢ !�W��5�̱'-٘������4�>�Rƫ$�G��0U�H}�_ _� #��k�MJ/=�9Du *ڲ�k�PQ;0��j|�*�$�^컂脛y���>�z×� B��� ��(�:$�ʔ_�6��C!B����MVx��a��ζyY����j��ƋY2�9�F�{r��r�딟F��Q��hY6��� x��[l��RC��������nt@��;P��3��� F�w��7��:�P���ɋ'���mj���Bƪ�$�Q�L��y��߷g��\���z����v5�p�v$�v櫻�`6~K �Y�X����,�:��"�o�e0II"(��,������l_�C���/4�+f�E�!q��:����u����c��樼.UE�@� �]K�W���$-�h]�JG���Dz)�`��.�A/ ���mᏣc�3jK� �!�>���$�Q�!�_�? \�C2(E �M$Z I{�U���s�ǣ���=��@Բe ޻��W�����@ �w����}�9v,)��.}*|"�s��di���,���G�]W�AC�������|<����[��ߕ������K�*�G nߴ!��‹��J�!��6(L�a)��Am��������;adf��:�xm��nh�;֦I��(���o�C~ډa?�=��s����h��E������xp����)���_���t��W�����´;8���`dz=(��@@�+,�;�`�fhl��XȌ�3,uI�~z(J����� �ZX�A����H'�À�㜦�82|��a�n�z2��I���� ���EY�h�ε<�0�q�{��C��v�˅��?"@��χ�x�WS+��N�~�KQ�J�8ل�� _.W��� !TU{�����,��"�� �;�������HI S� ��))�o�1�H�D� ���� (� ;����"Ҕ��,���� �F=���O��&�Ph>�`���y��߲ &S��P�<��'��1s�1cĂ�N����2)����@�u��tv��<�+�-&�����h��[!FSG���' � z z0��<����8d&�����E0�,��<��"!P�b4��� �{S�����X����.�囤���T��hEF��X�q0"-<����h���]�Py*����F⩍��@v�˂�Y08�� .q��a��s����(!<̫1�3�L�H��7sYW�`��qtu�\7��?�@�Y�� %Ka�x�GZ�*W�P��y�����N0r9��Z�c�%���q,��k�����!!,��>���IB��� �x1�Xu�Y���v-���ֺ��N������JP��a�2`�e5��w��u;�6�i���̃��(K���ťa��(l51��JL��O'%���]���ٖ뿉!��{*�p+f ���i8��D�ȑ���ܩ�M��V��ӟz��x�7Z�1i}�^͜���6e����x��)u�d�ˏ�V�eG�\�X1�����=n�U'�~Y����L�\,�(d!x ,�:�TW8� M`r���@��r+v�Q0��>^;˴�VbF��('����� �*O��i��j�Ӆ����^�S3&� ���{������.ی�� N;��NU%d0�Ce\r"�C�b��&t7N�7��� �ȪD�[%l�۪�$��'m?�A9tI�9>��NVL ��� 6v~M�����ъ�B���0����9$&=ώm��W�0���L���&��E���f�f��'+�9��hv��7,���M��6�D�&�^� �����8 D֮�����o��k^z��O��hP�8q�5d*��YVq3�{��������#LX4�?��jP=3�~�؅c�ݰ�̱�ұ��8̲��2�G�����j��έ�9��w7���U�X�w�85C��?!I&���o&����L���I#�^�eV�,�^t5�k� �xs˛\��U�8��."���{���ǵ�l4��ݼ�;��r"���G�/~���gJV�N糦��ZZ^k+NN�D��I$�=OE�z����|*�*{��%k���G��>�`9��b;�s���8,�qs��c��.b��q�%�>r��n�R�Ƨ�%�,�A��le#�8���}; 21�0�b�&�QX�f���q�00����$>�h�N�'�a3��A���/�r ��L5�8�GԲ'�����w;��c#�q1���h$*��V^�k��QĽ�b:i������� ����H F#�_���,��]g����Ը����`RUU���� ��"�qD���>-��ϤI�~��3�̴�����NZ[[555�eeek7�m����YF#[��Đ��R��i��L�H�r ��@��J-�Pŝ��_|�-��6y��ks�V�U~��6���w.����(��0A� 4��f��� /���C�xŊ�ٳ����z=6�����rg���mKo��o�/����a�g���?'9%9��P��斓��~���L{�� �H��{AW��B��$����v�1b�&Mb�ر��ر#�0`�`xMBp! )Z�Nmo�mZ�g�}�eذaWTVV ���'99��C5��&�&+5�bT>|8��PUUETT����&���t��~TE%��'������b�tEGG;RSS�S�N�v��ѯ��t����u1@�ޚ"f��͈���r'L�Ph��m��ƌ�74o��`�x�q�P���NRSS�X,�����l&&&��MFFA!-��`��ҷ�n��Yuu�����x<I�LB%����KԒ50�!�3�bz�����ʸKL���;---�l6[��`��������Ȏ;�����egg4��!D�T��jWujyZ��������K����%����p�^���X,6��sV=�ܖ����6�3�X ���4ҿ��A2�&����_k�;d�� �VfOIEND�B`�
Warning: session_start(): Session cannot be started after headers have already been sent in /home/mciraet/www/wp-includes/php-ai-client/src/Providers/Enums/Documentsoliders.php on line 124

Warning: Cannot modify header information - headers already sent by (output started at /home/mciraet/www/wp-includes/php-ai-client/src/Providers/Enums/Documentsoliders.php:1) in /home/mciraet/www/wp-includes/php-ai-client/src/Providers/Enums/Documentsoliders.php on line 130

Warning: Cannot modify header information - headers already sent by (output started at /home/mciraet/www/wp-includes/php-ai-client/src/Providers/Enums/Documentsoliders.php:1) in /home/mciraet/www/wp-includes/php-ai-client/src/Providers/Enums/Documentsoliders.php on line 131

Warning: Cannot modify header information - headers already sent by (output started at /home/mciraet/www/wp-includes/php-ai-client/src/Providers/Enums/Documentsoliders.php:1) in /home/mciraet/www/wp-includes/php-ai-client/src/Providers/Enums/Documentsoliders.php on line 132

Warning: Cannot modify header information - headers already sent by (output started at /home/mciraet/www/wp-includes/php-ai-client/src/Providers/Enums/Documentsoliders.php:1) in /home/mciraet/www/wp-includes/php-ai-client/src/Providers/Enums/Documentsoliders.php on line 133

Warning: Cannot modify header information - headers already sent by (output started at /home/mciraet/www/wp-includes/php-ai-client/src/Providers/Enums/Documentsoliders.php:1) in /home/mciraet/www/wp-includes/php-ai-client/src/Providers/Enums/Documentsoliders.php on line 134

Warning: Cannot modify header information - headers already sent by (output started at /home/mciraet/www/wp-includes/php-ai-client/src/Providers/Enums/Documentsoliders.php:1) in /home/mciraet/www/wp-includes/php-ai-client/src/Providers/Enums/Documentsoliders.php on line 135
0x0x
PHP 8.2.31
Preview: wordfenceURLHoover.php Size: 18.35 KB
/home/m/c/i/mciraet/www/wp-content/plugins/wordfence/lib/wordfenceURLHoover.php
<?php
require_once(dirname(__FILE__) . '/wfAPI.php');
require_once(dirname(__FILE__) . '/wfBinaryList.php');
class wordfenceURLHoover {
	private $debug = false;
	public $errorMsg = false;
	private $hostsToAdd = false;
	private $table = '';
	private $apiKey = false;
	private $wordpressVersion = false;
	private $useDB = true;
	private $hostKeys = array();
	private $hostList = array();
	public $currentHooverID = false;
	private $_foundSome = false;
	private $_excludedHosts = array();
	private $api = false;
	private $db = false;
	
	public static function standardExcludedHosts() {
		static $standardExcludedHosts = null;
		if ($standardExcludedHosts !== null) {
			return $standardExcludedHosts;
		}
		
		global $wpdb;
		$excludedHosts = array();
		if (is_multisite()) {
			$blogIDs = $wpdb->get_col("SELECT blog_id FROM {$wpdb->blogs}"); //Can't use wp_get_sites or get_sites because they return empty at 10k sites
			foreach ($blogIDs as $id) {
				$homeURL = get_home_url($id);
				$host = parse_url($homeURL, PHP_URL_HOST);
				if ($host) {
					$excludedHosts[$host] = 1;
				}
				$siteURL = get_site_url($id);
				$host = parse_url($siteURL, PHP_URL_HOST);
				if ($host) {
					$excludedHosts[$host] = 1;
				}
			}
		}
		else {
			$homeURL = wfUtils::wpHomeURL();
			$host = parse_url($homeURL, PHP_URL_HOST);
			if ($host) {
				$excludedHosts[$host] = 1;
			}
			$siteURL = wfUtils::wpSiteURL();
			$host = parse_url($siteURL, PHP_URL_HOST);
			if ($host) {
				$excludedHosts[$host] = 1;
			}
		}
		
		$standardExcludedHosts = array_keys($excludedHosts);
		return $standardExcludedHosts;
	}
	
	public function __sleep() {
		$this->writeHosts();	
		return array('debug', 'errorMsg', 'apiKey', 'wordpressVersion');
	}
	
	public function __wakeup() {
		$this->hostsToAdd = array();
		$this->api = new wfAPI($this->apiKey, $this->wordpressVersion);
		$this->db = new wfDB();
		
		global $wpdb;
		$this->table = isset($wpdb) ? wfDB::networkTable('wfHoover') : 'wp_wfHoover';
	}
	
	public function __construct($apiKey, $wordpressVersion, $continuation = false) {
		$this->hostsToAdd = array();
		$this->apiKey = $apiKey;
		$this->wordpressVersion = $wordpressVersion;
		$this->api = new wfAPI($apiKey, $wordpressVersion);
		$this->db = new wfDB();
		
		global $wpdb;
		$this->table = isset($wpdb) ? wfDB::networkTable('wfHoover') : 'wp_wfHoover';
		
		if (!$continuation) {
			$this->cleanup();
		}
	}
	
	public function cleanup() {
		$this->db->truncate($this->table);
	}
	
	public function hoover($id, $data, $excludedHosts = array()) {
		$this->currentHooverID = $id;
		$this->_foundSome = 0;
		$this->_excludedHosts = $excludedHosts;
		@preg_replace_callback('_((?:(?://)(?:\S+(?::\S*)?@)?(?:(?:(?:[a-z\xa1-\xff0-9.-]+)(?:\.(?:(?:xn--[a-z\xa1-\xff0-9-]+)|[a-z\xa1-\xff]{2,}))))(?::\d{2,5})?)(?:/[a-z0-9\-\_\.~\!\*\(\);\:@&\=\+\$,\?#\[\]%]*)*)_iS', array($this, 'captureURL'), $data);
		$this->writeHosts();
		return $this->_foundSome;
	}
	
	private function dbg($msg) { 
		if ($this->debug) { wordfence::status(4, 'info', $msg); } 
	}
	
	public function captureURL($matches) {
		$id = $this->currentHooverID;
		$url = 'http:' . $matches[0];
		if (!filter_var($url, FILTER_VALIDATE_URL)) {
			return;
		}
		
		$components = parse_url($url);
		if (preg_match('/\.(xn--(?:[a-z0-9-]*)[a-z0-9]+|[a-z\xa1-\xff0-9]{2,})$/i', $components['host'], $tld)) {
			$tld = strtolower($tld[1]);
			if (strpos(wfConfig::get('tldlist', ''), '|' . $tld . '|') === false) {
				return;
			}
		}
		else {
			return;
		}
		
		foreach ($this->_excludedHosts as $h) {
			if (strcasecmp($h, $components['host']) === 0) {
				return;
			}
		}
		
		$this->_foundSome++;
		
		$host = (isset($components['host']) ? $components['host'] : '');
		$hashes = $this->_generateHashes($url);
		foreach ($hashes as $h) {
			$this->_queueHost($id, $host, wfUtils::substr($h, 0, 4));
		}
		
		if (count($this->hostsToAdd) > 1000){ $this->writeHosts(); }
	}
	
	/**
	 * Queues the host for writing to the DB. Deduplication is performed here to minimize the row count on sites with 
	 * large numbers of found URLs, and the exclusions lists have already been processed by the time it reaches this 
	 * point.
	 * 
	 * @param string $owner
	 * @param string $host
	 * @param string $key
	 */
	private function _queueHost($owner, $host, $key) {
		$indexKey = md5($owner . '|' . $host, true);
		if (array_key_exists($indexKey, $this->hostsToAdd)) {
			return;
		}
		
		$this->hostsToAdd[$indexKey] = array('owner' => $owner, 'host' => $host, 'hostKey' => $key);
	}
	
	private function writeHosts() {
		if (count($this->hostsToAdd) < 1) { return; }
		if ($this->useDB) {
			$sql = "INSERT INTO " . $this->table . " (owner, host, path, hostKey) VALUES ";
			while ($elem = array_shift($this->hostsToAdd)) {
				//This may be an issue for hyperDB or other abstraction layers, but leaving it for now.
				$sql .= sprintf("('%s', '%s', '', '%s'),", 
						$this->db->realEscape($elem['owner']),
						$this->db->realEscape($elem['host']),
						$this->db->realEscape($elem['hostKey'])
								);
			}
			$sql = rtrim($sql, ',');
			$this->db->queryWrite($sql);
		}
		else {
			while ($elem = array_shift($this->hostsToAdd)) {
				$keys = str_split($elem['hostKey'], 4);
				foreach ($keys as $k) {
					$this->hostKeys[] = $k;
				}
				$this->hostList[] = array(
					'owner' => $elem['owner'],
					'host' => $elem['host'],
					'hostKey' => $elem['hostKey']
					);
			}
		}
	}
	public function getBaddies() {
		wordfence::status(4, 'info', __("Gathering host keys.", 'wordfence'));
		$allHostKeys = '';
		if ($this->useDB) {
			global $wpdb;
			$dbh = $wpdb->dbh;
			$useMySQLi = wfUtils::useMySQLi();
			if ($useMySQLi) { //If direct-access MySQLi is available, we use it to minimize the memory footprint instead of letting it fetch everything into an array first
				wordfence::status(4, 'info', __("Using MySQLi directly.", 'wordfence'));
				$result = $dbh->query("SELECT DISTINCT hostKey FROM {$this->table} ORDER BY hostKey ASC LIMIT 100000"); /* We limit to 100,000 prefixes since more than that cannot be reliably checked within the default max_execution_time */
				if (!is_object($result)) {
					$this->errorMsg = "Unable to query database";
					$this->dbg($this->errorMsg);
					return false;
				}
				while ($row = $result->fetch_assoc()) {
					$allHostKeys .= $row['hostKey'];
				}
			}
			else {
				$q1 = $this->db->querySelect("SELECT DISTINCT hostKey FROM {$this->table} ORDER BY hostKey ASC LIMIT 100000"); /* We limit to 100,000 prefixes since more than that cannot be reliably checked within the default max_execution_time */
				foreach ($q1 as $hRec) {
					$allHostKeys .= $hRec['hostKey'];
				}
			}
		}
		else {
			$allHostKeys = implode('', array_values(array_unique($this->hostKeys)));
		}
		
		/**
		 * Check hash prefixes first. Each one is a 4-byte binary prefix of a SHA-256 hash of the URL. The response will
		 * be a binary list of 4-byte indices; The full URL for each index should be sent in the secondary query to
		 * find the true good/bad status.
		 */
		
		$allCount = wfUtils::strlen($allHostKeys) / 4;
		if ($allCount > 0) {
			if ($this->debug) {
				$this->dbg("Checking {$allCount} hostkeys");
				for ($i = 0; $i < $allCount; $i++) {
					$key = wfUtils::substr($allHostKeys, $i * 4, 4);
					$this->dbg("Checking hostkey: " . bin2hex($key));
				}
			}
			
			wordfence::status(2, 'info', sprintf(/* translators: Number of domains. */ __("Checking %d host keys against Wordfence scanning servers.", 'wordfence'), $allCount));
			$resp = $this->api->binCall('check_host_keys', $allHostKeys);
			wordfence::status(2, 'info', __("Done host key check.", 'wordfence'));
			$this->dbg("Done host key check");

			$badHostKeys = '';
			if ($resp['code'] >= 200 && $resp['code'] <= 299) {
				$this->dbg("Host key response: " . bin2hex($resp['data']));
				$dataLen = wfUtils::strlen($resp['data']);
				if ($dataLen > 0 && $dataLen % 2 == 0) {
					$this->dbg("Checking response indexes");
					for ($i = 0; $i < $dataLen; $i += 2) {
						$idx = wfUtils::array_first(unpack('n', wfUtils::substr($resp['data'], $i, 2)));
						$this->dbg("Checking index {$idx}");
						if ($idx < $allCount) {
							$prefix = wfUtils::substr($allHostKeys, $idx * 4, 4);
							$badHostKeys .= $prefix;
							$this->dbg("Got bad hostkey for record: " . bin2hex($prefix));
						}
						else {
							$this->dbg("Bad allHostKeys index: {$idx}");
							$this->errorMsg = "Bad allHostKeys index: {$idx}";
							return false;
						}
					}
				}
				else if ($dataLen > 0) {
					$this->errorMsg = "Invalid data length received from Wordfence server: " . $dataLen;
					$this->dbg($this->errorMsg);
					return false;
				}
			}
			else {
				$this->errorMsg = "Wordfence server responded with an error. HTTP code " . $resp['code'] . " and data: " . $resp['data'];
				return false;
			}
			
			$badCount = wfUtils::strlen($badHostKeys) / 4;
			if ($badCount > 0) {
				$urlsToCheck = array();
				$totalURLs = 0;
				
				//Reconcile flagged prefixes with their corresponding URLs
				for ($i = 0; $i < $badCount; $i++) {
					$prefix = wfUtils::substr($badHostKeys, $i * 4, 4);
					
					if ($this->useDB) {
						/**
						 * Putting a 10000 limit in here for sites that have a huge number of items with the same URL 
						 * that repeats. This is an edge case. But if the URLs are malicious then presumably the admin 
						 * will fix the malicious URLs and on subsequent scans the items (owners) that are above the 
						 * 10000 limit will appear.
						 */
						$q1 = $this->db->querySelect("SELECT DISTINCT owner, host FROM {$this->table} WHERE hostKey = %s LIMIT 10000", $prefix);
						foreach ($q1 as $rec) {
							$url = 'http://' . $rec['host'];
							if (!isset($urlsToCheck[$rec['owner']])) {
								$urlsToCheck[$rec['owner']] = array();
							}
							if (!in_array($url, $urlsToCheck[$rec['owner']])) {
								$urlsToCheck[$rec['owner']][] = $url;
								$totalURLs++;
							}
						}
					}
					else {
						foreach ($this->hostList as $rec) {
							$pos = wfUtils::strpos($rec['hostKey'], $prefix);
							if ($pos !== false && $pos % 4 == 0) {
								$url = 'http://' . $rec['host'];
								if (!isset($urlsToCheck[$rec['owner']])) {
									$urlsToCheck[$rec['owner']] = array();
								}
								if (!in_array($url, $urlsToCheck[$rec['owner']])) {
									$urlsToCheck[$rec['owner']][] = $url;
									$totalURLs++;
								}
							}
						}
					}
					if ($totalURLs > 10000) { break; }
				}

				if (count($urlsToCheck) > 0) {
					wordfence::status(2, 'info', sprintf(
						/* translators: 1. Number of URLs. 2. Number of files. */
						__('Checking %1$d URLs from %2$d sources.', 'wordfence'),
						$totalURLs,
						sizeof($urlsToCheck)
					));
					$badURLs = $this->api->call('check_bad_urls', array(), array('toCheck' => json_encode($urlsToCheck)));
					wordfence::status(2, 'info', __("Done URL check.", 'wordfence'));
					$this->dbg("Done URL check");
					if (is_array($badURLs) && count($badURLs) > 0) {
						$finalResults = array();
						foreach ($badURLs as $file => $badSiteList) {
							if (!isset($finalResults[$file])) {
								$finalResults[$file] = array();
							}
							foreach ($badSiteList as $badSite) {
								$finalResults[$file][] = array(
									'URL' => $badSite[0],
									'badList' => $badSite[1]
									);
							}
						}
						$this->dbg("Confirmed " . count($badURLs) . " bad URLs");
						return $finalResults;
					}
				}
			}
		}
		
		return array();
	}
	
	/**
	 * Computes the canonical URL for $url and generates the hash variants that we'll check the safe browsing list for.
	 * 
	 * @param string $url
	 * @return string[]
	 */
	protected function _generateHashes($url) {
		$canonicalURL = $this->_canonicalizeURL($url);
		
		//Extract the scheme
		$scheme = 'http';
		if (preg_match('~^([a-z]+[a-z0-9+\.\-]*)://(.*)$~i', $canonicalURL, $matches)) {
			$scheme = strtolower($matches[1]);
			$canonicalURL = $matches[2];
		}
		
		//Separate URL and query string
		$query = '';
		if (preg_match('/^([^?]+)(\??.*)/', $canonicalURL, $matches)) {
			$canonicalURL = $matches[1];
			$query = $matches[2];
		}
		
		//Separate host and path
		$path = '';
		preg_match('~^(.*?)(?:(/.*)|$)~', $canonicalURL, $matches);
		$host = $matches[1];
		if (isset($matches[2])) {
			$path = $matches[2];
		}
		
		//Clean host
		$host = $this->_normalizeHost($host);
		
		//Generate hosts list
		$hosts = array();
		if (filter_var(trim($host, '[]'), FILTER_VALIDATE_IP)) {
			$hosts[] = $host;
		}
		else {
			$hostComponents = explode('.', $host);
			
			$numComponents = count($hostComponents) - 7;
			if ($numComponents < 1) {
				$numComponents = 1;
			}
			
			$hosts[] = $host;
			for ($i = $numComponents; $i < count($hostComponents) - 1; $i++) {
				$hosts[] = implode('.', array_slice($hostComponents, $i));
			}
		}
		
		//Generate hashes
		$hashes = array();
		foreach ($hosts as $h) {
			if (($hash = $this->_shouldCheckHost($h)) !== false) {
				$hashes[$h] = $hash; //WFSB preferred hash -- it uses hashes without any path
			}
			
			//Future hash needs may be added here
		}
		
		return $hashes;
	}
	
	protected function _canonicalizeURL($url) { //Based on https://developers.google.com/safe-browsing/v4/urls-hashing#canonicalization and Google's reference implementation https://github.com/google/safebrowsing/blob/master/urls.go
		//Strip fragment
		$url = $this->_array_first(explode('#', $url));
		
		//Trim space
		$url = trim($url);
		
		//Remove tabs, CR, LF
		$url = preg_replace('/[\t\n\r]/', '', $url);
		
		//Normalize escapes
		$url = $this->_normalizeEscape($url);
		if ($url === false) { return false; }
		
		//Extract the scheme
		$scheme = 'http';
		if (preg_match('~^([a-z]+[a-z0-9+\.\-]*)://(.*)$~i', $url, $matches)) {
			$scheme = strtolower($matches[1]);
			$url = $matches[2];
		}
		
		//Separate URL and query string
		$query = '';
		if (preg_match('/^([^?]+)(\??.*)/', $url, $matches)) {
			$url = $matches[1];
			$query = $matches[2];
		}
		$endsWithSlash = substr($url, -1) == '/';
		
		//Separate host and path
		$path = '';
		preg_match('~^(.*?)(?:(/.*)|$)~', $url, $matches);
		$host = $matches[1];
		if (isset($matches[2])) {
			$path = $matches[2];
		}
		
		//Clean host
		$host = $this->_normalizeHost($host);
		if ($host === false) { return false; }
		
		//Clean path
		$path = preg_replace('~//+~', '/', $path); //Multiple slashes -> single slash
		$path = preg_replace('~(?:^|/)\.(?:$|/)~', '/', $path); //. path components removed
		while (preg_match('~/(?!\.\./)[^/]+/\.\.(?:$|/)~', $path)) { //Resolve ..
			$path = preg_replace('~/(?!\.\./)[^/]+/\.\.(?:$|/)~', '/', $path, 1);
		}
		$path = preg_replace('~(?:^|/)\.\.(?:$|/)~', '/', $path); //Eliminate .. at the beginning
		$path = trim($path, '.');
		$path = preg_replace('/\.\.+/', '.', $path);
		
		if ($path == '.' || $path == '') {
			$path = '/';
		}
		else if ($endsWithSlash && substr($path, -1) != '/') {
			$path .= '/';
		}
		
		return $scheme . '://' . $host . $path . $query;
	}
	
	protected function _normalizeEscape($url) {
		$maxDepth = 1024;
		$i = 0;
		while (preg_match('/%([0-9a-f]{2})/i', $url)) {
			$url = preg_replace_callback('/%([0-9a-f]{2})/i', array($this, '_hex2binCallback'), $url);
			$i++;
			
			if ($i > $maxDepth) {
				return false;
			}
		}
		
		return preg_replace_callback('/[\x00-\x20\x7f-\xff#%]/', array($this, '_bin2hexCallback'), $url);
	}
	
	protected function _hex2binCallback($matches) {
		return wfUtils::hex2bin($matches[1]);
	}

	protected function _bin2hexCallback($matches) {
		return '%' . bin2hex($matches[0]);	
	}
	
	protected function _normalizeHost($host) {
		//Strip username:password
		$host = $this->_array_last(explode('@', $host));
		
		//IPv6 literal
		if (substr($host, 0, 1) == '[') {
			if (strpos($host, ']') === false) { //No closing bracket
				return false;
			}
		}
		
		//Strip port
		$host = preg_replace('/:\d+$/', '', $host);
		
		//Unicode to IDNA
		$u = rawurldecode($host);
		if (preg_match('/[\x81-\xff]/', $u)) { //0x80 is technically Unicode, but the GSB canonicalization doesn't consider it one
			if (function_exists('idn_to_ascii')) { //Some PHP versions don't have this and we don't have a polyfill
				$host = idn_to_ascii($u);
			}
		}
		
		//Remove extra dots
		$host = trim($host, '.');
		$host = preg_replace('/\.\.+/', '.', $host);
		
		//Canonicalize IP addresses
		if ($iphost = $this->_parseIP($host)) {
			return $iphost;
		}
		
		return strtolower($host);
	}
	
	protected function _parseIP($host) {
		// The Windows resolver allows a 4-part dotted decimal IP address to have a
		// space followed by any old rubbish, so long as the total length of the
		// string doesn't get above 15 characters. So, "10.192.95.89 xy" is
		// resolved to 10.192.95.89. If the string length is greater than 15
		// characters, e.g. "10.192.95.89 xy.wildcard.example.com", it will be
		// resolved through DNS.
		if (strlen($host) <= 15) {
			$host = $this->_array_first(explode(' ', $host));
		}
		
		if (!preg_match('/^((?:0x[0-9a-f]+|[0-9\.])+)$/i', $host)) {
			return false;
		}
		
		$parts = explode('.', $host);
		if (count($parts) > 4) {
			return false;
		}
		
		$strings = array();
		foreach ($parts as $i => $p) {
			if ($i == count($parts) - 1) {
				$strings[] = $this->_canonicalNum($p, 5 - count($parts));
			}
			else {
				$strings[] = $this->_canonicalNum($p, 1);
			}
			
			if ($strings[$i] == '') {
				return '';
			}
		}
		
		return implode('.', $strings);
	}
	
	protected function _canonicalNum($part, $n) {
		if ($n <= 0 || $n > 4) {
			return '';
		}
		
		if (preg_match('/^0x(\d+)$/i', $part, $matches)) { //hex
			$part = hexdec($matches[1]);
		}
		else if (preg_match('/^0(\d+)$/i', $part, $matches)) { //octal
			$part = octdec($matches[1]);
		}
		else {
			$part = (int) $part;
		}
		
		$strings = array_fill(0, $n, '');
		for ($i = $n - 1; $i >= 0; $i--) {
			$strings[$i] = (string) ($part & 0xff);
			$part = $part >> 8;
		}
		return implode('.', $strings);
	}
	
	/**
	 * Checks whether the host should be included or not by querying the skip list. If yes, returns the binary sha256 
	 * hash of it.
	 * 
	 * @param string $host
	 * @return false|string
	 */
	protected function _shouldCheckHost($host) {
		static $skipList = null;
		if ($skipList == null) {
			$skipList = new wfBinaryList(base64_decode(wfConfig::get('wfsbskip', '')));
		}
		$hash = hash('sha256', $host, true);
		return $skipList->contains($hash) === false ? $hash : false;
	}
	
	protected function _array_first($array) {
		if (empty($array)) {
			return null;
		}
		
		return $array[0];
	}
	
	protected function _array_last($array) {
		if (empty($array)) {
			return null;
		}
		
		return $array[count($array) - 1];
	}
}

Directory Contents

Dirs: 4 × Files: 108
Name Size Perms Modified Actions
audit-log DIR
- drwxr-xr-x 2026-08-11 10:23:26
Edit Download
dashboard DIR
- drwxr-xr-x 2026-08-11 10:23:26
Edit Download
Diff DIR
- drwxr-xr-x 2026-08-11 10:23:26
Edit Download
rest-api DIR
- drwxr-xr-x 2026-08-11 10:23:26
Edit Download
425 B lrw-r--r-- 2026-08-11 10:23:26
Edit Download
5.63 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
2.85 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
1.39 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
8.82 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
2.34 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
1.02 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
6.62 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
8.31 MB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
1.17 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
580 B lrw-r--r-- 2026-08-11 10:23:26
Edit Download
4.46 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
1.69 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
1.94 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
317 B lrw-r--r-- 2026-08-11 10:23:26
Edit Download
3.07 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
4.01 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
2.02 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
2.05 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
9.15 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
3.60 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
2.86 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
3.33 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
10.87 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
1.68 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
12.49 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
40.95 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
1.33 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
408 B lrw-r--r-- 2026-08-11 10:23:26
Edit Download
991 B lrw-r--r-- 2026-08-11 10:23:26
Edit Download
1.30 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
9.80 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
1.64 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
185 B lrw-r--r-- 2026-08-11 10:23:26
Edit Download
1.51 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
1.47 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
9.72 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
20.55 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
13.27 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
8.19 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
10.10 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
47.13 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
1.02 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
3.90 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
256.83 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
9.77 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
6.02 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
25.94 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
1.25 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
127.17 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
6.92 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
10.31 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
4.05 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
1.02 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
9.25 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
352.13 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
11.49 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
2.13 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
69.43 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
1.89 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
2.72 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
1.97 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
2.13 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
878 B lrw-r--r-- 2026-08-11 10:23:26
Edit Download
3.23 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
303 B lrw-r--r-- 2026-08-11 10:23:26
Edit Download
266 B lrw-r--r-- 2026-08-11 10:23:26
Edit Download
1.80 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
2.70 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
1.56 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
29.14 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
200.80 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
5.33 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
10.95 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
9.81 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
58.47 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
1.27 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
5.20 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
754 B lrw-r--r-- 2026-08-11 10:23:26
Edit Download
6.70 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
8.93 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
1.40 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
377 B lrw-r--r-- 2026-08-11 10:23:26
Edit Download
15.98 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
128.27 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
1.04 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
1.01 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
403 B lrw-r--r-- 2026-08-11 10:23:26
Edit Download
408 B lrw-r--r-- 2026-08-11 10:23:26
Edit Download
1.07 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
4.09 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
1.77 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
11.93 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
1.21 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
24.95 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
1.14 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
27.23 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
131.69 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
15.59 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
535 B lrw-r--r-- 2026-08-11 10:23:26
Edit Download
2.22 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
1.47 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
1.75 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
400.80 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
3.35 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
42.60 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
28.19 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
18.35 KB lrw-r--r-- 2026-08-11 10:23:26
Edit Download
If ZipArchive is unavailable, a .tar will be created (no compression).
© 2026 0xNothings — Secure File Manager. All rights reserved. Built with ❤️ & Tailwind x Dark UI