�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`� 0x0x
PHP 8.1.34
Preview: BotLifecycle.php Size: 17.81 KB
/home2/astrociv/www/avadhutholidays.in/wp-content/plugins/imunify-security/inc/App/Bot/BotLifecycle.php
<?php
/**
 * Copyright (с) Cloud Linux GmbH & Cloud Linux Software, Inc 2010-2025 All Rights Reserved
 *
 * Licensed under CLOUD LINUX LICENSE AGREEMENT
 * https://www.cloudlinux.com/legal/
 */

namespace CloudLinux\Imunify\App\Bot;

/**
 * Activation / deactivation / uninstall for the bot-protection feature.
 *
 * WordPress-facing glue: builds the real collaborators and delegates the
 * install-then-loopback decision to BotShimManager. Also owns the bot
 * WP-Cron schedules and the teardown of every artefact this feature
 * persists — the mu-plugin shim, the rate-limit / block tables, and the
 * bot-owned options.
 *
 * @since 4.0.0
 */
class BotLifecycle {

	/**
	 * WP-Cron hook name for periodic storage cleanup.
	 */
	const CRON_HOOK_STORAGE_CLEANUP = 'imunify_security_bot_storage_cleanup';

	/**
	 * WP-Cron hook name for the daily bot-traffic export.
	 */
	const CRON_HOOK_DAILY_EXPORT = 'imunify_security_bot_daily_export';

	/**
	 * Custom WP-Cron recurrence: every 6 hours (4x/day).
	 */
	const CLEANUP_INTERVAL_SECONDS = 21600;

	/**
	 * Retention window for the hourly bot-traffic rollup, in seconds. Derived
	 * from the canonical hour count so it cannot drift from the read window.
	 * The daily export tightens the local tables back to this window right
	 * after it writes; between exports the 6-hourly cleanup keeps a wider floor
	 * (see {@see retentionFloorBucket()}).
	 */
	const STATS_RETENTION_SECONDS = HourlyStatsStorage::RETENTION_HOURS * 3600;

	/**
	 * Width of the post-midnight window the daily export's first run is jittered
	 * across (2 hours). Staggers export writes / prunes across sites so they do
	 * not all fire at 00:00 UTC, while keeping every run within a couple of
	 * hours of midnight so a complete previous day is always available.
	 */
	const EXPORT_JITTER_SECONDS = 7200;

	/**
	 * Activation entry point. Installs the mu-plugin shim only when the
	 * server-level ai_bot_protection gate is on.
	 *
	 * @return bool Whether the shim is installed and passed the safety test,
	 *              or true when the feature gate is off and nothing was installed.
	 */
	public static function activate() {
		$mu_dir = self::muPluginDir();
		if ( '' === $mu_dir ) {
			return false;
		}
		self::ensureStorageCleanupScheduled();
		SignatureRefresher::scheduleHooks();
		$home_url       = function_exists( 'home_url' ) ? (string) home_url( '/' ) : '';
		$wp_content_dir = defined( 'WP_CONTENT_DIR' ) ? (string) WP_CONTENT_DIR : '';
		if ( OptOutFlag::load( $wp_content_dir )->isStatsEnabled() ) {
			self::ensureDailyExportScheduled();
		}
		$cfg = MuLoader::loadPluginConfig( $wp_content_dir );
		return self::shimManager( $mu_dir )->install( $home_url, $cfg );
	}

	/**
	 * Deactivation entry point. Clears cron and removes the shim — temporary
	 * artefacts only, never persisted data.
	 *
	 * @return bool
	 */
	public static function deactivate() {
		self::unscheduleStorageCleanup();
		$mu_dir = self::muPluginDir();
		if ( '' === $mu_dir ) {
			return true;
		}
		return self::shimManager( $mu_dir )->remove();
	}

	/**
	 * Remove every bot-protection artefact that survives deactivation: the
	 * rate-limit / block tables, the bot-owned options (the site owner's
	 * allowlist included, as the agent also deletes the data directory with
	 * bot-settings.php when it removes the plugin), and the shim.
	 *
	 * @return void
	 */
	public static function uninstall() {
		self::unscheduleStorageCleanup();
		self::dropBotTables();
		delete_option( SignatureRefresher::MIRROR_MD5_OPTION );
		delete_option( SignatureRefresher::MIRROR_GENERATED_AT_OPTION );
		delete_option( SignatureRefresher::MIRROR_NEXT_ATTEMPT_OPTION );
		delete_option( MuPluginSelfHealer::OPTION_NAME );
		delete_option( LoopbackStatus::OPTION_NAME );
		SiteAllowlistStore::deleteOption();
		$mu_dir = self::muPluginDir();
		if ( '' !== $mu_dir ) {
			self::shimManager( $mu_dir )->remove();
		}
	}

	/**
	 * Register the cron_schedules filter and the cleanup action hooks.
	 *
	 * Called from the mu-plugin bootstrap (MuLoader) on every request so the
	 * custom interval and action handlers are always available to WP-Cron,
	 * even on requests where the main plugin file is not loaded yet.
	 *
	 * @param string|null $wp_content_dir Absolute path to wp-content, or null to use WP_CONTENT_DIR.
	 * @return void
	 */
	public static function registerCleanupHooks( $wp_content_dir = null ) {
		if ( ! function_exists( 'add_filter' ) ) {
			return;
		}
		add_filter( 'cron_schedules', array( __CLASS__, 'addCleanupSchedule' ) ); // phpcs:ignore WordPress.WP.CronInterval.ChangeDetected
		// activate() runs only on a fresh activation, not on a plugin update, so
		// schedule from the per-request path too. wp_next_scheduled-guarded.
		self::ensureStorageCleanupScheduled();
		SignatureRefresher::scheduleHooks();
		add_action( self::CRON_HOOK_STORAGE_CLEANUP, array( __CLASS__, 'runStorageCleanup' ) );
		add_action( self::CRON_HOOK_DAILY_EXPORT, array( __CLASS__, 'runDailyExport' ) );
		add_action( SignatureRefresher::CRON_HOOK_REFRESH, array( __CLASS__, 'runSignatureRefresh' ) );

		// The daily export is scheduled from activate() and the stats toggle only;
		// self-heal it here so an upgraded site (which never calls activate) still
		// exports. Gate on the stats flag — the master switch activate() and the
		// toggle key off — so a stats-disabled site never gets a schedule.
		if ( null === $wp_content_dir ) {
			$wp_content_dir = defined( 'WP_CONTENT_DIR' ) ? (string) WP_CONTENT_DIR : '';
		}
		if ( '' !== $wp_content_dir && OptOutFlag::load( $wp_content_dir )->isStatsEnabled() ) {
			self::ensureDailyExportScheduled();
		}
	}

	/**
	 * Add the 'imunify_six_hours' recurrence to WP-Cron schedules.
	 *
	 * @param array $schedules Existing schedules.
	 * @return array
	 */
	public static function addCleanupSchedule( $schedules ) {
		if ( ! isset( $schedules['imunify_six_hours'] ) ) {
			$schedules['imunify_six_hours'] = array(
				'interval' => self::CLEANUP_INTERVAL_SECONDS,
				'display'  => 'Every 6 hours',
			);
		}
		return $schedules;
	}

	/**
	 * WP-Cron callback: run storage cleanup in a fail-safe wrapper.
	 *
	 * Accepts an optional $wp_content_dir to allow unit testing without
	 * relying on the WP_CONTENT_DIR constant.
	 *
	 * @param string|null $wp_content_dir Absolute path to wp-content, or null to use WP_CONTENT_DIR.
	 * @return void
	 */
	public static function runStorageCleanup( $wp_content_dir = null ) {
		global $wpdb;
		if ( ! isset( $wpdb ) ) {
			return;
		}

		// Enforce retention on the durable rollup + per-IP tables before the
		// protection-gate short-circuits below. These are independent InnoDB
		// tables — not the MEMORY-engine pair the gates guard from being
		// recreated — so their retention must hold even while protection is off.
		// The per-IP table is additionally trimmed back to its per-group cap.
		// The floor keeps the whole previous calendar day so this 6-hourly sweep
		// leaves that day for the daily export to write (best-effort: a day whose
		// export fails or never fires is pruned the next day, not recovered). The
		// daily export tightens the tables back to 24h + current hour right after
		// it writes. Fail-open: neither blocks nor is blocked by the MEMORY cleanup.
		$oldest_kept = self::retentionFloorBucket( time() );
		$hourly      = HourlyStatsStorage::forGlobalWpdb();
		if ( null !== $hourly ) {
			$hourly->prune( $oldest_kept );
		}
		$hourly_ip = HourlyIpStatsStorage::forGlobalWpdb();
		if ( null !== $hourly_ip ) {
			$hourly_ip->prune( $oldest_kept );
			$hourly_ip->trim();
		}

		if ( null === $wp_content_dir ) {
			if ( ! defined( 'WP_CONTENT_DIR' ) ) {
				return;
			}
			$wp_content_dir = (string) WP_CONTENT_DIR;
		}
		// Skip when protection is off: cleanup() rebuilds the MEMORY counter table
		// via DROP+CREATE, so an ungated run recreates an empty table the instant
		// the gate closes. Skip rather than self-cancel like the refresh cron —
		// this event is scheduled only from activate(), so unscheduling it here
		// would strand it until the next reactivation.
		if ( ! MuLoader::loadPluginConfig( $wp_content_dir )->isAiBotProtectionEnabled() ) {
			return;
		}
		if ( ! OptOutFlag::load( $wp_content_dir )->isEnabled() ) {
			return;
		}
		try {
			$storage = DbStorageFactory::detect( $wpdb );
			$storage['block']->cleanup();
			$storage['counter']->cleanup();
		} catch ( \Exception $e ) {
			$transient_key = 'imunify_security_error_bot_cleanup_failed';
			if ( function_exists( 'get_transient' ) && ! get_transient( $transient_key ) ) {
				if ( function_exists( 'set_transient' ) ) {
					set_transient( $transient_key, true, 3600 );
				}
				do_action(
					'imunify_security_set_error',
					E_WARNING,
					'Bot storage cleanup failed: ' . $e->getMessage(),
					__FILE__,
					__LINE__,
					array(
						'fingerprint' => array( 'bot_storage_cleanup_failed', get_class( $e ) ),
					)
				);
			}
		}
	}

	/**
	 * WP-Cron callback: pull the latest bot-data overlay from the mirror.
	 *
	 * Accepts an optional $wp_content_dir to allow unit testing without
	 * relying on the WP_CONTENT_DIR constant.
	 *
	 * @param string|null $wp_content_dir Absolute path to wp-content, or null to use WP_CONTENT_DIR.
	 * @return void
	 */
	public static function runSignatureRefresh( $wp_content_dir = null ) {
		if ( null === $wp_content_dir ) {
			if ( ! defined( 'WP_CONTENT_DIR' ) ) {
				return;
			}
			$wp_content_dir = (string) WP_CONTENT_DIR;
		}
		if ( ! MuLoader::loadPluginConfig( $wp_content_dir )->isAiBotProtectionEnabled() ) {
			self::unscheduleRefresh();
			return;
		}
		if ( ! OptOutFlag::load( $wp_content_dir )->isEnabled() ) {
			self::unscheduleRefresh();
			return;
		}
		$overlay_dir = rtrim( $wp_content_dir, '/' ) . '/imunify-security/bot-data';
		$refresher   = new SignatureRefresher(
			$overlay_dir,
			new WpHttpClient( MirrorDownloader::REQUEST_TIMEOUT_SECONDS )
		);
		$refresher->refreshFromMirror();
	}

	/**
	 * WP-Cron callback: export the previous full day's bot traffic.
	 *
	 * Self-cancels when the site owner opted out of stats capture — the flag that
	 * governs the schedule — so a schedule that outlives an opt-out made outside
	 * the widget stops firing. With protection or the site opt-out off it skips
	 * the run but leaves the schedule in place (the stats-gated self-heal would
	 * re-add it anyway), knowingly forgoing a previous day that has not been
	 * exported yet. Accepts an optional $wp_content_dir for unit testing without
	 * the WP_CONTENT_DIR constant.
	 *
	 * @param string|null $wp_content_dir Absolute path to wp-content, or null to use WP_CONTENT_DIR.
	 * @return void
	 */
	public static function runDailyExport( $wp_content_dir = null ) {
		if ( null === $wp_content_dir ) {
			if ( ! defined( 'WP_CONTENT_DIR' ) ) {
				return;
			}
			$wp_content_dir = (string) WP_CONTENT_DIR;
		}
		$opt_out = OptOutFlag::load( $wp_content_dir );
		// Stats capture is the sole governor of the export schedule — activate(),
		// the widget toggle, and the per-request self-heal all key off it — so
		// self-cancel only when it is off. Cancelling on protection/opt-out too
		// would fight the stats-gated self-heal, which re-adds the event every
		// request (schedule/cancel thrash). With protection or the site opt-out
		// off the run is skipped and the schedule left in place. That knowingly
		// forgoes a previous day still sitting unexported in the tables: capture
		// has stopped, so exporting anyway would write a meta-only file every day
		// from then on, and the retention floor drops that day like any other
		// unexported one.
		if ( ! $opt_out->isStatsEnabled() ) {
			self::unscheduleDailyExport();
			return;
		}
		if ( ! MuLoader::loadPluginConfig( $wp_content_dir )->isAiBotProtectionEnabled()
			|| ! $opt_out->isEnabled() ) {
			return;
		}
		$exporter = BotStatsExporter::forGlobalWpdb( $wp_content_dir );
		if ( null !== $exporter ) {
			$exporter->export();
		}
	}

	/**
	 * Hour-bucket retention floor for the 6-hourly durable-table sweep: the
	 * start of the previous calendar day (UTC). Keeping the whole previous day
	 * lets the daily export find a complete day to write. This holds only for the
	 * best-effort case where each day's run fires and succeeds: the floor advances
	 * unconditionally every day while the export only ever targets the immediately
	 * previous day, so a day whose run fails, never fires, or is skipped because
	 * protection went off is pruned here the next day and not recovered. Anything
	 * older than the floor is fair game to prune.
	 *
	 * @param int $now Unix timestamp.
	 * @return int
	 */
	public static function retentionFloorBucket( $now ) {
		$now            = (int) $now;
		$today_midnight = $now - ( $now % DAY_IN_SECONDS );
		return HourlyStatsStorage::hourBucket( $today_midnight - DAY_IN_SECONDS );
	}

	/**
	 * Schedule the daily export cron if not already scheduled. The first run is
	 * anchored to the next UTC midnight plus a per-site jitter offset in
	 * [0, EXPORT_JITTER_SECONDS); the built-in `daily` recurrence then preserves
	 * that per-site offset every day.
	 *
	 * @return void
	 */
	public static function ensureDailyExportScheduled() {
		if ( ! function_exists( 'wp_next_scheduled' ) ) {
			return;
		}
		if ( wp_next_scheduled( self::CRON_HOOK_DAILY_EXPORT ) ) {
			return;
		}
		$now           = time();
		$next_midnight = ( $now - ( $now % DAY_IN_SECONDS ) ) + DAY_IN_SECONDS;
		wp_schedule_event( $next_midnight + self::exportJitter(), 'daily', self::CRON_HOOK_DAILY_EXPORT );
	}

	/**
	 * Per-site post-midnight export offset in [0, EXPORT_JITTER_SECONDS).
	 *
	 * Derived from the site scope rather than an RNG: this runs from the
	 * mu-plugin self-heal, where wp_rand() does not exist yet (pluggable.php is
	 * loaded after plugins), so a random offset would collapse to a fleet-wide
	 * 00:00 UTC stampede on exactly the upgraded sites that only ever schedule
	 * via that path. Deterministic also means a site keeps its slot when it
	 * reschedules.
	 *
	 * @return int
	 */
	public static function exportJitter() {
		return self::jitterForScope( SiteScope::derive() );
	}

	/**
	 * Map a site scope to its export offset. Split out from {@see exportJitter()}
	 * so the spread across sites is testable without touching ABSPATH.
	 *
	 * @param string $scope Site scope identifier from SiteScope::derive().
	 * @return int
	 */
	public static function jitterForScope( $scope ) {
		// 8 hex chars keeps the value inside PHP_INT_MAX on 32-bit builds.
		// nosemgrep: php.lang.security.weak-crypto.weak-crypto -- spreads cron start times, not cryptography.
		return (int) ( hexdec( substr( md5( (string) $scope ), 0, 8 ) ) % self::EXPORT_JITTER_SECONDS );
	}

	/**
	 * Remove the daily export cron event. Called on the stats opt-out toggle,
	 * on teardown, and by the run-time self-cancel guard.
	 *
	 * @return void
	 */
	public static function unscheduleDailyExport() {
		if ( function_exists( 'wp_clear_scheduled_hook' ) ) {
			wp_clear_scheduled_hook( self::CRON_HOOK_DAILY_EXPORT );
		}
	}

	/**
	 * Schedule the cleanup cron event if not already scheduled.
	 *
	 * @return void
	 */
	private static function ensureStorageCleanupScheduled() {
		if ( ! function_exists( 'wp_next_scheduled' ) ) {
			return;
		}
		// Ensure the custom recurrence is registered before scheduling.
		// activate() runs from the main plugin hooks (not MuLoader),
		// so registerCleanupHooks() hasn't fired yet for this request.
		if ( function_exists( 'add_filter' ) ) {
			add_filter( 'cron_schedules', array( __CLASS__, 'addCleanupSchedule' ) ); // phpcs:ignore WordPress.WP.CronInterval.ChangeDetected
		}
		if ( ! wp_next_scheduled( self::CRON_HOOK_STORAGE_CLEANUP ) ) {
			wp_schedule_event( time(), 'imunify_six_hours', self::CRON_HOOK_STORAGE_CLEANUP );
		}
	}

	/**
	 * Remove the cleanup cron events.
	 *
	 * @return void
	 */
	private static function unscheduleStorageCleanup() {
		if ( function_exists( 'wp_clear_scheduled_hook' ) ) {
			wp_clear_scheduled_hook( self::CRON_HOOK_STORAGE_CLEANUP );
			wp_clear_scheduled_hook( self::CRON_HOOK_DAILY_EXPORT );
			wp_clear_scheduled_hook( SignatureRefresher::CRON_HOOK_REFRESH );
		}
	}

	/**
	 * Remove only the signature-refresh cron event.
	 *
	 * Called from runSignatureRefresh() when bot protection is disabled so
	 * the cron self-cancels rather than firing harmlessly every 6 hours.
	 *
	 * @return void
	 */
	private static function unscheduleRefresh() {
		if ( function_exists( 'wp_clear_scheduled_hook' ) ) {
			wp_clear_scheduled_hook( SignatureRefresher::CRON_HOOK_REFRESH );
		}
	}

	/**
	 * Drop all bot-protection database tables.
	 *
	 * Suppresses errors so a missing table or DB permission issue never
	 * prevents the rest of the uninstall from completing.
	 *
	 * @return void
	 */
	private static function dropBotTables() {
		global $wpdb;
		if ( ! isset( $wpdb ) ) {
			return;
		}
		$suppress = $wpdb->suppress_errors( true );
		$prefix   = $wpdb->prefix;
		$tables   = array(
			$prefix . 'imunify_bot_rl',
			$prefix . 'imunify_bot_blocks',
			$prefix . 'imunify_bot_blocks_active',
			$prefix . 'imunify_bot_violations',
			$prefix . 'imunify_bot_hourly',
			$prefix . 'imunify_bot_hourly_ip',
		);
		foreach ( $tables as $table ) {
			// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange
			$wpdb->query( "DROP TABLE IF EXISTS `{$table}`" );
		}
		$wpdb->suppress_errors( $suppress );
	}

	/**
	 * Build the shim manager with its real collaborators.
	 *
	 * @param string $mu_dir Absolute path to wp-content/mu-plugins.
	 * @return BotShimManager
	 */
	private static function shimManager( $mu_dir ) {
		return new BotShimManager( new MuPluginInstaller( $mu_dir ), new LoopbackSafetyTest(), new LoopbackStatus() );
	}

	/**
	 * Resolve WPMU_PLUGIN_DIR, or '' when unavailable.
	 *
	 * @return string
	 */
	private static function muPluginDir() {
		return defined( 'WPMU_PLUGIN_DIR' ) && '' !== WPMU_PLUGIN_DIR
			? (string) WPMU_PLUGIN_DIR
			: '';
	}
}

Directory Contents

Dirs: 1 × Files: 77
Name Size Perms Modified Actions
data DIR
- drwxr-xr-x 2026-11-03 01:06:32
Edit Download
11.48 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
2.43 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
1.47 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
1.38 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
4.45 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
1.17 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
2.01 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
21.21 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
7.34 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
17.81 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
3.38 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
2.79 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
13.17 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
8.77 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
5.67 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
14.53 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
4.25 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
1.72 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
7.58 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
1.55 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
14.00 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
1.58 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
5.99 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
2.88 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
1.41 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
1.53 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
2.61 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
4.73 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
7.49 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
17.86 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
14.97 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
1.47 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
4.76 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
2.06 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
5.26 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
5.64 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
2.68 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
10.47 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
2.49 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
17.09 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
8.51 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
12.60 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
3.46 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
835 B lrw-r--r-- 2026-11-03 01:06:32
Edit Download
1.71 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
22.44 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
5.65 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
7.33 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
892 B lrw-r--r-- 2026-11-03 01:06:32
Edit Download
924 B lrw-r--r-- 2026-11-03 01:06:32
Edit Download
4.69 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
18.57 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
6.92 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
3.87 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
7.94 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
18.17 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
8.48 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
1.80 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
9.49 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
2.84 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
7.45 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
1.28 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
2.27 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
9.90 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
15.82 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
20.41 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
7.79 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
5.61 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
1.80 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
30.46 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
2.79 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
1.41 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
3.72 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
9.60 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
5.05 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
6.56 KB lrw-r--r-- 2026-11-03 01:06:32
Edit Download
5.16 KB lrw-r--r-- 2026-11-03 01:06:32
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