403Webshell
Server IP : 46.105.57.169  /  Your IP : 216.73.217.35
Web Server : Apache
System : Linux webm002.cluster120.gra.hosting.ovh.net 6.18.42-ovh-vps-grsec-zfs+ #1 SMP PREEMPT_DYNAMIC Wed Aug 5 15:59:48 CEST 2026 x86_64
User : verseaumee ( 152031)
PHP Version : 8.5.7
Disable Function : _dyuweyrj4,_dyuweyrj4r,dl
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : OFF  |  Pkexec : OFF
Directory :  /home/verseaumee/123click/assets/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /home/verseaumee/123click/assets/wp-2fa.tar
includes/index.php000064400000000046150755130600010174 0ustar00<?php
/**
 * Nothing to see here.
 */
includes/functions/index.php000064400000000046150755130600012204 0ustar00<?php
/**
 * Nothing to see here.
 */
includes/functions/core.php000064400000020524150755130600012030 0ustar00<?php
/**
 * Core plugin functionality.
 *
 * @package WP2FA
 */

namespace WP2FA\Core;

use WP2FA\WP2FA;
use WP2FA\Utils\Settings_Utils;
use WP2FA\Admin\Helpers\WP_Helper;
use WP2FA\Admin\Views\Re_Login_2FA;
use WP2FA\Admin\Helpers\User_Helper;
use WP2FA\Admin\Controllers\Settings;

/**
 * Default setup routine
 *
 * @return void
 */
function setup() {
	$n = function ( $function ) {
		return __NAMESPACE__ . "\\$function";
	};

	add_action( 'init', $n( 'i18n' ) );
	add_action( 'init', $n( 'init' ) );
	add_action( 'admin_enqueue_scripts', $n( 'admin_scripts' ) );
	add_action( 'admin_enqueue_scripts', $n( 'admin_styles' ) );

	// Hook to allow async or defer on asset loading.
	add_filter( 'script_loader_tag', $n( 'script_loader_tag' ), 10, 2 );

	/**
	 * Fires after the plugin is loaded.
	 *
	 * @since 2.0.0
	 */
	do_action( WP_2FA_PREFIX . 'loaded' );
}

/**
 * Registers the default textdomain.
 *
 * @return void
 */
function i18n() {
	$locale = apply_filters( 'plugin_locale', get_locale(), 'wp-2fa' );
	load_textdomain( 'wp-2fa', WP_LANG_DIR . '/wp-2fa/wp-2fa-' . $locale . '.mo' );
	load_plugin_textdomain( 'wp-2fa', false, plugin_basename( WP_2FA_PATH ) . '/languages/' );
}

/**
 * Initializes the plugin and fires an action other plugins can hook into.
 *
 * @return void
 */
function init() {

	/**
	 * Fires when plugin is initiated.
	 *
	 * @since 2.0.0
	 */
	do_action( WP_2FA_PREFIX . 'init' );
}

/**
 * Activate the plugin
 *
 * @return void
 */
function activate() {
	// First load the init scripts in case any rewrite functionality is being loaded.
	init();
	flush_rewrite_rules();

	// Check if the user is allowed to manage options for the site.
	if ( current_user_can( 'manage_options' ) ) {
		// Add an option to let our plugin know this user has not been through the setup wizard.
		Settings_Utils::update_option( 'redirect_on_activate', true );
	}

	// Add plugin version to wp_options.
	Settings_Utils::update_option( 'plugin_version', WP_2FA_VERSION );
}

/**
 * Deactivate the plugin
 *
 * Uninstall routines should be in uninstall.php
 *
 * @return void
 */
function deactivate() {
}

/**
 * Uninstall the plugin
 *
 * @return void
 */
function uninstall() {
	WP2FA::init();
	if ( ! empty( WP2FA::get_wp2fa_general_setting( 'delete_data_upon_uninstall' ) ) ) {
		// Delete settings from wp_options.
		if ( WP_Helper::is_multisite() ) {
			$network_id = get_current_network_id();
			global $wpdb;
			$wpdb->query(
				$wpdb->prepare(
					"
					DELETE FROM $wpdb->sitemeta
					WHERE meta_key LIKE %s
					AND site_id = %d
					",
					array(
						'%wp_2fa_%',
						$network_id,
					)
				)
			);
		} else {
			global $wpdb;
			$wpdb->query(
				$wpdb->prepare(
					"
					DELETE FROM $wpdb->options
					WHERE option_name LIKE %s
					",
					array(
						'%wp_2fa_%',
					)
				)
			);
		}

		global $wpdb;
		$wpdb->query(
			$wpdb->prepare(
				"
				DELETE FROM $wpdb->usermeta
				WHERE 1
				AND meta_key LIKE %s
				",
				array(
					WP_2FA_PREFIX . 'wp_2fa_%',
				)
			)
		);
	}
}

/**
 * The list of knows contexts for enqueuing scripts/styles.
 *
 * @return array
 */
function get_enqueue_contexts() {
	return array( 'admin', 'frontend', 'shared' );
}

/**
 * Generate an URL to a script, taking into account whether SCRIPT_DEBUG is enabled.
 *
 * @param string $script Script file name (no .js extension).
 * @param string $context Context for the script ('admin', 'frontend', or 'shared').
 *
 * @return string|\WP_Error URL
 */
function script_url( $script, $context ) {

	if ( ! in_array( $context, get_enqueue_contexts(), true ) ) {
		return new \WP_Error( 'invalid_enqueue_context', 'Invalid $context specified in WP2FA script loader.' );
	}

	return WP_2FA_URL . 'dist/js/' . $script . '.js';
}

/**
 * Generate an URL to a stylesheet, taking into account whether SCRIPT_DEBUG is enabled.
 *
 * @param string $stylesheet Stylesheet file name (no .css extension).
 * @param string $context Context for the script ('admin', 'frontend', or 'shared').
 *
 * @return string|\WP_Error  URL
 */
function style_url( $stylesheet, $context ) {

	if ( ! in_array( $context, get_enqueue_contexts(), true ) ) {
		return new \WP_Error( 'invalid_enqueue_context', 'Invalid $context specified in WP2FA stylesheet loader.' );
	}

	return WP_2FA_URL . 'dist/css/' . $stylesheet . '.css';
}

/**
 * Enqueue scripts for admin.
 *
 * @return void
 */
function admin_scripts() {

	global $pagenow;

	// Get page argument from $_GET array.
	$page = ( isset( $_GET['page'] ) ) ? \sanitize_text_field( \wp_unslash( $_GET['page'] ) ) : ''; // phpcs:ignore
	if ( ( empty( $page ) || false === strpos( $page, 'wp-2fa' ) ) && 'profile.php' !== $pagenow ) {
		return;
	}

	\wp_enqueue_script(
		'wp_2fa_admin',
		script_url( 'admin', 'admin' ),
		array( 'jquery-ui-widget', 'jquery-ui-core', 'jquery-ui-autocomplete', 'wp_2fa_micro_modals', 'select2' ),
		WP_2FA_VERSION,
		true
	);

	\wp_enqueue_script(
		'wp_2fa_micro_modals',
		script_url( 'micromodal', 'admin' ),
		array(),
		WP_2FA_VERSION,
		true
	);

	enqueue_select2_scripts();

	// Data array.
	$data_array = array(
		'ajaxURL'                        => \admin_url( 'admin-ajax.php' ),
		'roles'                          => WP_Helper::get_roles_wp(),
		'nonce'                          => \wp_create_nonce( 'wp-2fa-settings-nonce' ),
		'codeValidatedHeading'           => \esc_html__( 'Congratulations', 'wp-2fa' ),
		'codeValidatedText'              => \esc_html__( 'Your account just got more secure', 'wp-2fa' ),
		'codeValidatedButton'            => \esc_html__( 'Close Wizard & Refresh', 'wp-2fa' ),
		'processingText'                 => \esc_html__( 'Processing Update', 'wp-2fa' ),
		'email_sent_success'             => \esc_html__( 'Email successfully sent', 'wp-2fa' ),
		'email_sent_failure'             => \esc_html__( 'Email delivery failed', 'wp-2fa' ),
		'invalidEmail'                   => \esc_html__( 'Please use a valid email address', 'wp-2fa' ),
		'license_validation_in_progress' => \esc_html__( 'Validating your license, please wait...', 'wp-2fa' ),
	);
	\wp_localize_script( 'wp_2fa_admin', 'wp2faData', $data_array );

	$role = User_Helper::get_user_role();

	$re_login = Settings::get_role_or_default_setting( Re_Login_2FA::RE_LOGIN_SETTINGS_NAME, 'current', $role );

	$data_array = array(
		'ajaxURL'         => \admin_url( 'admin-ajax.php' ),
		'nonce'           => \wp_create_nonce( 'wp2fa-verify-wizard-page' ),
		'codesPreamble'   => \esc_html__( 'These are the 2FA backup codes for the user', 'wp-2fa' ),
		'readyText'       => \esc_html__( 'I\'m ready', 'wp-2fa' ),
		'codeReSentText'  => \esc_html__( 'New code sent', 'wp-2fa' ),
		'backupCodesSent' => \esc_html__( 'Backup codes sent', 'wp-2fa' ),
		'reLoginEnabled'  => Re_Login_2FA::ENABLED_SETTING_VALUE,
		'reLogin'         => $re_login,
	);
	\wp_localize_script( 'wp_2fa_admin', 'wp2faWizardData', $data_array );
}

/**
 * Enqueue Select2 jQuery library
 *
 * @return void
 */
function enqueue_select2_scripts() {
	wp_enqueue_style( 'select2', style_url( 'select2.min', 'admin' ), array(), WP_2FA_VERSION );
	wp_enqueue_script( 'select2', script_url( 'select2.min', 'admin' ), array( 'jquery' ), WP_2FA_VERSION, false );
}

/**
 * Enqueue styles for admin.
 *
 * @return void
 */
function admin_styles() {

	wp_enqueue_style(
		'wp_2fa_admin',
		style_url( 'admin-style', 'admin' ),
		array(),
		WP_2FA_VERSION
	);
}

/**
 * Add async/defer attributes to enqueued scripts that have the specified script_execution flag.
 *
 * @link https://core.trac.wordpress.org/ticket/12009
 * @param string $tag    The script tag.
 * @param string $handle The script handle.
 * @return string
 */
function script_loader_tag( $tag, $handle ) {
	$script_execution = wp_scripts()->get_data( $handle, 'script_execution' );

	if ( ! $script_execution ) {
		return $tag;
	}

	if ( 'async' !== $script_execution && 'defer' !== $script_execution ) {
		return $tag;
	}

	// Abort adding async/defer for scripts that have this script as a dependency. _doing_it_wrong()?
	foreach ( wp_scripts()->registered as $script ) {
		if ( in_array( $handle, $script->deps, true ) ) {
			return $tag;
		}
	}

	// Add the attribute if it hasn't already been added.
	if ( ! preg_match( ":\s$script_execution(=|>|\s):", $tag ) ) {
		$tag = preg_replace( ':(?=></script>):', " $script_execution", $tag, 1 );
	}

	return $tag;
}

/**
 * Generates random string used to salt the key
 *
 * @return string
 *
 * @since 2.3.0
 */
function wp_salt(): string {
	return WP2FA::get_secret_key();
}
includes/functions/login-header.php000064400000015454150755130600013444 0ustar00<?php

/**
 * Output the login page header.
 *
 * @param string   $title    Optional. WordPress login Page title to display in the `<title>` element.
 *                           Default 'Log In'.
 * @param string   $message  Optional. Message to display in header. Default empty.
 * @param WP_Error $wp_error Optional. The error to pass. Default is a WP_Error instance.
 */
function login_header( $title = 'Log In', $message = '', $wp_error = null ) {

	global $error, $interim_login, $action;
	// Don't index any of these forms.
	add_action( 'login_head', 'wp_no_robots' );
	add_action( 'login_head', 'wp_login_viewport_meta' );

	if ( ! is_wp_error( $wp_error ) ) {
			$wp_error = new WP_Error();
	}
		// Shake it!
		$shake_error_codes = array( 'empty_password', 'empty_email', 'invalid_email', 'invalidcombo', 'empty_username', 'invalid_username', 'incorrect_password' );
		/**
		 * Filters the error codes array for shaking the login form.
		 *
		 * @since 3.0.0
		 *
		 * @param array $shake_error_codes Error codes that shake the login form.
		 */
		$shake_error_codes = apply_filters( 'shake_error_codes', $shake_error_codes );
	if ( $shake_error_codes && $wp_error->get_error_code() && in_array( $wp_error->get_error_code(), $shake_error_codes ) ) {
			add_action( 'login_head', 'wp_shake_js', 12 );
	}
		$login_title = get_bloginfo( 'name', 'display' );
		/* translators: Login screen title. 1: Login screen name, 2: Network or site name */
		$login_title = sprintf( __( '%1$s &lsaquo; %2$s &#8212; WordPress' ), $title, $login_title );
		/**
		 * Filters the title tag content for login page.
		 *
		 * @since 4.9.0
		 *
		 * @param string $login_title The page title, with extra context added.
		 * @param string $title       The original page title.
		 */
		$login_title = apply_filters( 'login_title', $login_title, $title );
	?><!DOCTYPE html>
		<!--[if IE 8]>
				<html xmlns="http://www.w3.org/1999/xhtml" class="ie8" <?php language_attributes(); ?>>
		<![endif]-->
		<!--[if !(IE 8) ]><!-->
				<html xmlns="http://www.w3.org/1999/xhtml" <?php language_attributes(); ?>>
		<!--<![endif]-->
		<head>
		<meta http-equiv="Content-Type" content="<?php bloginfo( 'html_type' ); ?>; charset=<?php bloginfo( 'charset' ); ?>" />
		<title><?php echo $login_title; ?></title>
		<?php
		wp_enqueue_style( 'login' );
		/*
		 * Remove all stored post data on logging out.
		 * This could be added by add_action('login_head'...) like wp_shake_js(),
		 * but maybe better if it's not removable by plugins
		 */
		if ( 'loggedout' == $wp_error->get_error_code() ) {
			?>
				<script>if("sessionStorage" in window){try{for(var key in sessionStorage){if(key.indexOf("wp-autosave-")!=-1){sessionStorage.removeItem(key)}}}catch(e){}};</script>
				<?php
		}
		/**
		 * Enqueue scripts and styles for the login page.
		 *
		 * @since 3.1.0
		 */
		do_action( 'login_enqueue_scripts' );
		/**
		 * Fires in the login page header after scripts are enqueued.
		 *
		 * @since 2.1.0
		 */
		do_action( 'login_head' );
		if ( \WP2FA\Admin\Helpers\WP_Helper::is_multisite() ) {
				$login_header_url   = network_home_url();
				$login_header_title = get_network()->site_name;
		} else {
				$login_header_url   = __( 'https://wordpress.org/' );
				$login_header_title = __( 'Powered by WordPress' );
		}
		/**
		 * Filters link URL of the header logo above login form.
		 *
		 * @since 2.1.0
		 *
		 * @param string $login_header_url Login header logo URL.
		 */
		$login_header_url = apply_filters( 'login_headerurl', $login_header_url );
		/**
		 * Filters the title attribute of the header logo above login form.
		 *
		 * @since 2.1.0
		 *
		 * @param string $login_header_title Login header logo title attribute.
		 */
		$login_header_title = apply_filters( 'login_headertitle', $login_header_title );
		/*
		 * To match the URL/title set above, Multisite sites have the blog name,
		 * while single sites get the header title.
		 */
		if ( \WP2FA\Admin\Helpers\WP_Helper::is_multisite() ) {
				$login_header_text = get_bloginfo( 'name', 'display' );
		} else {
				$login_header_text = $login_header_title;
		}
		$classes = array( 'login-action-' . $action, 'wp-core-ui' );
		if ( is_rtl() ) {
				$classes[] = 'rtl';
		}
		if ( $interim_login ) {
				$classes[] = 'interim-login';
			?>
				<style type="text/css">html{background-color: transparent;}</style>
				<?php
				if ( 'success' === $interim_login ) {
						$classes[] = 'interim-login-success';
				}
		}
		$classes[] = ' locale-' . sanitize_html_class( strtolower( str_replace( '_', '-', get_locale() ) ) );
		/**
		 * Filters the login page body classes.
		 *
		 * @since 3.5.0
		 *
		 * @param array  $classes An array of body classes.
		 * @param string $action  The action that brought the visitor to the login page.
		 */
		$classes = apply_filters( 'login_body_class', $classes, $action );
		?>
		</head>
		<body class="login <?php echo esc_attr( implode( ' ', $classes ) ); ?>">
		<?php
		/**
		 * Fires in the login page header after the body tag is opened.
		 *
		 * @since 4.6.0
		 */
		do_action( 'login_header' );
		?>
		<div id="login">
				<h1><a href="<?php echo esc_url( $login_header_url ); ?>" title="<?php echo esc_attr( $login_header_title ); ?>" tabindex="-1"><?php echo $login_header_text; ?></a></h1>
		<?php
		unset( $login_header_url, $login_header_title );
		/**
		 * Filters the message to display above the login form.
		 *
		 * @since 2.1.0
		 *
		 * @param string $message Login message text.
		 */
		$message = apply_filters( 'login_message', $message );
		if ( ! empty( $message ) ) {
				echo $message . "\n";
		}
		// In case a plugin uses $error rather than the $wp_errors object.
		if ( ! empty( $error ) ) {
				$wp_error->add( 'error', $error );
				unset( $error );
		}
		if ( $wp_error->get_error_code() ) {
				$errors   = '';
				$messages = '';
			foreach ( $wp_error->get_error_codes() as $code ) {
					$severity = $wp_error->get_error_data( $code );
				foreach ( $wp_error->get_error_messages( $code ) as $error_message ) {
					if ( 'message' == $severity ) {
							$messages .= '  ' . $error_message . "<br />\n";
					} else {
						$errors .= '    ' . $error_message . "<br />\n";
					}
				}
			}
			if ( ! empty( $errors ) ) {
					/**
					 * Filters the error messages displayed above the login form.
					 *
					 * @since 2.1.0
					 *
					 * @param string $errors Login error message.
					 */
					echo '<div id="login_error">' . apply_filters( 'login_errors', $errors ) . "</div>\n";
			}
			if ( ! empty( $messages ) ) {
					/**
					 * Filters instructional messages displayed above the login form.
					 *
					 * @since 2.5.0
					 *
					 * @param string $messages Login messages.
					 */
					echo '<p class="message">' . apply_filters( 'login_messages', $messages ) . "</p>\n";
			}
		}
} // End of login_header().

function wp_login_viewport_meta() {
	?>
		<meta name="viewport" content="width=device-width" />
		<?php
}
includes/classes/Utils/class-user-utils.php000064400000025460150755130600015050 0ustar00<?php
/**
 * Responsible for different user's manipulations.
 *
 * @package    wp2fa
 * @subpackage user-utils
 *
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 *
 * @see       https://wordpress.org/plugins/wp-2fa/
 */

declare(strict_types=1);

namespace WP2FA\Utils;

use WP2FA\WP2FA;
use WP2FA\Methods\Backup_Codes;
use WP2FA\Admin\Helpers\User_Helper;

if ( ! class_exists( '\WP2FA\Utils\User_Utils' ) ) {
	/**
	 * Utility class for creating modal popup markup.
	 *
	 * @package WP2FA\Utils
	 *
	 * @since 1.4.2
	 */
	class User_Utils {
		/**
		 * Holds map with human readable 2FA statuses.
		 *
		 * @var array
		 *
		 * @since 2.2.0
		 */
		private static $statuses;

		/**
		 * Determines the proper 2FA status of the given user.
		 *
		 * @param \WP_User $user - The user to check.
		 *
		 * @return array
		 *
		 * @since 2.2.0
		 */
		public static function determine_user_2fa_status( $user ) {
			// Get current user, we going to need this regardless.
			$current_user = wp_get_current_user();

			// Bail if we still dont have an object.
			if ( ! is_a( $user, '\WP_User' ) || ! is_a( $current_user, '\WP_User' ) ) {
				return array();
			}

			$roles = (array) $user->roles;

			// Grab grace period UNIX time.
			$grace_period_expired = User_Helper::get_grace_period( $user );
			$is_user_excluded     = User_Helper::is_excluded( $user->ID );
			$is_user_enforced     = User_Helper::is_enforced( $user->ID );
			$is_user_locked       = User_Helper::is_user_locked( $user->ID );
			$user_last_login      = User_Helper::get_login_date_for_user( $user->ID );

			// First lets see if the user already has a token.
			$enabled_methods = User_Helper::get_enabled_method_for_user( $user );

			$no_enforced_methods = false;
			if ( 'do-not-enforce' === WP2FA::get_wp2fa_setting( 'enforcement-policy' ) ) {
				$no_enforced_methods = true;
			}

			$user_type = array();
			// Order is important here - for speed optimizations see self::extract_statuses() function of that class - we probably need to redo the whole thing.
			if ( $no_enforced_methods && ! empty( $enabled_methods ) ) {
				$user_type[] = 'no_required_has_enabled';
			}

			if ( $no_enforced_methods && empty( $enabled_methods ) && ! $is_user_excluded ) {
				if ( empty( $user_last_login ) ) {
					$user_type[] = User_Helper::USER_UNDETERMINED_STATUS;
				} else {
					$user_type[] = 'no_required_not_enabled';
				}
			}

			if ( ! $no_enforced_methods && empty( $enabled_methods ) && ! $is_user_excluded && $is_user_enforced ) {
				$user_type[] = 'user_needs_to_setup_2fa';
			}

			if ( ! $no_enforced_methods && empty( $enabled_methods ) && ! $is_user_excluded && ! $is_user_enforced ) {
				if ( empty( $user_last_login ) ) {
					$user_type[] = User_Helper::USER_UNDETERMINED_STATUS;
				} else {
					$user_type[] = 'no_required_not_enabled';
				}
			}

			if ( $is_user_excluded ) {
				$user_type[] = 'user_is_excluded';
			}

			if ( $is_user_locked ) {
				$user_type[] = 'user_is_locked';
			}

			if ( ! empty( $enabled_methods ) ) {
				$user_type[] = 'has_enabled_methods';
			}

			$codes_remaining = Backup_Codes::codes_remaining_for_user( $user );
			if ( 0 === $codes_remaining ) {
				$user_type[] = 'user_needs_to_setup_backup_codes';
			}

			if ( empty( $roles ) ) {
				$user_type[] = 'orphan_user'; // User has no role.
			}

			if ( \current_user_can( 'manage_options' ) ) {
				$user_type[] = 'can_manage_options';
			}

			if ( \current_user_can( 'read' ) ) {
				$user_type[] = 'can_read';
			}

			if ( $grace_period_expired ) {
				$user_type[] = 'grace_has_expired';
			}

			if ( $current_user->ID === $user->ID ) {
				$user_type[] = 'viewing_own_profile';
			}

			/*
			 * Gives the ability to alter the user types for the user.
			 *
			 * @param string $user_type - Type of the user.
			 * @param \WP_User $user - The WP user.
			 *
			 * @since 2.0.0
			 */
			return \apply_filters( WP_2FA_PREFIX . 'additional_user_types', $user_type, $user );
		}

		/**
		 * Checks is all values exist in given array.
		 *
		 * @param array $needles  - Which values to check.
		 * @param array $haystack - The array to check against.
		 *
		 * @return bool
		 *
		 * @since 2.2.0
		 */
		public static function in_array_all( $needles, $haystack ) {
			return empty( array_diff( $needles, $haystack ) );
		}

		/**
		 * Check if role is not in given array of roles.
		 *
		 * @param array $roles      - All roles.
		 * @param array $user_roles - The User roles.
		 *
		 * @return bool
		 *
		 * @since 2.2.0
		 */
		public static function role_is_not( $roles, $user_roles ) {
			if (
			empty(
				array_intersect(
					$roles,
					$user_roles
				)
			)
			) {
				return true;
			}

			return false;
		}

		/**
		 * Return all users, either by using a direct query or get_users.
		 *
		 * @param string $method     Method to use.
		 * @param array  $users_args Query arguments.
		 *
		 * @return mixed Array of IDs/Object of Users.
		 *
		 * @since 2.2.0
		 */
		public static function get_all_users_data( $method, $users_args ) {
			if ( 'get_users' === $method ) {
				return get_users( $users_args );
			}

			// method is "query", let's build the SQL query ourselves.
			global $wpdb;

			$batch_size = isset( $users_args['batch_size'] ) ? $users_args['batch_size'] : false;
			$offset     = isset( $users_args['count'] ) ? $users_args['count'] * $batch_size : false;

			// Default.
			$select = 'SELECT ID, user_login FROM ' . $wpdb->users . '';

			// If we want to grab users with a specific role.
			if ( isset( $users_args['role__in'] ) && ! empty( $users_args['role__in'] ) ) {
				$roles  = $users_args['role__in'];
				$select = '
					SELECT  ID, user_login
					FROM    ' . $wpdb->users . ' u INNER JOIN ' . $wpdb->usermeta . ' um
					ON      u.ID = um.user_id
					WHERE   um.meta_key LIKE \'' . $wpdb->base_prefix . '%capabilities\'' . // phpcs:ignore
					' AND     (
			';
				$i      = 1;
				foreach ( $roles as $role ) {
					$select .= ' um.meta_value    LIKE    \'%"' . $role . '"%\' ';
					if ( $i < count( $roles ) ) {
						$select .= ' OR ';
					}
					++$i;
				}
				$select .= ' ) ';

				$excluded_users = ( ! empty( $users_args['excluded_users'] ) ) ? $users_args['excluded_users'] : array();

				$excluded_users = array_map(
					function ( $excluded_user ) {
						return '"' . $excluded_user . '"';
					},
					$excluded_users
				);

				if ( ! empty( $excluded_users ) ) {
					$select .= '
						AND user_login NOT IN ( ' . implode( ',', $excluded_users ) . ' )
				';
				}

				$skip_existing_2fa_users = ( ! empty( $users_args['skip_existing_2fa_users'] ) ) ? $users_args['skip_existing_2fa_users'] : false;

				if ( $skip_existing_2fa_users ) {
					$select .= '
				AND u.ID NOT IN (
				  SELECT DISTINCT user_id FROM  ' . $wpdb->usermeta . ' WHERE meta_key = \'wp_2fa_enabled_methods\'
				)
				';
				}
			}

			if ( $batch_size ) {
				$select .= ' LIMIT ' . $batch_size . ' OFFSET ' . $offset . '';
			}

			return $wpdb->get_results( $select ); // phpcs:ignore
		}

		/**
		 * Collects all the users with 2FA meta data.
		 *
		 * @param array $users_args - Arguments.
		 *
		 * @return string
		 *
		 * @since 2.2.0
		 */
		public static function get_all_user_ids_who_have_wp_2fa_metadata_present( $users_args ) {
			global $wpdb;

			$batch_size = isset( $users_args['batch_size'] ) ? $users_args['batch_size'] : false;
			$offset     = isset( $users_args['count'] ) ? $users_args['count'] * $batch_size : false;

			$select = '
			SELECT ID FROM ' . $wpdb->users . '
			INNER JOIN ' . $wpdb->usermeta . ' ON ' . $wpdb->users . '.ID = ' . $wpdb->usermeta . '.user_id
			WHERE ' . $wpdb->usermeta . '.meta_key LIKE \'wp_2fa_%\'
		';

			if ( $batch_size ) {
				$select .= '
				LIMIT ' . $batch_size . ' OFFSET ' . $offset . '
			';
			}

			$users = $wpdb->get_results( $select ); // phpcs:ignore

			$users = array_map(
				function ( $user ) {
					return (int) $user->ID;
				},
				$users
			);

			$users = implode( ',', $users );

			return $users;
		}

		/**
		 * Retrieve string of comma separated IDs.
		 *
		 * @param string $method     Method to use.
		 * @param array  $users_args Query arguments.
		 *
		 * @return string List of IDs.
		 *
		 * @since 2.2.0
		 */
		public static function get_all_user_ids( $method, $users_args ) {
			$user_data = self::get_all_users_data( $method, $users_args );

			$users = array_map(
				function ( $user ) {
					return (int) $user->ID;
				},
				$user_data
			);

			return implode( ',', $users );
		}

		/**
		 * Retrieve array if user IDs and login names.
		 *
		 * @param string $method     Method to use.
		 * @param array  $users_args Query arguments.
		 *
		 * @return array User details.
		 *
		 * @since 2.2.0
		 */
		public static function get_all_user_ids_and_login_names( $method, $users_args ) {
			$user_data = self::get_all_users_data( $method, $users_args );

			$users = array_map(
				function ( $user ) {
					$user_item['ID']         = (int) $user->ID;
					$user_item['user_login'] = $user->user_login;

					return $user_item;
				},
				$user_data
			);

			return $users;
		}

		/**
		 * Returns the array with human readable statuses of the WP 2FA.
		 *
		 * @since 1.6
		 *
		 * @return array
		 */
		public static function get_human_readable_user_statuses() {
			if ( null === self::$statuses ) {
				self::$statuses =
				array(
					'has_enabled_methods'                 => __( 'Configured', 'wp-2fa' ),
					'user_needs_to_setup_2fa'             => __( 'Required but not configured', 'wp-2fa' ),
					'no_required_has_enabled'             => __( 'Configured (but not required)', 'wp-2fa' ),
					'no_required_not_enabled'             => __( 'Not required & not configured', 'wp-2fa' ),
					'user_is_excluded'                    => __( 'Not allowed', 'wp-2fa' ),
					'user_is_locked'                      => __( 'Locked', 'wp-2fa' ),
					User_Helper::USER_UNDETERMINED_STATUS => __( 'User has not logged in yet, 2FA status is unknown', 'wp-2fa' ),
				);
			}

			return self::$statuses;
		}

		/**
		 * Gets the user types extracted with @see User_Utils::determine_user_2fa_status,
		 * checks values and generates human readable 2FA status text.
		 *
		 * @param array $user_types - The types of the user.
		 *
		 * @return array An array with the id and label elements of user 2FA status. Empty in case there is not match.
		 *
		 * @since 1.7.0 Changed the function to return the id and label of the first match it finds instead of concatenated labels of all matched statuses.
		 */
		public static function extract_statuses( array $user_types ) {
			if ( null === self::$statuses ) {
				self::get_human_readable_user_statuses();
			}

			if ( empty( $user_types ) ) {
				return array();
			}

			$key_to_search = reset( $user_types );

			if ( isset( self::$statuses[ $key_to_search ] ) ) {
				return array(
					'id'    => $key_to_search,
					'label' => self::$statuses[ $key_to_search ],
				);
			}

			return array();
		}
	}
}
includes/classes/Utils/index.php000064400000000046150755130600012731 0ustar00<?php
/**
 * Nothing to see here.
 */
includes/classes/Utils/class-debugging.php000064400000011403150755130600014657 0ustar00<?php
/**
 * Responsible for logging.
 *
 * @package    wp2fa
 * @subpackage utils
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 * @link       https://wordpress.org/plugins/wp-2fa/
 * @since 1.4.2
 */

declare(strict_types=1);

namespace WP2FA\Utils;

use PhpParser\Node\Stmt\Static_;

if ( ! class_exists( '\WP2FA\Utils\Debugging' ) ) {
	/**
	 * Utility class for creating modal popup markup.
	 *
	 * @package WP2FA\Utils
	 *
	 * @since 1.4.2
	 */
	class Debugging {

		/**
		 * Local cache for the logging dir so that it doesn't need to be repopulated each time get_logging_dir_path is called.
		 *
		 * @var string
		 *
		 * @since 1.4.2
		 */
		private static $logging_dir_path = '';

		/**
		 * Retrieve the logging status
		 *
		 * @return boolean
		 *
		 * @since 1.4.2
		 */
		private static function is_logging_enabled() {
			/**
			 * Enables / Disables the logging for the plugin.
			 *
			 * @param bool $disabled - Default logging for the plugin.
			 */
			return apply_filters( WP_2FA_PREFIX . 'logging_enabled', false );
		}

		/**
		 * Logs the given message
		 *
		 * @param string $message - The message to log.
		 *
		 * @return void
		 *
		 * @since 1.4.2
		 */
		public static function log( $message ) {
			if ( self::is_logging_enabled() ) {
				self::write_to_log( self::get_log_timestamp() . "\n" . $message . "\n" . __( 'Current memory usage: ', 'wp-2fa' ) . memory_get_usage( true ) . "\n" );
			}
		}

		/**
		 * Retrieves the path to the log file
		 *
		 * @return string
		 *
		 * @since 1.4.2
		 */
		private static function get_logging_dir_path() {
			if ( strlen( self::$logging_dir_path ) === 0 ) {
				$uploads_dir            = wp_upload_dir( null, false );
				self::$logging_dir_path = trailingslashit( trailingslashit( $uploads_dir['basedir'] ) . WP_2FA_LOGS_DIR );
			}

			return self::$logging_dir_path;
		}

		/**
		 * Write data to log file.
		 *
		 * @param string $data     - Data to write to file.
		 * @param bool   $override - Set to true if overriding the file.
		 *
		 * @return bool
		 *
		 * @since 1.4.2
		 */
		private static function write_to_log( $data, $override = false ) {
			$logging_dir_path = self::get_logging_dir_path();
			if ( ! is_dir( $logging_dir_path ) ) {
				self::create_index_file();
				self::create_htaccess_file();
			}

			$log_file_name = gmdate( 'Y-m-d' );

			return self::write_to_file( 'wp-2fa-debug-' . $log_file_name . '-' . self::get_random_file_string_addon() . '.log', $data, $override );
		}

		/**
		 * Create an index.php file, if none exists, in order to
		 * avoid directory listing in the specified directory.
		 *
		 * @return bool
		 *
		 * @since 1.4.2
		 */
		private static function create_index_file() {
			return self::write_to_file( 'index.php', '<?php // Silence is golden' );
		}

		/**
		 * Create an .htaccess file, if none exists, in order to
		 * block access to directory listing in the specified directory.
		 *
		 * @return bool
		 *
		 * @since 1.4.2
		 */
		private static function create_htaccess_file() {
			return self::write_to_file( '.htaccess', 'Deny from all' );
		}

		/**
		 * Write data to log file in the uploads directory.
		 *
		 * @param string $filename - File name.
		 * @param string $content  - Contents of the file.
		 * @param bool   $override - (Optional) True if overriding file contents.
		 *
		 * @return bool
		 *
		 * @since 1.4.2
		 */
		private static function write_to_file( $filename, $content, $override = false ) {
			global $wp_filesystem;
			require_once ABSPATH . 'wp-admin/includes/file.php';
			WP_Filesystem();

			$logging_dir = self::get_logging_dir_path();

			$result = false;

			if ( ! is_dir( $logging_dir ) ) {
				if ( false === wp_mkdir_p( $logging_dir ) ) {
					return false;
				}
			}

			$filepath = $logging_dir . $filename;
			if ( ! $wp_filesystem->exists( $filepath ) || $override ) {
				$result = $wp_filesystem->put_contents( $filepath, $content );
			} else {
				$existing_content = $wp_filesystem->get_contents( $filepath );
				$result           = $wp_filesystem->put_contents( $filepath, $existing_content . $content );
			}

			return $result;
		}

		/**
		 * Returns the timestamp for log files.
		 *
		 * @return string
		 *
		 * @since 1.4.2
		 */
		private static function get_log_timestamp() {
			return '[' . gmdate( 'd-M-Y H:i:s' ) . ' UTC]';
		}

		/**
		 * Generates a short random string which is used to generate log file name.
		 *
		 * @return string
		 *
		 * @since 2.8.0
		 */
		private static function get_random_file_string_addon(): string {
			$rnd_string = Settings_Utils::get_option( 'debug_name', false );
			if ( ! $rnd_string ) {
				$rnd_string = (string) \wp_generate_password( 20, false, false );
				Settings_Utils::update_option( 'debug_name', $rnd_string );
			}

			return $rnd_string;
		}
	}
}
includes/classes/Utils/class-date-time-utils.php000064400000004160150755130600015735 0ustar00<?php
/**
 * Responsible for date / time manipulation.
 *
 * @package    wp2fa
 * @subpackage utils
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 * @link       https://wordpress.org/plugins/wp-2fa/
 */

declare(strict_types=1);

namespace WP2FA\Utils;

use WP2FA\WP2FA;

if ( ! class_exists( '\WP2FA\Utils\Date_Time_Utils' ) ) {
	/**
	 * Utility class for date and time manipulation, format conversion and so on.
	 *
	 * @package WP2FA\Utils
	 * @since 1.4.2
	 */
	class Date_Time_Utils {

		/**
		 * Formats the date string
		 *
		 * @param string|null $grace_policy Grace policy value.
		 * @param int         $grace_expiry Expiration time as unix based timestamp.
		 *
		 * @return string Translated grace period expiration string.
		 */
		public static function format_grace_period_expiration_string( $grace_policy = null, $grace_expiry = - 1 ) {
			if ( null === $grace_policy ) {
				$grace_policy = WP2FA::get_wp2fa_setting( 'grace-policy' );
			}

			if ( 'no-grace-period' === $grace_policy ) {
				return \esc_html__( 'no grace period', 'wp-2fa' );
			}

			if ( -1 === $grace_expiry ) {
				if ( 'use-grace-period' === $grace_policy ) {
					$grace_period             = WP2FA::get_wp2fa_setting( 'grace-period' );
					$grace_period_denominator = WP2FA::get_wp2fa_setting( 'grace-period-denominator' );
					$grace_period_string      = $grace_period . ' ' . $grace_period_denominator;
					$grace_expiry             = (int) strtotime( $grace_period_string );
				} else {
					// this will probably never be reached, leaving it here for now just in case.
					$grace_expiry = time();
				}
			}

			$expiration_date_time = implode(
				' ',
				array(
					// Purposefully not using the SettingsUtil class as we don't want this prefixed.
					date_i18n( get_option( 'date_format' ), $grace_expiry ),
					date_i18n( get_option( 'time_format' ), $grace_expiry ),
				)
			);

			/* translators: Grace period expiration label. %s: Date and time formatted using WordPress date and time formats. */
			return sprintf( \esc_html__( 'before %s', 'wp-2fa' ), $expiration_date_time );
		}
	}
}
includes/classes/Utils/class-settings-utils.php000064400000006640150755130600015731 0ustar00<?php
/**
 * Responsible for various settings manipulations.
 *
 * @package    wp2fa
 * @subpackage utils
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 * @link       https://wordpress.org/plugins/wp-2fa/
 */

declare(strict_types=1);

namespace WP2FA\Utils;

use WP2FA\Admin\Helpers\WP_Helper;

if ( ! class_exists( '\WP2FA\Utils\Settings_Utils' ) ) {

	/**
	 * Utility class handling settings CRUD.
	 *
	 * @package WP2FA\Utils
	 *
	 * @since 1.7.0
	 */
	class Settings_Utils {

		/**
		 * Creates a hash based on the passed settings array.
		 *
		 * @param array $settings - Settings array.
		 *
		 * @return string
		 */
		public static function create_settings_hash( array $settings ): string {
			return md5( json_encode( $settings ) ); // phpcs:ignore
		}

		/**
		 * Returns an option by given name
		 *
		 * @param string $setting_name - The name of the option.
		 * @param mixed  $default_value - The default value if there is no one stored.
		 *
		 * @return mixed
		 *
		 * @since 2.0.0
		 */
		public static function get_option( $setting_name, $default_value = false ) {
			$prefixed_setting_name = self::setting_prefixer( $setting_name );
			return ( WP_Helper::is_multisite() ) ? get_network_option( null, $prefixed_setting_name, $default_value ) : get_option( $prefixed_setting_name, $default_value );
		}

		/**
		 * Updates an option by a given name with a given value
		 *
		 * @param string $setting_name - The name of the setting to update.
		 * @param mixed  $new_value - The value to be stored.
		 *
		 * @return mixed
		 *
		 * @since 2.0.0
		 */
		public static function update_option( $setting_name, $new_value ) {
			$prefixed_setting_name = self::setting_prefixer( $setting_name );
			return ( WP_Helper::is_multisite() ) ? update_network_option( null, $prefixed_setting_name, $new_value ) : update_option( $prefixed_setting_name, $new_value, true );
		}

		/**
		 * Deletes an option by a given name
		 *
		 * @param string $setting_name - The name of the option to delete.
		 *
		 * @return mixed
		 *
		 * @since 2.0.0
		 */
		public static function delete_option( $setting_name ) {
			$prefixed_setting_name = self::setting_prefixer( $setting_name );
			return ( WP_Helper::is_multisite() ) ? delete_network_option( null, $prefixed_setting_name ) : delete_option( $prefixed_setting_name );
		}

		/**
		 * Created a prefixed setting name from supplied string.
		 *
		 * @param  string $setting_name - The name of the setting.
		 *
		 * @return string
		 */
		private static function setting_prefixer( $setting_name ) {
			// Ensure we have not already been passed a prefixed setting name.
			return ( strpos( $setting_name, 'wp_2fa_' ) === 0 ) ? $setting_name : WP_2FA_PREFIX . $setting_name;
		}

		/**
		 * Converts a string (e.g. 'yes' or 'no') to a bool.
		 *
		 * @since 2.0.0
		 * @param string $string String to convert.
		 * @return bool
		 */
		public static function string_to_bool( $string ) {
			return is_bool( $string ) ? $string : ( 'yes' === $string || 1 === $string || 'true' === $string || '1' === $string || 'on' === $string || 'enable' === $string );
		}

		/**
		 * Converts a bool to a 'yes' or 'no'.
		 *
		 * @since 2.0.0
		 * @param bool $bool String to convert.
		 * @return string
		 */
		public static function bool_to_string( $bool ) {
			if ( ! is_bool( $bool ) ) {
				$bool = self::string_to_bool( $bool );
			}
			return true === $bool ? 'yes' : 'no';
		}
	}
}
includes/classes/Utils/class-generate-modal.php000064400000005433150755130600015616 0ustar00<?php
/**
 * Responsible for modal dialogs generation.
 *
 * @package    wp2fa
 * @subpackage utils
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 * @link       https://wordpress.org/plugins/wp-2fa/
 */

declare(strict_types=1);

namespace WP2FA\Utils;

use WP2FA\WP2FA;

if ( ! class_exists( '\WP2FA\Utils\Generate_Modal' ) ) {

	/**
	 * Utility class for creating modal popup markup.
	 *
	 * @package WP2FA\Utils
	 *
	 * @since 1.4.2
	 */
	class Generate_Modal {

		/**
		 * General modals based on given args.
		 *
		 * @param  string $modal_id              Unique ID for the modal.
		 * @param  string $modal_title           (Optional) Modal title.
		 * @param  string $modal_content         The HTML content we want to show in the modal.
		 * @param  array  $modal_footer_buttons  The HTML content we want to show at the footer of the modal, usually buttons.
		 * @param  string $should_modal_autoopen (Optional) if anything is passed we will open the modal automatically.
		 * @param  string $max_width             (Optional) Max possible width of modal.
		 */
		public static function generate_modal( $modal_id, $modal_title, $modal_content, $modal_footer_buttons = array(), $should_modal_autoopen = '', $max_width = '' ) {

			$buttons = '';
			$modal   = '';
			$title   = ( ! empty( $modal_title ) ) ? '<header class="modal__header"><h4 class="modal__title" id="modal-' . \esc_attr( $modal_id ) . '-title">' . $modal_title . '</h4></header>' : false;

			if ( ! empty( $modal_footer_buttons ) ) {
				foreach ( $modal_footer_buttons as $button_markup ) {
					$buttons .= $button_markup;
				}
			}

			$styling_class = ( empty( WP2FA::get_wp2fa_white_label_setting( 'enable_wizard_styling' ) ) ) ? 'default_styling' : 'enable_styling';

			if ( ! empty( $should_modal_autoopen ) ) {
				$modal_class = 'wp2fa-modal micromodal-slide is-open ' . $styling_class;
				$hidden      = 'false';
			} else {
				$modal_class = 'wp2fa-modal micromodal-slide ' . $styling_class;
				$hidden      = 'true';
			}

			$max_width_styles = ( ! empty( $max_width ) ) ? 'style="max-width:' . \esc_attr( $max_width ) . '; min-width: 0;"' : false;

			$modal = '
			<div class="' . $modal_class . '" id="' . \esc_attr( $modal_id ) . '" aria-hidden="' . \esc_attr( $hidden ) . '">
			<div class="modal__overlay" tabindex="-1">
				<div class="modal__container" role="dialog" aria-modal="true" aria-labelledby="modal-' . \esc_attr( $modal_id ) . '-title" ' . $max_width_styles . '>
					' . $title . '
				<main class="modal__content wp2fa-form-styles" id="modal-' . \esc_attr( $modal_id ) . '-content">
					' . wpautop( $modal_content ) . '
				</main>
				<footer class="modal__footer">
					' . $buttons . '
				</footer>
				</div>
			</div>
			</div>
			';

			return $modal;
		}
	}
}
includes/classes/Utils/class-migration.php000064400000022000150755130600014710 0ustar00<?php
/**
 * Responsible for plugin updates.
 *
 * @package    wp2fa
 * @subpackage utils
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 * @link       https://wordpress.org/plugins/wp-2fa/
 */

declare(strict_types=1);

namespace WP2FA\Utils;

use WP2FA\Utils\Abstract_Migration;
use WP2FA\Utils\User_Utils;
use WP2FA\Utils\Settings_Utils;

defined( 'ABSPATH' ) || exit; // Exit if accessed directly.

/**
 * Migration class
 */
if ( ! class_exists( '\WP2FA\Utils\Migration' ) ) {

	/**
	 * Put all you migration methods here
	 *
	 * @package WP2FA\Utils
	 * @since 1.6
	 */
	class Migration extends Abstract_Migration {

		/**
		 * The name of the option from which we should extract version
		 * Note: version is expected in version format - 1.0.0; 1; 1.0; 1.0.0.0
		 * Note: only numbers will be processed
		 *
		 * @var string
		 *
		 * @since 1.6.0
		 */
		protected static $version_option_name = WP_2FA_PREFIX . 'plugin_version';

		/**
		 * The constant name where the plugin version is stored
		 * Note: version is expected in version format - 1.0.0; 1; 1.0; 1.0.0.0
		 * Note: only numbers will be processed
		 *
		 * @var string
		 *
		 * @since 1.6.0
		 */
		protected static $const_name_of_plugin_version = 'WP_2FA_VERSION';

		/**
		 * The name of the plugin settings
		 *
		 * @var string
		 */
		private static $plugin_settings_name = WP_2FA_SETTINGS_NAME;

		/**
		 * The name of the plugin policy settings
		 *
		 * @var string
		 */
		private static $plugin_policy_name = WP_2FA_POLICY_SETTINGS_NAME;

		/**
		 * The name of the plugin white label settings
		 *
		 * @var string
		 */
		private static $plugin_white_label_name = WP_2FA_WHITE_LABEL_SETTINGS_NAME;

		/**
		 * The name of the plugin email settings
		 *
		 * @var string
		 */
		private static $plugin_email_settings_name = WP_2FA_EMAIL_SETTINGS_NAME;

		/**
		 * Migration for version upto 1.6.0
		 *
		 * @return void
		 * @since 1.6.0
		 */
		protected static function migrate_up_to_160() {
			$settings = self::get_settings( self::$plugin_settings_name );
			if ( ! is_array( $settings ) ) {
				return;
			}

			$needs_update = false;

			$settings_to_convert = array( 'enforced_roles', 'enforced_users', 'excluded_users', 'excluded_roles' );
			foreach ( $settings_to_convert as $setting_name ) {
				if ( array_key_exists( $setting_name, $settings ) && ! is_array( $settings[ $setting_name ] ) ) {
					$settings[ $setting_name ] = array_filter(
						explode( ',', $settings[ $setting_name ] )
					);
					$needs_update              = true;
				}
			}

			if ( ! isset( $settings['backup_codes_enabled'] ) ) {
				$settings['backup_codes_enabled'] = 'yes';
				$needs_update                     = true;
			}

			if ( $needs_update ) {
				// Update settings.
				self::set_settings( self::$plugin_settings_name, $settings );
			}
		}

		/**
		 * Migration for version upto 1.6.2
		 *
		 * @return void
		 * @since 1.6.2
		 */
		protected static function migrate_up_to_162() {
			$settings = self::get_settings( self::$plugin_settings_name );
			if ( ! is_array( $settings ) ) {
				return;
			}

			$needs_update = false;

			$settings_to_convert = array( 'excluded_sites' );
			foreach ( $settings_to_convert as $setting_name ) {
				if ( array_key_exists( $setting_name, $settings ) && ! is_array( $settings[ $setting_name ] ) ) {
					$original_settings_split   = array_filter(
						explode( ',', $settings[ $setting_name ] )
					);
					$settings[ $setting_name ] = array();
					foreach ( $original_settings_split as $value ) {
						$settings[ $setting_name ][] = mb_substr( $value, mb_strrpos( $value, ':' ) + 1 );
					}
					$needs_update = true;
				}
			}

			self::migrate_up_to_160();

			if ( $needs_update ) {
				// Update settings.
				self::set_settings( self::$plugin_settings_name, $settings );
			}
		}

		/**
		 * Migration for version upto 1.5.0
		 *
		 * @return void
		 */
		protected static function migrate_up_to_150() {
			$settings = self::get_settings( self::$plugin_settings_name );

			if ( is_array( $settings ) && array_key_exists( 'enforcment-policy', $settings ) ) {
				// Correct setting name.
				$settings['enforcement-policy'] = $settings['enforcment-policy'];
				// Remove old setting.
				unset( $settings['enforcment-policy'] );
				// Update settings.
				self::set_settings( self::$plugin_settings_name, $settings );
			}
		}

		/**
		 * Migration for version upto 1.7.0
		 *
		 * @return void
		 */
		protected static function migrate_up_to_170() {
			$settings = self::get_settings( self::$plugin_settings_name );

			if ( is_array( $settings ) && array_key_exists( 'notify_users', $settings ) ) {
				// Remove old setting.
				unset( $settings['notify_users'] );
				// Update settings.
				self::set_settings( self::$plugin_settings_name, $settings );
			}

			$email_settings  = self::get_settings( self::$plugin_email_settings_name );
			$items_to_remove = array( 'send_enforced_email', 'enforced_email_subject', 'enforced_email_body' );

			if ( is_array( $email_settings ) && User_Utils::in_array_all( $items_to_remove, $email_settings ) ) {
				foreach ( $items_to_remove as $item ) {
					if ( isset( $email_settings[ $item ] ) ) {
						unset( $email_settings[ $item ] );
					}
				}
				// Update settings.
				self::set_settings( self::$plugin_email_settings_name, $email_settings );
			}
		}

		/**
		 * Migration for version upto 2.0.0
		 * Separates the current settings into 3 different types of settings:
		 *  - Policy
		 *  - General
		 *  - White label
		 *
		 * @return void
		 */
		protected static function migrate_up_to_200() {
			$settings = self::get_settings( self::$plugin_settings_name );

			if ( is_array( $settings ) ) {

				$new_settings_array = array_flip(
					array(
						'enable_grace_cron',
						'limit_access',
						'delete_data_upon_uninstall',
						'enable_destroy_session',
					)
				);

				$new_white_label_array = array_flip(
					array(
						'default-text-code-page',
					)
				);

				$settings_array = array_intersect_key(
					$settings,
					$new_settings_array
				);

				$settings = array_diff_key( $settings, $new_settings_array );

				self::set_settings( self::$plugin_settings_name, $settings_array );

				$white_label_settings = array_intersect_key(
					$settings,
					$new_white_label_array
				);

				$settings = array_diff_key( $settings, $new_white_label_array );

				self::set_settings( self::$plugin_white_label_name, $white_label_settings );

				self::set_settings( self::$plugin_policy_name, $settings );
			}
		}

		/**
		 * Migration for version upto 2.2.0
		 *
		 * @return void
		 */
		protected static function migrate_up_to_220() {
			global $wpdb;

			$new_prefix = 'wp_2fa_trusted_device_';
			$old_prefix = 'wp2fa_trusted_device_';

			delete_transient( 'wp_2fa_config_file_hash' );

			$wpdb->query(
				$wpdb->prepare(
					"
				 UPDATE $wpdb->usermeta
				 SET meta_key = REPLACE( meta_key, %s, %s )
				 WHERE meta_key LIKE %s
				 ",
					array(
						$old_prefix,
						$new_prefix,
						$old_prefix . '%',
					)
				)
			);
		}

		/**
		 * Migration for version upto 2.3.0
		 *
		 * @return void
		 */
		protected static function migrate_up_to_230() {

			$version = self::get_settings( self::$version_option_name );

			if ( $version && version_compare( $version, '2.2.1', '<=' ) ) {
				$settings = self::get_settings( self::$plugin_white_label_name );

				if ( isset( $settings['enable_wizard_styling'] ) ) {
					$settings['enable_wizard_styling'] = false;
				} else {
					$settings                          = array();
					$settings['enable_wizard_styling'] = false;
				}

				self::set_settings( self::$plugin_white_label_name, $settings );
			}
		}

		/**
		 * Migration for version upto 2.4.0
		 *
		 * @return void
		 */
		protected static function migrate_up_to_240() {

			\delete_transient( 'wp_2fa_config_file_hash' );

			if ( \wp_next_scheduled( 'wp_2fa_check_grace_period_status' ) ) {
				\wp_clear_scheduled_hook( 'wp_2fa_check_grace_period_status' );
			}
		}

		/**
		 * Migration for version upto 2.6.2
		 *
		 * @return void
		 */
		protected static function migrate_up_to_262() {

			self::migrate_up_to_240();
		}

		/**
		 * Migration for version upto 2.6.3
		 *
		 * @return void
		 */
		protected static function migrate_up_to_263() {

			self::migrate_up_to_240();
		}

		/**
		 * Migration for version upto 2.8.0
		 *
		 * @return void
		 *
		 * @since 2.8.0
		 */
		protected static function migrate_up_to_280() {

			self::migrate_up_to_240();
		}

		/**
		 * Returns the plugin settings by a given setting type
		 *
		 * @param mixed $setting_name - The setting which needs to be extracted.
		 *
		 * @return mixed
		 */
		private static function get_settings( $setting_name ) {
			return Settings_Utils::get_option( $setting_name );
		}

		/**
		 * Updates the plugin settings
		 *
		 * @param mixed $setting_name - The setting which needs to be updated.
		 * @param mixed $settings - The settings values.
		 *
		 * @return void
		 */
		private static function set_settings( $setting_name, $settings ) {
			Settings_Utils::update_option( $setting_name, $settings );
		}
	}
}
includes/classes/Utils/class-white-label.php000064400000037672150755130600015141 0ustar00<?php
/**
 * Responsible for white labeling functionality.
 *
 * @package    wp2fa
 * @subpackage white-label
 *
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 *
 * @see       https://wordpress.org/plugins/wp-2fa/
 */

declare(strict_types=1);

namespace WP2FA\Utils;

if ( ! class_exists( '\WP2FA\Utils\White_Label' ) ) {
	/**
	 * Utility class for white labeling.
	 *
	 * @package WP2FA\Utils
	 *
	 * @since 2.8.0
	 */
	class White_Label {

		/**
		 * Local static cache for plugins settings.
		 *
		 * @var array
		 *
		 * @since 2.8.0
		 */
		private static $plugin_settings = array();

		/**
		 * Inits the plugin related classes and settings
		 *
		 * @return void
		 *
		 * @since 2.8.0
		 */
		public static function init() {

			self::$plugin_settings[ WP_2FA_WHITE_LABEL_SETTINGS_NAME ] = Settings_Utils::get_option( WP_2FA_WHITE_LABEL_SETTINGS_NAME, array() );
		}

		/**
		 * Util function to grab white label settings or apply defaults if no settings are saved into the db.
		 *
		 * @param  string  $setting_name Settings to grab value of.
		 * @param boolean $get_default_on_empty return default setting value if current one is empty.
		 *
		 * @return string|array               Settings value or default value.
		 *
		 * @since 2.8.0
		 */
		public static function get_setting( $setting_name = '', $get_default_on_empty = false ) {
			$default_settings = self::get_default_settings();

			$white_label_setting = self::$plugin_settings[ WP_2FA_WHITE_LABEL_SETTINGS_NAME ];

			// If we have no setting name, return them all.
			if ( empty( $setting_name ) ) {
				return $white_label_setting;
			}

			// First lets check if any options have been saved.
			if ( empty( $white_label_setting ) || ! isset( $white_label_setting ) ) {
				$apply_defaults = true;
			}

			if ( $apply_defaults ) {
				return isset( $default_settings[ $setting_name ] ) ? $default_settings[ $setting_name ] : '';
			} elseif ( ! isset( $white_label_setting[ $setting_name ] ) ) {
				if ( true === $get_default_on_empty ) {
					if ( isset( $default_settings[ $setting_name ] ) ) {
						return $default_settings[ $setting_name ];
					}
				}

				return '';
			} else {
				return $white_label_setting[ $setting_name ];
			}
		}

		/**
		 * Array with all the plugin default settings.
		 *
		 * @return array
		 *
		 * @since 2.8.0
		 */
		public static function get_default_settings() {
			$default_settings = array(
				'default-text-code-page'                   => '<p>' . __( 'Please enter the two-factor authentication (2FA) verification code below to login. Depending on your 2FA setup, you can get the code from the 2FA app or it was sent to you by email.', 'wp-2fa' ) . '</p><p><strong>' . __( 'Note: if you are supposed to receive an email but did not receive any, please click the Resend Code button to request another code.', 'wp-2fa' ) . '</strong></p>',
				'default-text-pw-reset-code-page'          => '<p>' . __( 'You have been sent a one-time code via email. Please enter the code below and then click Get New Password to proceed with the password reset.', 'wp-2fa' ) . '</p><br><p><strong>' . __( 'Note: If you have not received the code please click the button Resend Code. If you still do not get the code after pressing the button, please contact the website\'s administrator.', 'wp-2fa' ) . '</strong></p>',
				'default-2fa-required-notice'              => '<p>' . __( 'This website\'s administrator requires you to enable two-factor authentication (2FA) {grace_period_remaining}.', 'wp-2fa' ) . '</p><br><p>' . __( 'Failing to configure 2FA within this time period will result in a locked account. For more information, please contact your website administrator.', 'wp-2fa' ) . '</p>',
				'default-2fa-resetup-required-notice'      => '<p>' . __( 'This website\'s administrator requires you to enable two-factor authentication (2FA) {grace_period_remaining}.', 'wp-2fa' ) . '</p><br><p>' . __( 'Failing to configure 2FA within this time period will result in a locked account. For more information, please contact your website administrator.', 'wp-2fa' ) . '</p>',
				'custom-text-authy-code-page-intro'        => __( 'If you are using the Authy app approve the OneTouch request to log in.', 'wp-2fa' ),
				'custom-text-authy-code-page-awaiting'     => __( 'Waiting for approval from application...', 'wp-2fa' ),
				'custom-text-authy-code-page'              => __( 'Manually enter the code from the mobile app.', 'wp-2fa' ),
				'custom-text-twilio-code-page'             => __( 'Enter the 2FA code you have received over SMS.', 'wp-2fa' ),
				'custom-text-clickatell-code-page'         => __( 'Enter the 2FA code you have received over SMS.', 'wp-2fa' ),
				'custom-text-yubico-code-page'             => __( 'Please insert the YubiKey in a USB port and touch / click the button on the YubiKey to generate the OTP required to log in.', 'wp-2fa' ),
				'custom-text-app-code-page'                => '<p>' . __( 'Please enter the two-factor authentication (2FA) verification code below to login. Depending on your 2FA setup, you can get the code from the 2FA app or it was sent to you by email.', 'wp-2fa' ) . '</p><p><strong>' . __( 'Note: if you are supposed to receive an email but did not receive any, please click the Resend Code button to request another code.', 'wp-2fa' ) . '</strong></p>',
				'custom-text-email-code-page'              => '<p>' . __( 'Please enter the two-factor authentication (2FA) verification code below to login. Depending on your 2FA setup, you can get the code from the 2FA app or it was sent to you by email.', 'wp-2fa' ) . '</p><p><strong>' . __( 'Note: if you are supposed to receive an email but did not receive any, please click the Resend Code button to request another code.', 'wp-2fa' ) . '</strong></p>',

				'default-backup-code-page'                 => __( 'Enter a backup verification code.', 'wp-2fa' ),
				'method_invalid_setting'                   => 'login_block',
				'enable_wizard_styling'                    => 'enable_wizard_styling',
				'show_help_text'                           => 'show_help_text',
				'enable_wizard_logo'                       => '',
				'enable_welcome'                           => '',
				'welcome'                                  => '',
				'method_selection'                         => '<h3>' . __( 'Choose the 2FA method', 'wp-2fa' ) . '</h3>' . esc_html__(
					'There are {available_methods_count} methods available to choose from for 2FA:',
					'wp-2fa'
				),
				'method_selection_single'                  => '<h3>' . __( 'Choose the 2FA method', 'wp-2fa' ) . '</h3><p>' . __( 'Only the below 2FA method is allowed on this website:', 'wp-2fa' ) . '</p>',
				'method_help_authy_intro'                  => '<h3>' . __( 'Setting up Push notifications', 'wp-2fa' ) . '</h3><p>' . __( 'To enable push notifications enter the country and cellphone number in order to use it with this account.', 'wp-2fa' ) . '</p>',
				'method_help_twilio_intro'                 => '<h3>' . __( 'Setting up 2FA over SMS', 'wp-2fa' ) . '</h3><p>' . __( 'When you use 2FA over SMS to log in to this website you will receive your one-time code via an SMS on your cellphone. Therefore please enter the cellphone number of where you would like to receive the SMS below.', 'wp-2fa' ) . '</p>',
				'method_help_clickatell_intro'             => '<h3>' . __( 'Setting up 2FA over SMS', 'wp-2fa' ) . '</h3><p>' . __( 'When you use 2FA over SMS to log in to this website you will receive your one-time code via an SMS on your cellphone. Therefore please enter the cellphone number of where you would like to receive the SMS below.', 'wp-2fa' ) . '</p>',
				'method_help_oob_intro'                    => '<h3>' . __( 'Setting up Link over email 2FA', 'wp-2fa' ) . '</h3><p>' . __( 'Please select the email address to where the out-of-band link should be sent:', 'wp-2fa' ) . '</p>',
				'method_help_yubico_intro'                 => '<h3>' . __( 'Setting up 2FA with YubiKey', 'wp-2fa' ) . '</h3><p>' . __( '1 - Insert your YubiKey into the computer\'s / mobile\'s USB port', 'wp-2fa' ) . '</p><p>' . __( '2 - Touch / press the button on your YubiKey to generate the OTP code, which is automatically populated below', 'wp-2fa' ) . '</p>',
				'method_verification_oob_pre'              => '<h3>' . __( 'Almost there…', 'wp-2fa' ) . '</h3><p>' . __( 'Please type in the one-time code sent to your email address to finalize the setup. Once the code is confirmed and 2FA is set up, you only have to verify a login by clicking on a link sent to you via email.', 'wp-2fa' ) . '</p>',
				'method_verification_authy_pre'            => '<h3>' . __( 'Almost there…', 'wp-2fa' ) . '</h3><p>' . __( 'Please type in the code from your Authy application with name {authy_name}', 'wp-2fa' ) . '</p>',
				'method_verification_twilio_pre'           => '<h3>' . __( 'Almost there…', 'wp-2fa' ) . '</h3><p>' . __( 'Please type in the one-time code sent via SMS to your phone to confirm your phone number.', 'wp-2fa' ) . '</p>',
				'method_verification_clickatell_pre'       => '<h3>' . __( 'Almost there…', 'wp-2fa' ) . '</h3><p>' . __( 'Please type in the one-time code sent via SMS to your phone to confirm your phone number.', 'wp-2fa' ) . '</p>',
				'method_verification_yubico_pre'           => '<h3>' . __( 'Almost there…', 'wp-2fa' ) . '</h3><p>' . __( 'Touch the YubiKey again to generate the OTP code to confirm the setup. Once the code is populated below, it should be automatically saved and verified. If that does not happen by any reason, once the secret key was pasted, click "Validate & save" button below to manually save and complete the configuration.', 'wp-2fa' ) . '</p>',
				'backup_codes_intro_multi'                 => '<h3>' . __( 'Your login just got more secure', 'wp-2fa' ) . '</h3><p>' . __( 'It is recommended to configure a backup 2FA method in case you do not have access to the primary 2FA method to generate a code to log in. You can configure any of the below. You can always configure any or both from your user profile page later.', 'wp-2fa' ) . '</p>',
				'backup_codes_intro'                       => '<h3>' . __( 'Your login just got more secure', 'wp-2fa' ) . '</h3><p>' . __( 'Congratulations! You have enabled two-factor authentication for your user. You’ve just helped towards making this website more secure!', 'wp-2fa' ) . '</p>',
				'backup_codes_intro_continue'              => '<h3>' . __( 'Your login just got more secure', 'wp-2fa' ) . '</h3><p>' . __( 'Congratulations! You have enabled two-factor authentication for your user. You’ve just helped towards making this website more secure!', 'wp-2fa' ) . '</p><p>' . __( 'You should now generate the list of backup method. Although this is optional, it is highly recommended to have a secondary 2FA method. This can be used as a backup should the primary 2FA method fail. This can happen if, for example, you forget your smartphone, the smartphone runs out of battery, or there are email deliverability problems.', 'wp-2fa' ) . '</p>',
				'backup_codes_generate_intro'              => '<h3>' . __( 'Generate list of backup codes', 'wp-2fa' ) . '</h3><p>' . __( 'It is recommended to generate and print some backup codes in case you lose access to your primary 2FA method.', 'wp-2fa' ) . '</p>',
				'backup_codes_generated'                   => '<h3>' . __( 'Backup codes generated', 'wp-2fa' ) . '</h3><p>' . __( 'Here are your backup codes:', 'wp-2fa' ) . '</p>',
				'no_further_action'                        => '<h3>' . __( 'Congratulations! You are all set.', 'wp-2fa' ),
				'2fa_required_intro'                       => '<h3>' . __( 'You are required to configure 2FA.', 'wp-2fa' ) . '</h3><p>' . __( 'In order to keep this site - and your details secure, this website’s administrator requires you to enable 2FA authentication to continue.', 'wp-2fa' ) . '</p><p>' . __( 'Two factor authentication ensures only you have access to your account by creating an added layer of security when logging in -', 'wp-2fa' ) . ' <a href="https://melapress.com/wordpress-2fa/?&utm_source=plugin&utm_medium=link&utm_campaign=wp2fa" target="_blank" rel="noopener">' . __( 'Learn more', 'wp-2fa' ) . '</a></p>',
				'authy_reconfigure_intro'                  => '<h3>' . __( '{reconfigure_or_configure_capitalized} push notification method', 'wp-2fa' ) . '</h3><p>' . __( 'Click the below button to {reconfigure_or_configure} the push notifications configuration.', 'wp-2fa' ) . '</p>',
				'authy_reconfigure_intro_unavailable'      => '<h3>' . __( '{reconfigure_or_configure_capitalized} push notification method', 'wp-2fa' ) . '</h3><p>' . __( 'The 2FA service you want to use is currently unavailable. Please try again later or restart the wizard to choose another method.', 'wp-2fa' ) . '</p>',
				'twilio_reconfigure_intro'                 => '<h3>' . __( '{reconfigure_or_configure_capitalized} SMS method (Twilio)', 'wp-2fa' ) . '</h3><p>' . __( 'Click the below button to {reconfigure_or_configure} the mobile phone number where the one-time code should be sent.', 'wp-2fa' ) . '</p>',
				'clickatell_reconfigure_intro'             => '<h3>' . __( '{reconfigure_or_configure_capitalized} SMS method (Clickatell)', 'wp-2fa' ) . '</h3><p>' . __( 'Please select the phone where code should be send:', 'wp-2fa' ) . '</p>',
				'yubico_reconfigure_intro'                 => '<h3>' . __( '{reconfigure_or_configure_capitalized} 2FA over YubiKey', 'wp-2fa' ) . '</h3><p>' . __( 'Click the below button to {reconfigure_or_configure} the YubiKey associated with your user.', 'wp-2fa' ) . '</p>',
				'twilio_reconfigure_intro_unavailable'     => '<h3>' . __( '{reconfigure_or_configure_capitalized} SMS method', 'wp-2fa' ) . '</h3><p>' . __( 'The 2FA over SMS service you want to use is currently unavailable. Please try again later or restart the wizard to choose another method.', 'wp-2fa' ) . '</p>',
				'clickatell_reconfigure_intro_unavailable' => '<h3>' . __( '{reconfigure_or_configure_capitalized} SMS method', 'wp-2fa' ) . '</h3><p>' . __( 'The 2FA over SMS service you want to use is currently unavailable. Please try again later or restart the wizard to choose another method.', 'wp-2fa' ) . '</p>',
				'yubico_reconfigure_intro_unavailable'     => '<h3>' . __( ' {reconfigure_or_configure_capitalized} 2FA over YubiKey', 'wp-2fa' ) . '</h3><p>' . __( 'The Yubico service you want to use is currently unavailable. Please try again later or restart the wizard to choose another method.', 'wp-2fa' ) . '</p>',
				'oob_reconfigure_intro'                    => '<h3>' . __( '{reconfigure_or_configure_capitalized} link over email method', 'wp-2fa' ) . '</h3><p>' . __( 'Click the below button to {reconfigure_or_configure} the email address where the link should be sent.', 'wp-2fa' ) . '</p>',
				'custom_css'                               => '',
				'login_custom_css'                         => '',
				'logo-code-page'                           => '',
				'disable_login_css'                        => '',
				'login-to-view-area'                       => '<p>' . __( 'You must be logged in to view this page. {login_url}', 'wp-2fa' ) . '</p>',
				'backup_email_intro'                       => '<h3>' . __( 'Your login just got more secure', 'wp-2fa' ) . '</h3><p>' . __( 'Well done on configuring 2FA, your login has just got more secure. To make sure you never get locked out you are required to confirm your email address and use email as an alternative and backup 2FA method in case your primary method is unavailable. Please confirm your email address below', 'wp-2fa' ) . '</p>',
				'user-profile-form-preamble-title'         => __( 'Two-factor authentication settings', 'wp-2fa' ),
				'user-profile-form-preamble-desc'          => __( 'Add two-factor authentication to strengthen the security of your user account.', 'wp-2fa' ),
				'use_custom_2fa_message'                   => 'use-defaults',
			);

			/**
			 * Gives the ability to filter the default settings array of the plugin
			 *
			 * @param array $settings - The array with all the default settings.
			 *
			 * @since 2.0.0
			 */
			$default_settings = \apply_filters( WP_2FA_PREFIX . 'white_label_default_settings', $default_settings );

			return $default_settings;
		}
	}
}
includes/classes/Utils/class-abstract-migration.php000064400000015620150755130600016523 0ustar00<?php
/**
 * Abstract migration class.
 *
 * @package    wp2fa
 * @subpackage utils
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 * @link       https://wordpress.org/plugins/wp-2fa/
 */

declare(strict_types=1);

namespace WP2FA\Utils;

use WP2FA\Utils\Settings_Utils;

defined( 'ABSPATH' ) || exit; // Exit if accessed directly.

/**
 * Abstract AMigration class
 */
if ( ! class_exists( '\WP2FA\Utils\Abstract_Migration' ) ) {

	/**
	 * Utility class to ease the migration process.
	 *
	 * Every migration must go in its own method
	 * The naming convention is migrateUpTo_XXX where XXX is the number of the version,
	 * format is numbers only.
	 * Example: migration for version upto 1.4 must be in migrateUpTo_14 method
	 *
	 * The numbers in the names of the methods must have exact numbers count as in the selected
	 * version in use, even if there are silent numbers for some of the major versions as 1, 2, 3 etc. (the .0.0 is skipped / silent)
	 * Example:
	 *  - if X.X.X is selected for version number, then for version 1.1 method must have "...migrateUpTo_110..." in its name
	 *  - if X.X is selected for version number, then for version 1, method must have "...migrateUpTo_10..." in its name
	 *
	 * Note: you can add prefix to the migration method, if that is necessary, but "migrateUpTo_" is a must -
	 * the name must contain that @see getAllMigrationMethodsAsNumbers of that class.
	 * For version extraction the number following the last '_' will be used
	 * TODO: the mandatory part of the method name can be a setting in the class, but is that a good idea?
	 *
	 * Note: order of the methods is not preserved - version numbers will be used for ordering
	 *
	 * @package WP2FA\Utils
	 *
	 * @since 1.6
	 */
	class Abstract_Migration {

		/**
		 * Extracted version from the DB (WP option)
		 *
		 * @var string
		 *
		 * @since 1.6.0
		 */
		protected static $stored_version = '';

		/**
		 * The name of the option from which we should extract version
		 * Note: version is expected in version format - 1.0.0; 1; 1.0; 1.0.0.0
		 * Note: only numbers will be processed
		 *
		 * @var string
		 *
		 * @since 1.6.0
		 */
		protected static $version_option_name = '';

		/**
		 * The constant name where the plugin version is stored
		 * Note: version is expected in version format - 1.0.0; 1; 1.0; 1.0.0.0
		 * Note: only numbers will be processed
		 *
		 * @var string
		 *
		 * @since 2.2.0
		 */
		protected static $const_name_of_plugin_version = '';

		/**
		 * Used for adding proper pads for the missing numbers
		 * Version number format used here depends on selection for how many numbers will be used for representing version
		 *
		 * For X.X     use 2;
		 * For X.X.X   use 3;
		 * For X.X.X.X use 4;
		 *
		 * etc.
		 *
		 * Example: if selected version format is X.X.X that means that 3 digits are used for versioning.
		 * And current version is stored as 2 (no suffix 0.0) that means that it will be normalized as 200.
		 *
		 * @var integer
		 *
		 * @since 1.6.0
		 */
		protected static $pad_length = 3;

		/**
		 * Collects all the migration methods which needs to be executed in order and executes them
		 *
		 * @return void
		 *
		 * @since 1.6.0
		 */
		public static function migrate() {

			if ( version_compare( static::get_stored_version(), \constant( static::$const_name_of_plugin_version ), '<' ) ) {

				$stored_version_as_number  = static::normalize_version( static::get_stored_version() );
				$target_version_as_number  = static::normalize_version( \constant( static::$const_name_of_plugin_version ) );
				$method_as_version_numbers = static::get_all_migration_methods_as_numbers();

				$migrate_methods = array_filter(
					$method_as_version_numbers,
					function ( $method, $key ) use ( &$stored_version_as_number, &$target_version_as_number ) {
						if ( $target_version_as_number > $stored_version_as_number ) {
							return ( in_array( $key, range( $stored_version_as_number, $target_version_as_number ), true ) );
						}

						return false;
					},
					ARRAY_FILTER_USE_BOTH
				);

				if ( ! empty( $migrate_methods ) ) {
					\ksort( $migrate_methods );
					foreach ( $migrate_methods as $method ) {
						static::{$method}();
					}
				}

				// If we have a previous version, its an update so flag notice.
				if ( ! empty( Settings_Utils::get_option( static::$version_option_name ) ) ) {
					Settings_Utils::update_option( 'wp_2fa_update_redirection_needed', true );
				}

				self::store_updated_version();
			}

			/**
			 * Downgrading the plugin? Set the version number.
			 * Leave the rest as is.
			 *
			 * @return void
			 *
			 * @since 2.2.0
			 */
			if ( version_compare( static::get_stored_version(), \constant( static::$const_name_of_plugin_version ), '>' ) ) {
				self::store_updated_version();
			}
		}

		/**
		 * Extracts currently stored version from the DB
		 *
		 * @return string
		 *
		 * @since 1.6.0
		 */
		private static function get_stored_version() {

			if ( '' === trim( (string) static::$stored_version ) ) {
				static::$stored_version = (string) Settings_Utils::get_option( static::$version_option_name, '0.0.0' );
			}

			return static::$stored_version;
		}

		/**
		 * Stores the version to which we migrated
		 *
		 * @return void
		 *
		 * @since 1.6.0
		 */
		private static function store_updated_version() {
			Settings_Utils::update_option( static::$version_option_name, \constant( static::$const_name_of_plugin_version ) );
		}

		/**
		 * Normalized the version numbers to numbers
		 *
		 * Version format is expected to be as follows:
		 * X.X.X
		 *
		 * All non numeric values will be removed from the version string
		 *
		 * Note: version is expected in version format - 1.0.0; 1; 1.0; 1.0.0.0
		 * Note: only numbers will be processed
		 *
		 * @param string $version - The version string we have to use.
		 *
		 * @return string
		 *
		 * @since 1.6.0
		 */
		private static function normalize_version( string $version ) {
			$version_as_number = (int) filter_var( $version, FILTER_SANITIZE_NUMBER_INT );

			if ( self::$pad_length > strlen( (string) $version_as_number ) ) {
				$version_as_number = str_pad( (string) $version_as_number, static::$pad_length, '0', STR_PAD_RIGHT );
			}

			return $version_as_number;
		}

		/**
		 * Collects all the migration methods from the class and stores them in the array
		 * Array is in following format:
		 * key - number of the version
		 * value - name of the method
		 *
		 * @return array
		 *
		 * @since 1.6.0
		 */
		private static function get_all_migration_methods_as_numbers() {
			$class_methods = \get_class_methods( get_called_class() );

			$method_as_version_numbers = array();
			foreach ( $class_methods as $method ) {
				if ( false !== \strpos( $method, 'migrate_up_to_' ) ) {
					$ver                               = \substr( $method, \strrpos( $method, '_' ) + 1, \strlen( $method ) );
					$method_as_version_numbers[ $ver ] = $method;
				}
			}

			return $method_as_version_numbers;
		}
	}
}
includes/classes/Utils/class-request-utils.php000064400000002777150755130600015570 0ustar00<?php
/**
 * Responsible for the requests.
 *
 * @package    wp2fa
 * @subpackage utils
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 * @link       https://wordpress.org/plugins/wp-2fa/
 * @since      2.0.0
 */

declare(strict_types=1);

namespace WP2FA\Utils;

if ( ! class_exists( '\WP2FA\Utils\Request_Utils' ) ) {

	/**
	 * Utility class to extract info from current request.
	 *
	 * @package WP2FA\Utils
	 * @since 2.0.0
	 */
	class Request_Utils {

		/**
		 * Extracts the IP address for the currently browsing user
		 *
		 * @return string
		 *
		 * @since 2.0.0
		 */
		public static function get_ip() {
			foreach (
			array(
				'HTTP_CLIENT_IP',
				'HTTP_X_FORWARDED_FOR',
				'HTTP_X_FORWARDED',
				'HTTP_X_CLUSTER_CLIENT_IP',
				'HTTP_FORWARDED_FOR',
				'HTTP_FORWARDED',
				'REMOTE_ADDR',
			) as $key
			) {
				if ( array_key_exists( $key, $_SERVER ) === true ) {
					foreach ( array_map( 'trim', explode( ',', $_SERVER[ $key ] ) ) as $ip ) { // phpcs:ignore
						if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) !== false ) {
							return $ip;
						}
					}
				}
			}
		}

		/**
		 * Extracts the User agent for the currently request.
		 *
		 * @return string
		 *
		 * @since 2.0.0
		 */
		public static function get_user_agent() {
			if ( ! array_key_exists( 'HTTP_USER_AGENT', $_SERVER ) ) {
				return '';
			}

			return trim( (string) $_SERVER['HTTP_USER_AGENT'] ); // phpcs:ignore
		}
	}
}
includes/classes/App/index.php000064400000000046150755130600012351 0ustar00<?php
/**
 * Nothing to see here.
 */
includes/classes/App/grace-period/class-grace-period.php000064400000014573150755130600017261 0ustar00<?php
/**
 * Main file of the grace period settings extension class.
 *
 * @package    wp2fa
 * @subpackage grace-period
 * @since      2.0.0
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 * @link       https://wordpress.org/plugins/wp-2fa/
 */

namespace WP2FA\App;

use WP2FA\Admin\Helpers\User_Helper;
use WP2FA\Admin\Controllers\Settings;
use WP2FA\Extensions\RoleSettings\Role_Settings_Controller;

defined( 'ABSPATH' ) || exit; // Exit if accessed directly.

/**
 * Grace period class
 */
if ( ! class_exists( '\WP2FA\App\Grace_Period' ) ) {

	/**
	 * Responsible for users which grace period has expired
	 *
	 * Gives the administrator the ability to select what action the plugin should take:
	 * - Lock the user
	 * - Force the user to set up their 2FA immediately
	 *
	 * @since 2.0.0
	 */
	class Grace_Period {

		/**
		 * Inits all the hooks
		 *
		 * @return void
		 *
		 * @since 2.0.0
		 */
		public static function init() {
			\add_filter( WP_2FA_PREFIX . 'after_grace_period', array( __CLASS__, 'grace_period_options' ), 10, 5 );
			\add_filter( WP_2FA_PREFIX . 'loop_settings', array( __CLASS__, 'add_setting_value' ) );
			\add_filter( WP_2FA_PREFIX . 'default_settings', array( __CLASS__, 'add_default_settings' ) );
			\add_filter( WP_2FA_PREFIX . 'should_account_be_locked_on_grace_period_expiration', array( __CLASS__, 'maybe_prevent_account_lock' ), 10, 2 );
		}

		/**
		 * Prevent account locking if allowed, depending on the plugin settings.
		 *
		 * @param boolean  $state - Current state of the checking.
		 * @param \WP_User $user - The User class.
		 *
		 * @return bool
		 *
		 * @since 2.0.0
		 */
		public static function maybe_prevent_account_lock( bool $state, \WP_User $user ) {
			if ( 'configure-right-away' === Settings::get_role_or_default_setting( 'grace-policy-after-expire-action', $user ) ) {
				User_Helper::set_user_enforced_instantly( true, $user );

				return false;
			}

			return $state;
		}

		/**
		 * Collects the options for the main plugin settings page and returns them
		 *
		 * @param string $content - HTML content.
		 * @param string $role - The name of the role.
		 * @param string $name_prefix - Name prefix for the input name, includes the role name if provided.
		 * @param string $data_role - Data attribute - used by the JS.
		 * @param string $role_id - The role name, used to identify the inputs.
		 *
		 * @return string
		 *
		 * @since 2.0.0
		 */
		public static function grace_period_options( string $content, string $role = '', string $name_prefix = '', string $data_role = '', string $role_id = '' ) {
			return $content . self::grace_options( $role, $name_prefix, $data_role, $role_id );
		}

		/**
		 * Adds global plugin setting options
		 *
		 * @param array $loop_settings - Array with current plugin settings.
		 *
		 * @return array
		 *
		 * @since 2.0.0
		 */
		public static function add_setting_value( array $loop_settings ) {
			$loop_settings[] = 'grace-policy-after-expire-action';

			return $loop_settings;
		}

		/**
		 * Checks the grace policy setting for the given user
		 *
		 * @param \WP_User $user - The user for which we have to check the settings.
		 *
		 * @return boolean
		 *
		 * @since 2.0.0
		 */
		public static function is_set_up_immediately_set( \WP_User $user ) {
			if ( 'configure-right-away' === Settings::get_role_or_default_setting( 'grace-policy-after-expire-action', $user ) ) {

				return true;
			}

			return false;
		}

		/**
		 * Adds the extension default settings to the main plugin settings
		 *
		 * @param array $default_settings - array with plugin default settings.
		 *
		 * @return array
		 *
		 * @since 2.0.0
		 */
		public static function add_default_settings( array $default_settings ) {
			$default_settings['grace-policy-after-expire-action'] = 'configure-right-away';

			return $default_settings;
		}

		/**
		 * Adds options to the settings page
		 *
		 * @param string $role - The name of the role.
		 * @param string $name_prefix - Name prefix for the input name, includes the role name if provided.
		 * @param string $data_role - Data attribute - used by the JS.
		 * @param string $role_id - The role name, used to identify the inputs.
		 *
		 * @return string
		 *
		 * @since 2.0.0
		 */
		private static function grace_options( string $role = '', string $name_prefix = '', string $data_role = '', string $role_id = '' ): string {
			ob_start();

			if ( class_exists( 'WP2FA\Extensions\RoleSettings\Role_Settings_Controller' ) ) {
				$expire_action = Role_Settings_Controller::get_setting( $role, 'grace-policy-after-expire-action', 'configure-right-away' );
			} else {
				$expire_action = Settings::get_role_or_default_setting( 'grace-policy-after-expire-action', null, null, 'configure-right-away' );
			}
			if ( false === $expire_action ) {
				$expire_action = 'configure-right-away';
			}
			?>
			<div class="sub-setting-indent">
				<p class="description" style="margin-top: 15px; margin-bottom: 8px;">
					<?php echo \esc_html__( 'What should the plugin do with users who do not configure 2FA within the grace period?', 'wp-2fa' ); ?>
				</p>
				<fieldset>
					<label for="configure-right-away<?php echo \esc_attr( $role_id ); ?>" style="margin-bottom: 10px; display: inline-block;">
						<input type="radio" name="<?php echo \esc_attr( $name_prefix ); ?>[grace-policy-after-expire-action]" 
						id="configure-right-away<?php echo \esc_attr( $role_id ); ?>" 
						<?php echo $data_role; // phpcs:ignore?> 
						value="configure-right-away" <?php checked( $expire_action, 'configure-right-away' ); ?> class="js-nested">
						<span><?php echo \esc_html__( 'Do not let them access the dashboard / user page once they log in until they configure 2FA', 'wp-2fa' ); ?></span>
					</label>

					<br>
					<div style="clear:both">
					<label for="manual-block<?php echo \esc_attr( $role_id ); ?>">
						<input type="radio" name="<?php echo \esc_attr( $name_prefix ); ?>[grace-policy-after-expire-action]" <?php checked( $expire_action, 'manual-block' ); ?> 
						id="manual-block<?php echo \esc_attr( $role_id ); ?>"
						<?php echo $data_role; // phpcs:ignore?> 
						value="manual-block" class="js-nested">
						<span><?php echo \esc_html__( 'Block the user (administrators have to manually unblock them)', 'wp-2fa' ); ?></span>
					</label>
					</div>
				</fieldset>
			</div>
			<?php
			$html_content = ob_get_contents();
			ob_end_clean();

			return $html_content;
		}
	}
}
includes/classes/App/grace-period/index.php000064400000000046150755130600014712 0ustar00<?php
/**
 * Nothing to see here.
 */
includes/classes/Shortcodes/class-shortcodes.php000064400000020335150755130600016122 0ustar00<?php
/**
 * Responsible for rendering the short codes.
 *
 * @package    wp2fa
 * @subpackage short-codes
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 * @link       https://wordpress.org/plugins/wp-2fa/
 */

declare(strict_types=1);

namespace WP2FA\Shortcodes;

use WP2FA\Core;
use WP2FA\WP2FA;
use WP2FA\Admin\User_Notices;
use WP2FA\Admin\User_Profile;
use WP2FA\Admin\Helpers\WP_Helper;
use WP2FA\Admin\Views\Re_Login_2FA;
use WP2FA\Admin\Helpers\User_Helper;
use WP2FA\Admin\Controllers\Settings;

if ( ! class_exists( '\WP2FA\Shortcodes\Shortcodes' ) ) {
	/**
	 * Class for rendering shortcodes.
	 */
	class Shortcodes {

		/**
		 * Constructor.
		 */
		public static function init() {
			\add_shortcode( 'wp-2fa-setup-form', array( __CLASS__, 'user_setup_2fa_form' ) );
			\add_shortcode( 'wp-2fa-setup-notice', array( __CLASS__, 'user_setup_2fa_notice' ) );
			\add_action( 'wp_enqueue_scripts', array( __CLASS__, 'register_2fa_shortcode_scripts' ) );
		}

		/**
		 * Register scripts and styles.
		 */
		public static function register_2fa_shortcode_scripts() {
			// Add our front end stuff, which we only want to load when the shortcode is present.
			\wp_register_script( 'wp_2fa_frontend_scripts', Core\script_url( 'wp-2fa', 'admin' ), array( 'jquery', 'wp_2fa_micro_modals' ), WP_2FA_VERSION, true );
			\wp_register_script( 'wp_2fa_micro_modals', Core\script_url( 'micromodal', 'admin' ), array(), WP_2FA_VERSION, true );
			\wp_register_style( 'wp_2fa_styles', Core\style_url( 'styles', 'frontend' ), array(), WP_2FA_VERSION );

			$data_array = array(
				'ajaxURL'        => \admin_url( 'admin-ajax.php' ),
				'roles'          => WP_Helper::get_roles_wp(),
				'nonce'          => \wp_create_nonce( 'wp-2fa-settings-nonce' ),
				'codesPreamble'  => \esc_html__( 'These are the 2FA backup codes for the user', 'wp-2fa' ),
				'readyText'      => \esc_html__( 'I\'m ready', 'wp-2fa' ),
				'codeReSentText' => \esc_html__( 'New code sent', 'wp-2fa' ),
				'allDoneHeading' => \esc_html__( 'All done.', 'wp-2fa' ),
				'allDoneText'    => \esc_html__( 'Your login just got more secure.', 'wp-2fa' ),
				'closeWizard'    => \esc_html__( 'Close Wizard', 'wp-2fa' ),
				'invalidEmail'   => \esc_html__( 'Please use a valid email address', 'wp-2fa' ),
			);
			\wp_localize_script( 'wp_2fa_frontend_scripts', 'wp2faData', $data_array );

			$role = User_Helper::get_user_role();

			$re_login = Settings::get_role_or_default_setting( Re_Login_2FA::RE_LOGIN_SETTINGS_NAME, 'current', $role );

			$data_array                  = array(
				'ajaxURL'         => \admin_url( 'admin-ajax.php' ),
				'nonce'           => \wp_create_nonce( 'wp2fa-verify-wizard-page' ),
				'codesPreamble'   => \esc_html__( 'These are the 2FA backup codes for the user', 'wp-2fa' ),
				'readyText'       => \esc_html__( 'I\'m ready', 'wp-2fa' ),
				'codeReSentText'  => \esc_html__( 'New code sent', 'wp-2fa' ),
				'invalidEmail'    => \esc_html__( 'Please use a valid email address', 'wp-2fa' ),
				'backupCodesSent' => \esc_html__( 'Backup codes sent', 'wp-2fa' ),
				'reLogin'         => $re_login,
				'reLoginEnabled'  => Re_Login_2FA::ENABLED_SETTING_VALUE,
			);
			$redirect_page               = Settings::get_role_or_default_setting( 'redirect-user-custom-page-global', 'current', $role );
			$data_array['redirectToUrl'] = ( '' !== trim( (string) $redirect_page ) ) ? \trailingslashit( get_site_url() ) . $redirect_page : '';
			// Check and override if custom redirect page is selected and custom redirect is set.
			if (
			'yes' === Settings::get_role_or_default_setting( 'create-custom-user-page', 'current', $role ) ||
			'yes' === Settings::get_role_or_default_setting( 'create-custom-user-page' ) ) {
				if (
				'' !== trim( (string) Settings::get_role_or_default_setting( 'redirect-user-custom-page', 'current', $role ) ) ||
				'' !== trim( (string) Settings::get_role_or_default_setting( 'redirect-user-custom-page' ) ) ) {
					if ( 'yes' === Settings::get_role_or_default_setting( 'create-custom-user-page', 'current', $role ) ) {
						$data_array['redirectToUrl'] = \trailingslashit( get_site_url() ) . Settings::get_role_or_default_setting( 'redirect-user-custom-page', 'current', $role );
					} else {
						$data_array['redirectToUrl'] = \trailingslashit( get_site_url() ) . Settings::get_role_or_default_setting( 'redirect-user-custom-page' );
					}
				}
			}

			// Check for shortcode parameter - if one is present use it to redirect the user - highest priority.
			if ( isset( $redirect_after ) && ! empty( $redirect_after ) ) {
				$data_array['redirectToUrl'] = \trailingslashit( \get_site_url() ) . \urlencode( $redirect_after );
			} elseif ( isset( $_GET['return'] ) && ! empty( $_GET['return'] ) ) {
				$data_array['redirectToUrl'] = \trailingslashit( \get_site_url() ) . strip_tags( \wp_unslash( $_GET['return'] ) ); // phpcs:ignore
			}

			\wp_localize_script( 'wp_2fa_frontend_scripts', 'wp2faWizardData', $data_array );
		}

		/**
		 * Output setup form.
		 *
		 * @param array $atts - Array with the attributes passed to shortcode.
		 *
		 * @return string
		 */
		public static function user_setup_2fa_form( $atts ) {

			/** Shortcode redirect_after is supported, with which the user can override all other settings */
			extract( // phpcs:ignore
				\shortcode_atts(
					array(
						'show_preamble'       => 'true',
						'redirect_after'      => '',
						'do_not_show_enabled' => 'false',
					),
					$atts
				)
			);

			/**
			 * Fires when the FE shortcode scripts are registered.
			 *
			 * @param bool $shortcodes - True if called from the short codes method.
			 *
			 * @since 2.2.0
			 */
			\do_action( WP_2FA_PREFIX . 'shortcode_scripts', true );

			if ( is_user_logged_in() ) {
				\wp_enqueue_script( 'wp_2fa_frontend_scripts' );
				\wp_enqueue_style( 'wp_2fa_styles' );

				ob_start();
				echo '<form id="your-profile" class="wp-2fa-configuration-form">';
				User_Profile::inline_2fa_profile_form( 'output_shortcode', $show_preamble, array( 'do_not_show_enabled' => $do_not_show_enabled ) );
				echo '</form>';
				$content = ob_get_contents();
				ob_end_clean();

				return $content;
			} elseif ( ! is_admin() && ! is_user_logged_in() ) {
				ob_start();
				$new_page_id = WP2FA::get_wp2fa_setting( 'custom-user-page-id' );
				$redirect_to = ! empty( $new_page_id ) ? \get_permalink( $new_page_id ) : \get_home_url();
				$link_markup = '<a href="' . \esc_url( \wp_login_url( $redirect_to ) ) . '">' . \esc_html__( 'Login here.', 'wp-2fa' ) . '</a>';
				$message     = '<p id="wp_2fa_login_to_view_text">' . str_replace( '{login_url}', $link_markup, WP2FA::get_wp2fa_white_label_setting( 'login-to-view-area', true ) ) . '</p>';
				echo \wp_kses_post( $message );
				$content = ob_get_contents();
				ob_end_clean();
				return $content;
			}
		}

		/**
		 * Output setup nag.
		 *
		 * @param array $atts - Array with the attributes passed to shortcode.
		 *
		 * @return string
		 */
		public static function user_setup_2fa_notice( $atts ) {
			extract( // phpcs:ignore
				\shortcode_atts(
					array(
						'configure_2fa_url' => '',
					),
					$atts
				)
			);

			// TODO: is that really necessary?
			User_Notices::init();

			if ( ! is_admin() && is_user_logged_in() ) {
				\wp_enqueue_script( 'wp_2fa_micro_modals' );
				\wp_enqueue_script( 'wp_2fa_frontend_scripts' );
				\wp_enqueue_style( 'wp_2fa_styles' );

				$data_array = array(
					'ajaxURL'        => \admin_url( 'admin-ajax.php' ),
					'roles'          => WP_Helper::get_roles_wp(),
					'nonce'          => \wp_create_nonce( 'wp-2fa-settings-nonce' ),
					'codesPreamble'  => \esc_html__( 'These are the 2FA backup codes for the user', 'wp-2fa' ),
					'readyText'      => \esc_html__( 'I\'m ready', 'wp-2fa' ),
					'codeReSentText' => \esc_html__( 'New code sent', 'wp-2fa' ),
					'allDoneHeading' => \esc_html__( 'All done.', 'wp-2fa' ),
					'allDoneText'    => \esc_html__( 'Your login just got more secure.', 'wp-2fa' ),
					'closeWizard'    => \esc_html__( 'Close Wizard', 'wp-2fa' ),
				);
				\wp_localize_script( 'wp_2fa_frontend_scripts', 'wp2faData', $data_array );

				ob_start();
				User_Notices::user_setup_2fa_nag( 'output_shortcode', $configure_2fa_url );
				$content = ob_get_contents();
				ob_end_clean();

				return $content;
			}

			return '';
		}
	}
}
includes/classes/Shortcodes/index.php000064400000000046150755130600013746 0ustar00<?php
/**
 * Nothing to see here.
 */
includes/classes/Admin/Fly-Out/assets/css/flyout.css000064400000010253150755130600016467 0ustar00 #mlp-flyout {
    position: fixed;
    z-index: 100049;
    transition: all 0.3s ease-in-out;
    right: 40px;
    bottom: 40px;
    opacity: 1;
  }
  
  #mlp-overlay {
    background: #000;
    opacity: 0.4;
    filter: alpha(opacity=40);
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    display: none;
    z-index: 100049;
  }
  
  #mlp-flyout a:focus {
    outline: none;
    box-shadow: none;
  }
  
  #mlp-flyout #mlp-elmnts-button {
    display: block;
  }
  
  #mlp-flyout #mlp-elmnts-image-wrapper {
    border: 3px solid #000000;
    border-radius: 50%;
    padding: 0;
    display: block;
    overflow: hidden;
    background: #000000;
    box-shadow: 0 3px 20px rgba(0, 0, 0, 0.2);
  }
  
  #mlp-flyout #mlp-elmnts-button img {
    width: 55px;
    height: 55px;
    display: block;
    overflow: hidden;
    padding: 2px;
    box-sizing: border-box;
    position: relative;
    top: 4px;
  }
  
  #mlp-flyout #mlp-elmnts-button:hover #mlp-elmnts-image-wrapper {
    box-shadow: 0 3px 30px rgba(0, 0, 0, 0.25);
  }
  
  #mlp-flyout:not(.opened) #mlp-elmnts-button:hover .mlp-elmnts-label {
    opacity: 1;
    margin-right: 0;
  }
  
  #mlp-flyout .mlp-elmnts-label {
    position: absolute;
    display: block;
    top: 50%;
    right: calc(100% + 25px);
    transform: translateY(-50%) scale(1);
    -moz-transform: translateY(-50%);
    -webkit-transform: translateY(-50%);
    color: #fff;
    background: #444 0 0 no-repeat padding-box;
    font-size: 14px;
    white-space: nowrap;
    padding: 5px 10px;
    height: auto !important;
    line-height: initial;
    transition: all 0.2s ease-out;
    border-radius: 3px;
    -moz-border-radius: 3px;
    -webkit-border-radius: 3px;
    opacity: 0;
    margin-right: -50px;
  }
  
  #mlp-flyout .mlp-elmnts-icon {
    width: 40px;
    height: 40px;
    vertical-align: middle;
    line-height: 60px;
    text-align: center;
  }
  
  #mlp-flyout .mlp-elmnts-icon img {
    max-width: 70%;
    filter: brightness(100);
  }
  
  #mlp-flyout .mlp-elmnts-label.visible {
    opacity: 1;
  }
  
  #mlp-flyout .mlp-elmnts-menu-item {
    position: absolute;
    left: 10px;
    width: 40px;
    height: 40px;
    opacity: 0;
    visibility: hidden;
    transform: scale(0);
    border-radius: 50%;
    box-shadow: 0 3px 20px rgba(0, 0, 0, 0.2);
    background: #1c3aa9;
    text-align: center;
    vertical-align: middle;
    text-decoration: none;
    transition-timing-function: ease-in-out;
  }
  
  #mlp-flyout .mlp-elmnts-menu-item.accent {
    background: #ca4a1f;
  }
  
  #mlp-flyout.opened .mlp-elmnts-menu-item {
    opacity: 1;
    visibility: visible;
    transform: scale(1);
  }
  
  #mlp-flyout .mlp-elmnts-menu-item:hover {
    box-shadow: 0 3px 30px rgba(0, 0, 0, 0.25);
  }
  
  #mlp-flyout .mlp-elmnts-menu-item:hover .mlp-elmnts-label {
    right: calc(100% + 55px);
  }
  
  #mlp-flyout .mlp-elmnts-menu-item .mlp-elmnts-label {
    right: calc(100% + 70px);
  }
  
  #mlp-flyout .mlp-elmnts-menu-item .dashicons {
    line-height: 41px;
    font-size: 23px;
    color: #fff;
    padding: 0px 3px 0px 0;
  }
  
  .mlp-elmnts-menu-item-1 {
    bottom: 75px;
    transition: transform 0.2s 30ms, background-color 0.2s;
  }
  
  .mlp-elmnts-menu-item-2 {
    bottom: 130px;
    transition: transform 0.2s 70ms, background-color 0.2s;
  }
  
  .mlp-elmnts-menu-item-3 {
    bottom: 185px;
    transition: transform 0.2s 110ms, background-color 0.2s;
  }
  
  .mlp-elmnts-menu-item-4 {
    bottom: 240px;
    transition: transform 0.2s 150ms, background-color 0.2s;
  }
  
  .mlp-elmnts-menu-item-5 {
    bottom: 295px;
    transition: transform 0.2s 190ms, background-color 0.2s;
  }
  
  .mlp-elmnts-menu-item-6 {
    bottom: 350px;
    transition: transform 0.2s 230ms, background-color 0.2s;
  }
  
  .mlp-elmnts-menu-item-7 {
    bottom: 405px;
    transition: transform 0.2s 270ms, background-color 0.2s;
  }
  
  .mlp-elmnts-menu-item-8 {
    bottom: 460px;
    transition: transform 0.2s 310ms, background-color 0.2s;
  }
  
  .mlp-elmnts-menu-item-9 {
    bottom: 515px;
    transition: transform 0.2s 350ms, background-color 0.2s;
  }
  
  .mlp-elmnts-menu-item-10 {
    bottom: 570px;
    transition: transform 0.2s 390ms, background-color 0.2s;
  }
  includes/classes/Admin/Fly-Out/assets/js/flyout.js000064400000000742150755130600016141 0ustar00
jQuery(document).ready(function () {
  jQuery('#mlp-elmnts-button').on('click', function (e) {
    e.preventDefault();

    jQuery('#mlp-flyout').toggleClass('opened');
    jQuery('#mlp-overlay').toggle();

    return false;
  }); // open/close menu

  jQuery('#mlp-overlay').on('click', function (e) {
    e.preventDefault();

    jQuery(this).hide();
    jQuery('#mlp-flyout').removeClass('opened');

    return false;
  }); // click on overlay - hide menu
}); // jQuery ready
  includes/classes/Admin/Fly-Out/class-flyout.php000064400000016653150755130600015511 0ustar00<?php
/**
 * Responsible for fly-out menu shown on some of the plugin pages.
 *
 * @package    wp2fa
 * @subpackage flyout
 *
 * @since 2.8.0
 *
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 *
 * @see       https://wordpress.org/plugins/wp-2fa/
 */

declare(strict_types=1);

namespace WP2FA\Admin\FlyOut;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

if ( ! class_exists( '\WP2FA\Admin\FlyOut\FlyOut' ) ) {
	/**
	 * Generates fly-out menu on the plugin admin screen.
	 *
	 * @since 2.8.0
	 */
	class FlyOut {

		private const ENQUEUE_NAME          = 'mlp_flyout';
		private const CONFIG_TRANSIENT_NAME = \WP_2FA_PREFIX . 'flyout_config';

		/**
		 * Array with the configuration of the fly-out menu
		 *
		 * @var array
		 *
		 * @since 2.8.0
		 */
		private static $config = array();

		/**
		 * Class cache for the current screen (if admin is on it)
		 *
		 * @var bool
		 *
		 * @since 2.8.0
		 */
		private static $screen = null;

		/**
		 * Inits the class and its hooks
		 *
		 * @return void
		 *
		 * @since 2.8.0
		 */
		public static function init() {
			if ( ! \is_admin() ) {
				return;
			} else {
				self::load_config();
				if ( ! empty( self::$config ) ) {
					\add_action( 'admin_enqueue_scripts', array( __CLASS__, 'admin_enqueue_scripts' ) );
					\add_action( 'admin_head', array( __CLASS__, 'admin_head' ) );
					\add_action( 'admin_footer', array( __CLASS__, 'admin_footer' ) );
				}
			}
		}

		/**
		 * Loads the external config for processing
		 *
		 * @return void
		 *
		 * @since 2.8.0
		 */
		public static function load_config() {
			$config = array();

			$config = self::read_remote_config();

			if ( false === $config ) {
				return;
			}

			$defaults = array(
				'plugin_screen'     => '',
				'icon_border'       => '#0000ff',
				'icon_right'        => '40px',
				'icon_bottom'       => '40px',
				'icon_image'        => '',
				'icon_padding'      => '2px',
				'icon_size'         => '55px',
				'menu_accent_color' => '#ca4a1f',
				'custom_css'        => '',
				'menu_items'        => array(),
			);

			$config = array_merge( $defaults, (array) $config );
			if ( ! is_array( $config['plugin_screen'] ) ) {
				$config['plugin_screen'] = array( $config['plugin_screen'] );
			}

			self::$config = $config;
		}

		/**
		 * Checks the current screen and returns true if it is the plugin one
		 *
		 * @return boolean
		 *
		 * @since 2.8.0
		 */
		public static function is_plugin_screen(): bool {

			if ( \is_null( self::$screen ) ) {

				$screen       = \get_current_screen();
				self::$screen = false;

				if ( in_array( $screen->id, self::$config['plugin_screen'] ) ) {
					self::$screen = true;
				}
			}

			return self::$screen;
		}

		/**
		 * Loads the fly-out css and JS files
		 *
		 * @return void
		 *
		 * @since 2.8.0
		 */
		public static function admin_enqueue_scripts() {
			if ( false === self::is_plugin_screen() ) {
				return;
			}

			\wp_enqueue_style(
				self::ENQUEUE_NAME,
				WP_2FA_URL . '/includes/classes/Admin/Fly-Out/assets/css/flyout.css',
				array(),
				WP_2FA_VERSION
			);
			\wp_enqueue_script(
				self::ENQUEUE_NAME,
				WP_2FA_URL . '/includes/classes/Admin/Fly-Out/assets/js/flyout.js',
				array(),
				WP_2FA_VERSION,
				true
			);
		}

		/**
		 * Writes additional custom code in the header of the page
		 *
		 * @return void
		 *
		 * @since 2.8.0
		 */
		public static function admin_head() {
			if ( false === self::is_plugin_screen() ) {
				return;
			}

			$out  = '<style type="text/css">';
			$out .= '#mlp-flyout {
				right: ' . \sanitize_text_field( self::$config['icon_right'] ) . ';
				bottom: ' . \sanitize_text_field( self::$config['icon_bottom'] ) . ';
			}';
			$out .= '#mlp-flyout #mlp-elmnts-image-wrapper {
				border: ' . \sanitize_text_field( self::$config['icon_border'] ) . ';
			}';
			$out .= '#mlp-flyout #mlp-elmnts-button img {
				padding: ' . \sanitize_text_field( self::$config['icon_padding'] ) . ';
				width: ' . \sanitize_text_field( self::$config['icon_size'] ) . ';
				height: ' . \sanitize_text_field( self::$config['icon_size'] ) . ';
			}';
			$out .= '#mlp-flyout .mlp-elmnts-menu-item.accent {
				background: ' . \sanitize_text_field( self::$config['menu_accent_color'] ) . ';
			}';
			$out .= \sanitize_text_field( self::$config['custom_css'] );
			$out .= '</style>';

			echo $out; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		}

		/**
		 * Writes additional custom code in the footer of the page
		 *
		 * @return void
		 *
		 * @since 2.8.0
		 */
		public static function admin_footer() {
			if ( false === self::is_plugin_screen() ) {
				return;
			}

			$out               = '';
			$icons_url         = WP_2FA_URL . 'assets/images/';
			$default_link_item = array(
				'class'  => '',
				'href'   => '#',
				'target' => '_blank',
				'label'  => '',
				'icon'   => '',
				'data'   => '',
			);

			$out .= '<div id="mlp-overlay"></div>';

			$out .= '<div id="mlp-flyout">';

			$out .= '<a href="#" id="mlp-elmnts-button">';
			$out .= '<span class="mlp-elmnts-label">Open Quick Links</span>';
			$out .= '<span id="mlp-elmnts-image-wrapper">';
			$out .= '<img src="' . esc_url( $icons_url . self::$config['icon_image'] ) . '" alt="Open Quick Links" title="Open Quick Links">';
			$out .= '</span>';
			$out .= '</a>';

			$out .= '<div id="mlp-elmnts-menu">';
			$i    = 0;
			foreach ( array_reverse( self::$config['menu_items'] ) as $item ) {
				++$i;
				$item = array_merge( $default_link_item, $item );

				if ( ! empty( $item['icon'] ) && substr( $item['icon'], 0, 9 ) != 'dashicons' ) {
					$item['class'] .= ' mlp-elmnts-custom-icon';
					$item['class']  = trim( $item['class'] );
				}

				$out .= '<a ' . $item['data'] . ' href="' . esc_url( $item['href'] ) . '" class="mlp-elmnts-menu-item mlp-elmnts-menu-item-' . $i . ' ' . esc_attr( $item['class'] ) . '" target="_blank">';
				$out .= '<span class="mlp-elmnts-label visible">' . esc_html( $item['label'] ) . '</span>';
				if ( substr( $item['icon'], 0, 9 ) == 'dashicons' ) {
					$out .= '<span class="dashicons ' . sanitize_text_field( $item['icon'] ) . '"></span>';
				} elseif ( ! empty( $item['icon'] ) ) {
					$out .= '<span class="mlp-elmnts-icon"><img src="' . esc_url( $icons_url . $item['icon'] ) . '"></span>';
				}
				$out .= '</a>';
			} // foreach
			$out .= '</div>'; // #mlp-elmnts-menu

			$out .= '</div>'; // #mlp-flyout

			echo $out; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		}

		/**
		 * Reads the config file remotely and sets 2 days transient for caching. If for some reason cant read the remote - false is returned
		 *
		 * @return bool|array
		 *
		 * @since 2.8.0
		 */
		public static function read_remote_config() {
			$config = \get_transient( self::CONFIG_TRANSIENT_NAME );

			if ( false === $config || empty( $config ) ) {

				$api_response = \wp_remote_request( 'https://melapress.com/downloads/plugins-files/wp-2fa-flyout-config.php', array() );

				$response_code = \wp_remote_retrieve_response_code( $api_response );

				if ( \is_wp_error( $api_response ) || 200 !== (int) $response_code ) {

					return false;
				} else {
					$config = \wp_remote_retrieve_body( $api_response );

					\set_transient( self::CONFIG_TRANSIENT_NAME, $config, \DAY_IN_SECONDS * 3 );

					return \json_decode( $config, true );
				}
			}

			$config = json_decode( $config, true );

			if ( json_last_error() !== JSON_ERROR_NONE ) {
				$config = false;
			}

			return $config;
		}
	}
}
includes/classes/Admin/Fly-Out/index.php000064400000000046150755130600014160 0ustar00<?php
/**
 * Nothing to see here.
 */
includes/classes/Admin/Controllers/class-settings.php000064400000035261150755130600017032 0ustar00<?php
/**
 * Responsible for the plugin settings iterations
 *
 * @package    wp2fa
 * @subpackage admin_controllers
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 * @link       https://wordpress.org/plugins/wp-2fa/
 * @since      2.2.0
 */

declare(strict_types=1);

namespace WP2FA\Admin\Controllers;

use WP2FA\WP2FA;
use WP2FA\Admin\Settings_Page;
use WP2FA\Methods\Backup_Codes;
use WP2FA\Admin\Helpers\WP_Helper;
use WP2FA\Admin\Helpers\User_Helper;
use WP2FA\Extensions\OutOfBand\Out_Of_Band;
use WP2FA\Admin\SettingsPages\Settings_Page_Policies;

defined( 'ABSPATH' ) || exit; // Exit if accessed directly.

if ( ! class_exists( '\WP2FA\Admin\Controllers\Settings' ) ) {
	/**
	 * WP2FA Settings controller
	 *
	 * @since 2.2.0
	 */
	class Settings {

		/**
		 * The link to the WP admin settings page
		 *
		 * @var string
		 *
		 * @since 2.2.0
		 */
		private static $settings_page_link = '';

		/**
		 * The name of the WP2FA WP admin setup page
		 *
		 * @var string
		 *
		 * @since 2.2.0
		 */
		private static $setup_page_name = 'wp-2fa-setup';

		/**
		 * The link to the WP admin setup page
		 *
		 * @var string
		 *
		 * @since 2.2.0
		 */
		private static $setup_page_link = '';

		/**
		 * The link to the custom settings page (if one is presented)
		 *
		 * @var string
		 *
		 * @since 2.2.0
		 */
		private static $custom_setup_page_link = null;

		/**
		 * Array with all the backup methods available.
		 *
		 * Array must contain the following:
		 * [backup_method_slug] - [
		 *          'wizard-step' - The name (HTML friendly as it will be used in the tags) of the plugin wizard step.
		 *          'button_name' - The button name shown in the wizard - language translated.
		 * ]
		 *
		 * @var array
		 *
		 * @since 2.0.0
		 */
		private static $backup_methods = null;

		/**
		 * All available providers for the plugin
		 * For the specific role @see get_all_providers_for_role()
		 *
		 * @var array
		 *
		 * @since 2.2.0
		 */
		private static $all_providers = array();

		/**
		 * All available providers for the plugin with their translated names.
		 *
		 * @var array
		 *
		 * @since 2.5.0
		 */
		private static $all_providers_names_translated = array();

		/**
		 * All the available providers by user roles
		 *
		 * @var array
		 *
		 * @since 2.2.0
		 */
		private static $all_providers_for_roles = array();

		/**
		 * Returns the link to the WP admin settings page, based on the current WP install
		 *
		 * @return string
		 *
		 * @since 2.2.0
		 */
		public static function get_settings_page_link() {
			if ( '' === self::$settings_page_link ) {
				self::$settings_page_link = add_query_arg( 'page', Settings_Page::TOP_MENU_SLUG, network_admin_url( 'admin.php' ) );
			}

			return self::$settings_page_link;
		}

		/**
		 * Returns the link to the WP admin settings page, based on the current WP install
		 *
		 * @return string
		 *
		 * @since 2.2.0
		 */
		public static function get_setup_page_link() {
			if ( '' === self::$setup_page_link ) {
				self::$setup_page_link = self::get_custom_page_link();

				if ( empty( self::$setup_page_link ) ) {
					if ( WP_Helper::is_multisite() ) {
						self::$setup_page_link = add_query_arg( 'show', self::$setup_page_name, get_admin_url( get_current_blog_id(), 'profile.php' ) );
					} else {
						self::$setup_page_link = add_query_arg( 'show', self::$setup_page_name, admin_url( 'profile.php' ) );
					}
				}
			}

			return apply_filters( WP_2FA_PREFIX . 'setup_page_link', self::$setup_page_link );
		}

		/**
		 * Extracts the custom settings page URL
		 *
		 * @param mixed $user - User for which to extract the setting, null, \WP_User or user id - @see get_role_or_default_setting method of this class.
		 *
		 * @return string
		 *
		 * @since 2.2.0
		 */
		public static function get_custom_page_link( $user = null ): string {
			if ( null === self::$custom_setup_page_link ) {
				self::$custom_setup_page_link = self::get_role_or_default_setting( 'custom-user-page-id', $user );

				if ( ! empty( self::$custom_setup_page_link ) ) {
					$custom_slug = '';
					if ( WP_Helper::is_multisite() ) {
						\switch_to_blog( get_main_site_id() );

						// $custom_slug                  = get_post_field( 'post_name', get_post( self::$custom_setup_page_link ) );
						$new_page_permalink = get_permalink( get_post( self::$custom_setup_page_link ) );
						self::$custom_setup_page_link = $new_page_permalink;//trailingslashit( get_site_url() ) . $custom_slug;

						\restore_current_blog();
					} else {
						//$custom_slug                  = get_post_field( 'post_name', get_post( self::$custom_setup_page_link ) );

						$new_page_permalink = get_permalink( get_post( self::$custom_setup_page_link ) );
						self::$custom_setup_page_link = $new_page_permalink;
					}
				} else {
					$custom_user_page_id = (int) self::get_custom_settings_page_id( '', $user );
					if ( ! empty( $custom_user_page_id ) ) {
						self::$custom_setup_page_link = \get_permalink( $custom_user_page_id );
					}
				}
			}

			return (string) \apply_filters( WP_2FA_PREFIX . 'custom_setup_page_link', self::$custom_setup_page_link, $user );
		}

		/**
		 * Check all the roles for given setting
		 *
		 * @param string $setting_name - The name of the setting to check for.
		 *
		 * @return boolean
		 *
		 * @since 2.0.0
		 */
		public static function check_setting_in_all_roles( string $setting_name ): bool {
			$roles = WP_Helper::get_roles();

			foreach ( $roles as $role ) {
				if ( ! empty( WP2FA::get_wp2fa_setting( $setting_name, false, false, $role ) ) ) {
					return true;
				}
			}

			return false;
		}

		/**
		 * Return setting specific for the given role or default setting (based on user)
		 *
		 * @param string  $setting_name - The name of the setting.
		 * @param mixed   $user - \WP_User or any string or null - if string the current user will be used, if null global plugin setting will be used.
		 * @param mixed   $role - The name of the role (or null).
		 * @param boolean $get_default_on_empty - Get default setting on empty setting value.
		 * @param boolean $get_default_value - Extracts default value.
		 *
		 * @return mixed
		 *
		 * @since 2.0.0
		 */
		public static function get_role_or_default_setting( string $setting_name, $user = null, $role = null, $get_default_on_empty = false, $get_default_value = false ) {
			if ( null === $role ) {
				/**
				 * No user specified - get the default settings
				 */
				if ( null === $user || \WP_2FA_PREFIX . 'no-user' === $user ) {
					return WP2FA::get_wp2fa_setting( $setting_name, $get_default_on_empty, $get_default_value );
				}

				/**
				 * There is an User - extract the role
				 */
				if ( $user instanceof \WP_User || is_int( $user ) ) {
					if ( null === $role ) {
						$role = User_Helper::get_user_role( $user );
					}
					return WP2FA::get_wp2fa_setting( $setting_name, $get_default_on_empty, $get_default_value, $role );
				}

				/**
				 * No logged in current user, ergo no roles - fall back to defaults
				 */
				if ( 0 === User_Helper::get_user_object()->ID ) {
					return WP2FA::get_wp2fa_setting( $setting_name, $get_default_on_empty, $get_default_value );
				}

				$role = User_Helper::get_user_role();
			}

			return WP2FA::get_wp2fa_setting( $setting_name, $get_default_on_empty, $get_default_value, $role );
		}

		/**
		 * Returns all the backup methods currently supported
		 *
		 * @return array
		 *
		 * @since 2.0.0
		 */
		public static function get_backup_methods(): array {

			if ( null === self::$backup_methods ) {

				/**
				 * Gives the ability to add additional backup methods
				 *
				 * @param array The array with all the backup methods currently supported.
				 *
				 * @since 2.0.0
				 */
				self::$backup_methods = apply_filters( WP_2FA_PREFIX . 'backup_methods_list', array() );
			}

			return self::$backup_methods;
		}

		/**
		 * Get backup methods enabled for user based on its role
		 *
		 * @param \WP_User $user - The WP user which we must check.
		 *
		 * @return array
		 *
		 * @since 2.0.0
		 */
		public static function get_enabled_backup_methods_for_user_role( \WP_User $user ): array {
			$backup_methods = self::get_backup_methods();

			/**
			 * Extensions could change the enabled backup methods array.
			 *
			 * @param array - Backup methods array.
			 * @param \WP_User - The user to check for.
			 *
			 * @since 2.0.0
			 */
			return apply_filters( WP_2FA_PREFIX . 'backup_methods_enabled', $backup_methods, $user );
		}

		/**
		 * Returns all enabled providers for specific role
		 *
		 * @param string $role - The name of the role to check for.
		 *
		 * @return array
		 *
		 * @throws \Exception - if the role is wrong - throws an exception.
		 *
		 * @since 2.2.0
		 */
		public static function get_enabled_providers_for_role( string $role ) {

			if ( WP_Helper::is_role_exists( $role ) ) {
				self::get_all_roles_providers();

				return self::$all_providers_for_roles[ $role ];
			} elseif ( '' === $role ) {
				return array();
			} else {
				throw new \Exception( 'Role provided does not exists - "' . $role . '"' ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
			}
		}

		/**
		 * Checks if given provider is enabled for the given role.
		 *
		 * @param string $role - The name of the role.
		 * @param string $provider - The name of the provider.
		 *
		 * @return boolean
		 *
		 * @throws \Exception - If the provider is not registered in the plugin.
		 *
		 * @since 2.2.0
		 */
		public static function is_provider_enabled_for_role( string $role, string $provider ): bool {
			self::get_providers();

			if ( in_array( $provider, self::$all_providers, true ) ) {
				self::get_enabled_providers_for_role( $role );
				if ( isset( self::$all_providers_for_roles[ $role ][ $provider ] ) ) {
					return true;
				}

				return false;
			}

			throw new \Exception( 'Non existing provider ' . $provider ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
		}

		/**
		 * Returns all providers by roles.
		 * If given role does not have specified settings set - falls back to the default settings.
		 *
		 * @return array
		 *
		 * @since 2.2.0
		 */
		public static function get_all_roles_providers() {
			if ( empty( self::$all_providers_for_roles ) ) {
				$roles     = WP_Helper::get_roles();
				$providers = self::get_providers();

				foreach ( $roles as $role ) {
					self::$all_providers_for_roles[ $role ] = array();
					foreach ( $providers as $provider ) {
						if ( Backup_Codes::METHOD_NAME === $provider ) {
							self::$all_providers_for_roles[ $role ][ $provider ] = WP2FA::get_wp2fa_setting( $provider . '_enabled', false, false, $role );
						} elseif ( 'backup_email' === $provider ) {
							self::$all_providers_for_roles[ $role ][ $provider ] = WP2FA::get_wp2fa_setting( 'enable-email-backup', false, false, $role );
						} elseif ( class_exists( '\WP2FA\Extensions\OutOfBand\Out_Of_Band', false ) && Out_Of_Band::METHOD_NAME === $provider ) {
							self::$all_providers_for_roles[ $role ][ $provider ] = WP2FA::get_wp2fa_setting( 'enable_' . $provider . '_email', false, false, $role );
						} else {
							self::$all_providers_for_roles[ $role ][ $provider ] = WP2FA::get_wp2fa_setting( 'enable_' . $provider, false, false, $role );
						}
					}
					self::$all_providers_for_roles[ $role ] = array_filter( self::$all_providers_for_roles[ $role ] );
				}
			}

			return self::$all_providers_for_roles;
		}

		/**
		 * Returns an array with all providers and their translated name. Key is the method slug and value is the translated method name.
		 *
		 * @return array
		 *
		 * @since 2.5.0
		 */
		public static function get_providers_translate_names(): array {
			if ( empty( self::$all_providers_names_translated ) ) {
				/**
				 * Filter the supplied providers.
				 *
				 * This lets third-parties either remove providers (such as Email), or
				 * add their own providers (such as text message or Clef).
				 *
				 * @param array $provider array if available options.
				 */
				self::$all_providers_names_translated = apply_filters( WP_2FA_PREFIX . 'providers_translated_names', self::$all_providers_names_translated );
			}

			return self::$all_providers_names_translated;
		}

		/**
		 * Grab list of all register providers in the plugin.
		 *
		 * @return array
		 *
		 * @since 2.2.0
		 */
		public static function get_providers() {
			if ( empty( self::$all_providers ) ) {
				/**
				 * Filter the supplied providers.
				 *
				 * This lets third-parties either remove providers (such as Email), or
				 * add their own providers (such as text message or Clef).
				 *
				 * @param array $provider array if available options.
				 */
				self::$all_providers = apply_filters( WP_2FA_PREFIX . 'providers', self::$all_providers );
			}

			return self::$all_providers;
		}

		/**
		 * Returns the page ID stored in the given role or user, based on the multisite and page URL only.
		 *
		 * @param string       $role - The role name if any. Default fallback if not role no user is provided.
		 * @param \WP_User|int $user - The user object or user id if any.
		 *
		 * @return int
		 *
		 * @since 2.5.0
		 */
		public static function get_custom_settings_page_id( $role = '', $user = '' ) {
			if ( ! empty( $role ) ) {
				$page_slug = self::get_role_or_default_setting( 'custom-user-page-url', '', $role );
			} elseif ( ! empty( $user ) ) {
				$page_slug = self::get_role_or_default_setting( 'custom-user-page-url', $user );
			} else {
				$page_slug = self::get_role_or_default_setting( 'custom-user-page-url', '', '' );
			}

			if ( ! empty( $role ) ) {
				$separate_page = self::get_role_or_default_setting( 'separate-multisite-page-url', '', $role );
			} elseif ( ! empty( $user ) ) {
				$separate_page = self::get_role_or_default_setting( 'separate-multisite-page-url', $user );
			} else {
				$separate_page = self::get_role_or_default_setting( 'separate-multisite-page-url', '', '' );
			}

			$new_page_id = '';

			// Lets check for multisite first and if that is the case - lets search for that page on the user's default blog.
			if ( WP_Helper::is_multisite() && false !== $separate_page ) {
				if ( ! empty( $user ) ) {
					$blog_id = User_Helper::get_user_default_blog( $user );
				} else {
					$blog_id = \get_current_blog_id();
				}

				if ( 0 === $blog_id ) {
					$new_page_id = '';
				} else {
					// Switch to the blog context.
					\switch_to_blog( $blog_id );

					$page_exists = Settings_Page_Policies::get_post_by_post_name( $page_slug, 'page' );
					// Restore global context.
					\restore_current_blog();

					if ( false !== $page_exists ) {
						$new_page_id = $page_exists->ID;
					}
				}
			} else {
				$page_exists = Settings_Page_Policies::get_post_by_post_name( $page_slug, 'page' );
				if ( false !== $page_exists ) {
					$new_page_id = $page_exists->ID;
				}
			}

			return $new_page_id;
		}
	}
}
includes/classes/Admin/Controllers/index.php000064400000000046150755130600015167 0ustar00<?php
/**
 * Nothing to see here.
 */
includes/classes/Admin/Controllers/class-methods.php000064400000006442150755130600016634 0ustar00<?php
/**
 * Responsible for the plugin methods
 *
 * @package    wp2fa
 * @subpackage admin_controllers
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 * @link       https://wordpress.org/plugins/wp-2fa/
 */

namespace WP2FA\Admin\Controllers;

use WP2FA\WP2FA;
use WP2FA\Admin\Controllers\Settings;
use WP2FA\Admin\Helpers\Methods_Helper;
use WP2FA\Extensions\OutOfBand\Out_Of_Band;

defined( 'ABSPATH' ) || exit; // Exit if accessed directly.

/**
 * Methods class
 */
if ( ! class_exists( '\WP2FA\Admin\Controllers\Methods' ) ) {

	/**
	 * All the methods related functionality must be extracted from this class. Responsible only for global methods data, not the user method related stuff.
	 *
	 * @since 2.2.0
	 */
	class Methods {

		/**
		 * Holds all the enabled methods in the plugin
		 *
		 * @var array
		 *
		 * @since 2.2.0
		 */
		private static $enabled_methods = null;

		/**
		 * Works our a list of available 2FA methods. It doesn't include the disabled ones.
		 *
		 * TODO: There is a high possibility that this method is duplication of the Settings::get_providers - check and make the changes as there must be only one way to extract that info
		 *
		 * @return string[]
		 * @since 2.0.0
		 */
		public static function get_available_2fa_methods(): array {
			$available_methods = array();

			/**
			 * Add an option for external providers to implement their own 2fa methods and set them as available.
			 *
			 * @param array $available_methods - The array with all the available methods.
			 *
			 * @since 2.0.0
			 */
			return \apply_filters( WP_2FA_PREFIX . 'available_2fa_methods', $available_methods );
		}

		/**
		 * Returns array with all the enabled methods in the plugin for the current role
		 *
		 * @param string $role - Role to extract data for.
		 *
		 * @return array
		 *
		 * @since 2.2.0
		 */
		public static function get_enabled_methods( $role = 'global' ): array {
			if ( null === self::$enabled_methods || ! isset( self::$enabled_methods[ $role ] ) ) {
				self::$enabled_methods[ $role ] = array();
				$providers                      = Settings::get_providers();

				foreach ( $providers as $provider ) {
					if ( Settings::is_provider_enabled_for_role( $role, $provider ) ) {
						$method = Methods_Helper::get_method_by_provider_name( $provider );
						if ( $method && \method_exists( $method, 'is_secondary' ) && $method::is_secondary() ) {
							continue;
						} elseif ( class_exists( '\WP2FA\Extensions\OutOfBand\Out_Of_Band', false ) && Out_Of_Band::METHOD_NAME === $provider ) {
							self::$enabled_methods[ $role ][ $provider ] = WP2FA::get_wp2fa_setting( 'enable_' . $provider . '_email', false, false, $role );
						} else {
							self::$enabled_methods[ $role ][ $provider ] = WP2FA::get_wp2fa_setting( 'enable_' . $provider, false, false, $role );
						}
					}
				}

				self::$enabled_methods[ $role ] = array_filter( self::$enabled_methods[ $role ] );
			}

			return self::$enabled_methods;
		}

		/**
		 * Returns text with the number of methods supported for the given role
		 *
		 * @since 2.2.0
		 *
		 * @return string
		 */
		public static function get_number_of_methods_text() {
			return esc_html__(
				'There are {available_methods_count} methods available to choose from for 2FA:',
				'wp-2fa'
			);
		}
	}
}
includes/classes/Admin/class-premium-features.php000064400000046301150755130600016153 0ustar00<?php
/**
 * Premium features rendering class.
 *
 * @package    wp2fa
 * @subpackage admin
 *
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 *
 * @see       https://wordpress.org/plugins/wp-2fa/
 * @since      2.0.0
 */

declare(strict_types=1);

namespace WP2FA\Admin;

/*
 * Premium_Features class for the premium features show
 *
 * @since 2.4.0
 */
if ( ! class_exists( '\WP2FA\Admin\Premium_Features' ) ) {
	/**
	 * Handles contact the features page and content.
	 */
	class Premium_Features {
		public const TOP_MENU_SLUG = 'wp-2fa-premium-features';

		/**
		 * Create admin menu entry and settings page.
		 *
		 * @return void
		 *
		 * @since 2.8.0
		 */
		public static function add_extra_menu_item() {
			\add_submenu_page(
				Settings_Page::TOP_MENU_SLUG,
				\esc_html__( 'Premium Features', 'wp-2fa' ),
				\esc_html__( 'Premium Features ➤', 'wp-2fa' ),
				'manage_options',
				self::TOP_MENU_SLUG,
				array( __CLASS__, 'render' ),
				100
			);
		}

		/**
		 * Adds an upgrade banner to settings pages.
		 *
		 * @return void
		 *
		 * @since 2.8.0
		 */
		public static function add_settings_banner() {
			$banner  = '<div id="wp-2fa-side-banner">';
			$banner .= '<img src="' . \esc_url( WP_2FA_URL . 'dist/images/wizard-logo.png' ) . '">';
			$banner .= '<p>' . \esc_html__( 'Upgrade to Premium & benefit:', 'wp-2fa' ) . '</p>';
			$banner .= '<ul><li><span class="dashicons dashicons-yes-alt"></span>' . \esc_html__( 'Login with 2FA via SMS, push notification or with a simple mouse click', 'wp-2fa' ) . '</li>';
			$banner .= '<li><span class="dashicons dashicons-yes-alt"></span>' . \esc_html__( 'Add & manage trusted devices ("Remember this device" option)', 'wp-2fa' ) . '</li>';
			$banner .= '<li><span class="dashicons dashicons-yes-alt"></span> ' . \esc_html__( 'Add alternative 2FA methods ensuring no user is ever locked out', 'wp-2fa' ) . '</li>';
			$banner .= '<li><span class="dashicons dashicons-yes-alt"></span> ' . \esc_html__( 'One-click 2FA integration with WooCommerce', 'wp-2fa' ) . '</li>';
			$banner .= '<li><span class="dashicons dashicons-yes-alt"></span> ' . \esc_html__( 'Completely whitelabel the 2FA user experience including the 2FA code page, email & wizards text', 'wp-2fa' ) . '</li>';
			$banner .= '<li><span class="dashicons dashicons-yes-alt"></span> ' . \esc_html__( 'Configure different 2FA policies for different user roles', 'wp-2fa' ) . '</li>';
			$banner .= '<li><span class="dashicons dashicons-yes-alt"></span> ' . \esc_html__( 'Many other features', 'wp-2fa' ) . '</li>';
			$banner .= '<li><span class="dashicons dashicons-yes-alt"></span> ' . \esc_html__( 'No Ads!', 'wp-2fa' ) . '</li></ul>';
			$banner .= '<a href="https://melapress.com/wordpress-2fa/pricing/?utm_source=plugin&utm_medium=link&utm_campaign=wp2fa" class="button button-primary" target="_blank">' . \esc_html__( 'Upgrade to Premium', 'wp-2fa' ) . '</a>';
			$banner .= '</div>';

			echo $banner; // phpcs:ignore
		}

		/**
		 * Handles rendering the content.
		 *
		 * @return void
		 *
		 * @since 2.8.0
		 */
		public static function render() {
			?>
			<style>
				.features-wrap {
					background: #fff;
					padding: 25px 30px;
					margin-top: 25px;
				}

				.features-wrap h2 {
					font-size: 28px;
					margin-bottom: 30px;
				}

				.features-wrap p {
					font-size: 16px;
						line-height: 28px;
				}

				.feature-list {
					margin-bottom: 20px;
				}

				.feature-list li {
					margin-bottom: 10px;
					font-size: 15px;
				}

				.feature-list li .dashicons {
					color: #3E6BFF;
				}

				.premium-cta {
					margin: 25px 0 15px;
					text-align: center;
				}

				.premium-cta a:not(.inverse), .table-link {
					background-color: #3E6BFF;
					color: #fff;
					padding: 15px 26px;
					border-radius: 30px;
					font-size: 16px;
					white-space: nowrap;
					text-decoration: none;
					font-weight: 700;
					display: inline-block;
					margin-right: 15px;
					border: 2px solid #3E6BFF;
				}

				.premium-cta a:hover, .table-link:hover, .premium-cta a.inverse, .table-link.inverse {
					color: #3E6BFF;
					background-color: #fff;
				}

				.premium-cta a.inverse {
					font-weight: 700;
					text-decoration: none;
					font-size: 16px;
				}

				.content-block {
					margin-bottom: 26px;
					border-bottom: 1px solid #eee;
					padding-bottom: 15px;
				}

				.feature-table tr td {
					text-align: center;
					min-width: 200px
				}
				.feature-table tr td:first-of-type {
					text-align: left;
					font-weight: 500;
				}
				.feature-table td p {
					margin-top: 0;
				}
				.row-head span {
					font-size: 17px;
					font-weight: 700;
				}
				.feature-table .dashicons {
					color: #3E6BFF;
				}
				.feature-table .dashicons-no {
					color: red;
				}
				.table-link {
					font-size: 14px;
					padding: 9px;
					width: 193px;
					margin-top: 10px;
				}
				.pull-up {
					position: relative;
					top: -23px;
				}

				.wp2fa-logo {
					max-width: 130px;
				}

				.logo-wrap {
					float: left;
					margin-right: 30px;
				}
			</style>

			<div class="wrap help-wrap features-wrap wp-2fa-settings-wrapper">
				<div class="page-head">
					<h2><?php \esc_html_e( 'Upgrade to Premium and benefit more!', 'wp-2fa' ); ?></h2>
				</div>
				<div class="content-block">
					<div class="logo-wrap">
						<img class="wp2fa-logo" src="<?php echo WP_2FA_URL; // phpcs:ignore?>dist/images/wp-2fa-color_opt.png" alt="">
					</div>
					<div>
						<p><?php \esc_html_e( 'WP 2FA is your trusted gatekeeper, keeping your website, users, customers, team members, and anyone who accesses your website, including you, secure and better protected than ever before.', 'wp-2fa' ); ?></p>
						<p><?php \esc_html_e( 'Upgrade to WP 2FA Premium to add more secure authentication options and automate more, encouraging all your website users to utilize 2FA to its fullest extent and give your users more flexibility by allowing them to work from anywhere without compromising on security.', 'wp-2fa' ); ?></p>
					</div>
				</div>
				<div class="content-block">
					<p><strong><?php \esc_html_e( 'Upgrade to Premium and start benefiting from value-added features such as:', 'wp-2fa' ); ?></strong></p>
					<ul class="feature-list">
						<li><span class="dashicons dashicons-saved"></span> <?php \esc_html_e( 'More 2FA methods, including SMS, push notifications & one-click login', 'wp-2fa' ); ?></li>
						<li><span class="dashicons dashicons-saved"></span> <?php \esc_html_e( 'Trusted devices: Allow users to add trusted devices so they do not have to manually enter the 2FA code each time they log in', 'wp-2fa' ); ?></li>
						<li><span class="dashicons dashicons-saved"></span> <?php \esc_html_e( 'White labeling features: Gain increased trust by extending your business’ branding and tone of voice to all 2FA pages, wizards & emails', 'wp-2fa' ); ?></li>
						<li><span class="dashicons dashicons-saved"></span> <?php \esc_html_e( 'Refer to the features matrix below for a detailed list of all the premium features', 'wp-2fa' ); ?></li>
					</ul>
					<div class="premium-cta">
						<a href="<?php echo \esc_url( 'https://melapress.com/wordpress-2fa/pricing/?utm_source=plugin&utm_medium=link&utm_campaign=wp2fa' ); ?>" target="_blank" rel="noopener"><?php \esc_html_e( 'Upgrade to Premium', 'wp-2fa' ); ?></a>
					</div>		
				</div>
				<div class="content-block">
					<p><strong><?php \esc_html_e( 'WP 2FA plugin features', 'wp-2fa' ); ?></strong></p>
					<p><?php \esc_html_e( 'Take advantage of these benefits and many others, with prices starting from as little as $29 for 5 users per year. ', 'wp-2fa' ); ?></p>
					<table class="c21 feature-table">
						<tbody>
							<tr class="c2">
								<td class="c6" colspan="1" rowspan="1">
									<p class="c10 c4"><span class="c5"></span></p>
								</td>
								<td class="c8 row-head" colspan="1" rowspan="1">
									<p class="c7"><span class="c5"><?php \esc_html_e( 'Premium', 'wp-2fa' ); ?></span></p>
								</td>
								<td class="c12 row-head" colspan="1" rowspan="1">
									<p class="c7"><span class="c5"><?php \esc_html_e( 'Free', 'wp-2fa' ); ?></span></p>
								</td>
							</tr>
							<tr class="c2">
								<td class="c6" colspan="1" rowspan="1">
									<p class="c10"><span class="c5"><?php \esc_html_e( 'Support', 'wp-2fa' ); ?></span></p>
								</td>
								<td class="c8" colspan="1" rowspan="1">
									<p class="c7"><span class="c5"><?php \esc_html_e( '1-to-1 emails, forums', 'wp-2fa' ); ?></span></p>
								</td>
								<td class="c12" colspan="1" rowspan="1">
									<p class="c7"><span class="c5"><?php \esc_html_e( 'forums', 'wp-2fa' ); ?></span></p>
								</td>
							</tr>
							<tr class="c2">
								<td class="c6" colspan="1" rowspan="1">
									<p class="c10"><span class="c5"><?php \esc_html_e( 'Out of the box support for e-commerce, membership & third party plugins (no code required)', 'wp-2fa' ); ?></span></p>
								</td>
								<td class="c8" colspan="1" rowspan="1">
									<p class="c7"><span class="c5"><span class="dashicons dashicons-saved"></span></span></p>
								</td>
								<td class="c12" colspan="1" rowspan="1">
									<p class="c7"><span class="c5"><span class="dashicons dashicons-saved"></span></span></p>
								</td>
							</tr>
							<tr class="c2">
								<td class="c6" colspan="1" rowspan="1">
									<p class="c10"><span class="c5"><?php \esc_html_e( '2FA code via mobile app', 'wp-2fa' ); ?></span></p>
								</td>
								<td class="c8" colspan="1" rowspan="1">
									<p class="c7"><span class="c5"><span class="dashicons dashicons-saved"></span></span></p>
								</td>
								<td class="c12" colspan="1" rowspan="1">
									<p class="c7"><span class="c5"><span class="dashicons dashicons-saved"></span></span></p>
								</td>
							</tr>
							<tr class="c2">
								<td class="c6" colspan="1" rowspan="1">
									<p class="c10"><span class="c5"><?php \esc_html_e( '2FA code over email', 'wp-2fa' ); ?></span></p>
								</td>
								<td class="c8" colspan="1" rowspan="1">
									<p class="c7"><span class="c5"><span class="dashicons dashicons-saved"></span></span></p>
								</td>
								<td class="c12" colspan="1" rowspan="1">
									<p class="c7"><span class="c5"><span class="dashicons dashicons-saved"></span></span></p>
								</td>
							</tr>
							<tr class="c2">
								<td class="c6" colspan="1" rowspan="1">
									<p class="c10"><span class="c5"><?php \esc_html_e( '2FA login with hardware key (YubiKey)', 'wp-2fa' ); ?></span></p>
								</td>
								<td class="c8" colspan="1" rowspan="1">
									<p class="c7"><span class="c5"><span class="dashicons dashicons-saved"></span></span></p>
								</td>
								<td class="c12" colspan="1" rowspan="1">
									<p class="c7"><span class="c5"><span class="dashicons dashicons-no"></span></span></p>
								</td>
							</tr>
							<tr class="c2">
								<td class="c6" colspan="1" rowspan="1">
									<p class="c10"><span class="c5"><?php \esc_html_e( '2FA login with push notification (Authy)', 'wp-2fa' ); ?></span></p>
								</td>
								<td class="c8" colspan="1" rowspan="1">
									<p class="c7"><span class="c5"><span class="dashicons dashicons-saved"></span></span></p>
								</td>
								<td class="c12" colspan="1" rowspan="1">
									<p class="c7"><span class="c5"><span class="dashicons dashicons-no"></span></span></p>
								</td>
							</tr>
							<tr class="c2">
								<td class="c6" colspan="1" rowspan="1">
									<p class="c10"><span class="c5"><?php \esc_html_e( '2FA Login with SMS (with Twilio or Clickatell)', 'wp-2fa' ); ?></span></p>
								</td>
								<td class="c8" colspan="1" rowspan="1">
									<p class="c7"><span class="c5"><span class="dashicons dashicons-saved"></span></span></p>
								</td>
								<td class="c12" colspan="1" rowspan="1">
									<p class="c7"><span class="c5"><span class="dashicons dashicons-no"></span></span></p>
								</td>
							</tr>

							<tr class="c2">
								<td class="c6" colspan="1" rowspan="1">
									<p class="c10"><span class="c5"><?php \esc_html_e( 'One-click 2FA login (via link in email)', 'wp-2fa' ); ?></span></p>
								</td>
								<td class="c8" colspan="1" rowspan="1">
									<p class="c7"><span class="c5"><span class="dashicons dashicons-saved"></span></span></p>
								</td>
								<td class="c12" colspan="1" rowspan="1">
									<p class="c7"><span class="c5"><span class="dashicons dashicons-no"></span></span></p>
								</td>
							</tr>
							<tr class="c2">
								<td class="c6" colspan="1" rowspan="1">
									<p class="c10"><span class="c5"><?php \esc_html_e( 'Different 2FA policies per user role', 'wp-2fa' ); ?></span></p>
								</td>
								<td class="c8" colspan="1" rowspan="1">
									<p class="c7"><span class="c5"><span class="dashicons dashicons-saved"></span></span></p>
								</td>
								<td class="c12" colspan="1" rowspan="1">
									<p class="c7"><span class="c5"><span class="dashicons dashicons-no"></span></span></p>
								</td>
							</tr>
							<tr class="c2">
								<td class="c6" colspan="1" rowspan="1">
									<p class="c10"><span class="c5"><?php \esc_html_e( 'Trusted devices (remember devices)', 'wp-2fa' ); ?></span></p>
								</td>
								<td class="c8" colspan="1" rowspan="1">
									<p class="c7"><span class="c5"><span class="dashicons dashicons-saved"></span></span></p>
								</td>
								<td class="c12" colspan="1" rowspan="1">
									<p class="c7"><span class="c5"><span class="dashicons dashicons-no"></span></span></p>
								</td>
							</tr>
							<tr class="c2">
								<td class="c6" colspan="1" rowspan="1">
									<p class="c10"><span class="c5"><?php \esc_html_e( 'Alternative 2FA methods', 'wp-2fa' ); ?></span></p>
								</td>
								<td class="c8" colspan="1" rowspan="1">
									<p class="c7"><span class="c5"><span class="dashicons dashicons-saved"></span></span></p>
								</td>
								<td class="c12" colspan="1" rowspan="1">
									<p class="c7"><span class="c5"><?php \esc_html_e( 'Backup codes only', 'wp-2fa' ); ?></span></p>
								</td>
							</tr>
							<tr class="c2">
								<td class="c6" colspan="1" rowspan="1">
									<p class="c10"><span class="c5"><?php \esc_html_e( 'White labeling (logo, wizards, email, colours, fonts & custom CSS)', 'wp-2fa' ); ?></span></p>
								</td>
								<td class="c8" colspan="1" rowspan="1">
									<p class="c7"><span class="c5"><span class="dashicons dashicons-saved"></span></span></p>
								</td>
								<td class="c12" colspan="1" rowspan="1">
									<p class="c7"><span class="c5"><span class="dashicons dashicons-no"></span></span></p>
								</td>
							</tr>
							<tr class="c2">
								<td class="c6" colspan="1" rowspan="1">
									<p class="c10"><span class="c5"><?php \esc_html_e( 'One-click 2FA integration in WooCommerce user page', 'wp-2fa' ); ?></span></p>
								</td>
								<td class="c8" colspan="1" rowspan="1">
									<p class="c7"><span class="c5"><span class="dashicons dashicons-saved"></span></span></p>
								</td>
								<td class="c12" colspan="1" rowspan="1">
									<p class="c7"><span class="c5"><span class="dashicons dashicons-no"></span></span></p>
								</td>
							</tr>
							<tr class="c2">
								<td class="c6" colspan="1" rowspan="1">
									<p class="c10"><span class="c5"><?php \esc_html_e( 'Reports & Statistics', 'wp-2fa' ); ?></span></p>
								</td>
								<td class="c8" colspan="1" rowspan="1">
									<p class="c7"><span class="c5"><span class="dashicons dashicons-saved"></span></span></p>
								</td>
								<td class="c12" colspan="1" rowspan="1">
									<p class="c7"><span class="c5"><span class="dashicons dashicons-no"></span></span></p>
								</td>
							</tr>
							<tr class="c2">
								<td class="c6" colspan="1" rowspan="1">
									<p class="c10"><span class="c5"><?php \esc_html_e( 'Configurable 2FA code expiration time', 'wp-2fa' ); ?></span></p>
								</td>
								<td class="c8" colspan="1" rowspan="1">
									<p class="c7"><span class="c5"><span class="dashicons dashicons-saved"></span></span></p>
								</td>
								<td class="c12" colspan="1" rowspan="1">
									<p class="c7"><span class="c5"><span class="dashicons dashicons-no"></span></span></p>
								</td>
							</tr>
							<tr class="c2">
								<td class="c6" colspan="1" rowspan="1">
									<p class="c10"><span class="c5"><?php \esc_html_e( 'Sortable users\' 2FA status', 'wp-2fa' ); ?></span></p>
								</td>
								<td class="c8" colspan="1" rowspan="1">
									<p class="c7"><span class="c5"><span class="dashicons dashicons-saved"></span></span></p>
								</td>
								<td class="c12" colspan="1" rowspan="1">
									<p class="c7"><span class="c5"><span class="dashicons dashicons-no"></span></span></p>
								</td>
							</tr>
							<tr class="c2">
								<td class="c6" colspan="1" rowspan="1">
									<p class="c10"><span class="c5"><?php \esc_html_e( 'Export/import plugin settings', 'wp-2fa' ); ?></span></p>
								</td>
								<td class="c8" colspan="1" rowspan="1">
									<p class="c7"><span class="c5"><span class="dashicons dashicons-saved"></span></span></p>
								</td>
								<td class="c12" colspan="1" rowspan="1">
									<p class="c7"><span class="c5"><span class="dashicons dashicons-no"></span></span></p>
								</td>
							</tr>
							<tr class="c2">
								<td class="c6" colspan="1" rowspan="1">
									<p class="c10"><span class="c5"><?php \esc_html_e( 'No Ads!', 'wp-2fa' ); ?></span></p>
								</td>
								<td class="c8" colspan="1" rowspan="1">
									<p class="c7"><span class="c5"><span class="dashicons dashicons-saved"></span></span></p>
								</td>
								<td class="c12" colspan="1" rowspan="1">
									<p class="c7"><span class="c5"><span class="dashicons dashicons-no"></span></span></p>
								</td>
							</tr>
						</tbody>
					</table>

					<div class="premium-cta">
						<a href="<?php echo \esc_url( 'https://melapress.com/wordpress-2fa/pricing/?utm_source=plugin&utm_medium=link&utm_campaign=wp2fa' ); ?>" target="_blank" rel="noopener"><?php \esc_html_e( 'Upgrade to Premium', 'wp-2fa' ); ?></a>
					</div>		
				</div>

				<div>
					<p>
					<?php
					$text = sprintf(
						/* translators: 1: Link to our site 2: Link to our contact page */
						\esc_html__( 'Visit the WP 2FA %1$s for more information or %2$s  with any questions you might have. We look forward to hearing from you.', 'wp-2fa' ),
						'<a target="_blank" href="' . \esc_url( 'https://melapress.com/wordpress-2fa/?&utm_source=plugin&utm_medium=link&utm_campaign=wp2fa' ) . '">' . \esc_html__( 'plugin website', 'wp-2fa' ) . '</a>',
						'<a target="_blank" href="' . \esc_url( 'https://melapress.com/contact/?&utm_source=plugin&utm_medium=link&utm_campaign=wp2fa' ) . '">' . \esc_html__( 'contact us', 'wp-2fa' ) . '</a>'
					);

				echo $text; // phpcs:ignore -- Visit the WP 2FA plugin website for more information or contact us with any questions you might have. We look forward to hearing from you.
					?>
					</p>
				</div>
			</div>		
			<?php
		}

		/**
		 * Add "_blank" attr to pricing link to ensure it opens in new tab.
		 *
		 * @return void
		 *
		 * @since 2.8.0
		 */
		public static function pricing_new_tab_js() {
			?>
		<script type="text/javascript">
			jQuery( document ).ready( function() {
				jQuery( '.wp-2fa.pricing' ).parent().attr( 'target', '_blank' );
			});
		</script>
			<?php
		}
	}
}
includes/classes/Admin/Views/class-wizard-steps.php000064400000043361150755130600016415 0ustar00<?php
/**
 * Settings page render class.
 *
 * @package    wp2fa
 * @subpackage views
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 * @link       https://wordpress.org/plugins/wp-2fa/
 */

namespace WP2FA\Admin\Views;

use WP2FA\WP2FA;
use WP2FA\Utils\User_Utils;
use WP2FA\Admin\Helpers\WP_Helper;
use WP2FA\Admin\Helpers\User_Helper;
use WP2FA\Admin\Controllers\Settings;

defined( 'ABSPATH' ) || exit; // Exit if accessed directly.

if ( ! class_exists( '\WP2FA\Admin\Views\Wizard_Steps' ) ) {
	/**
	 * WP2FA Wizard Settings view controller
	 *
	 * @since 1.7
	 */
	class Wizard_Steps {

		/**
		 * Holds the nonce for json calls
		 *
		 * @since 1.7
		 *
		 * @var string
		 */
		private static $json_nonce = null;

		/**
		 * Holds the url to which to redirect the user after the setup is finished
		 *
		 * @var string
		 *
		 * @since 2.0.0
		 */
		private static $redirect_url = null;

		/**
		 * Introduction step form
		 *
		 * @since 1.7
		 *
		 * @return void
		 */
		public static function optional_user_welcome_step() {
			?>
			<div class="wizard-step active">
				<div class="mb-20">
					<?php echo \wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'welcome', true ) ); ?>
				</div>

				<div class="wp2fa-setup-actions">
					<a href="#" class="button wp-2fa-button-primary button-primary" data-name="next_step_setting_modal_wizard" data-next-step="choose-2fa-method"><?php \esc_html_e( 'Next Step', 'wp-2fa' ); ?></a>
					<button class="wp-2fa-button-secondary button button-secondary wp-2fa-button-secondary" data-close-2fa-modal aria-label="Close this dialog window"><?php \esc_html_e( 'Cancel', 'wp-2fa' ); ?></button>
				</div>
			</div>
			<?php
		}

		/**
		 * Introduction step form
		 *
		 * @since 1.7
		 *
		 * @return void
		 */
		public static function introduction_step() {
			?>
			<form method="post" class="wp2fa-setup-form">
				<?php wp_nonce_field( 'wp2fa-step-addon' ); ?>
				<div class="mb-20">
					<?php echo \wp_kses_post( WP2FA::get_wp2fa_white_label_setting( '2fa_required_intro', true ) ); ?>
				</div>

				<div class="wp2fa-setup-actions">
					<button class="button button-primary wp-2fa-button-primary"
					type="submit"
					name="save_step"
					value="<?php \esc_attr_e( 'Next', 'wp-2fa' ); ?>">
					<?php \esc_html_e( 'Next', 'wp-2fa' ); ?>
					</button>
				</div>
			</form>
			<?php
		}

		/**
		 * Welcome step of the wizard
		 *
		 * @since 1.7
		 *
		 * @param string $next_step - url of the next step.
		 *
		 * @return void
		 */
		public static function welcome_step( $next_step ) {
			$redirect = Settings::get_settings_page_link();

			?>
			<h3><?php \esc_html_e( 'Let us help you get started', 'wp-2fa' ); ?></h3>
			<p><?php \esc_html_e( 'Thank you for installing the WP 2FA plugin. This quick wizard will assist you with configuring the plugin and the two-factor authentication (2FA) settings for your user and the users on this website.', 'wp-2fa' ); ?></p>

			<div class="wp2fa-setup-actions">
				<a class="button button-primary"
					href="<?php echo \esc_url( $next_step ); ?>">
					<?php \esc_html_e( 'Let’s get started!', 'wp-2fa' ); ?>
				</a>
				<a class="button button-secondary wp-2fa-button-secondary first-time-wizard"
					href="<?php echo \esc_url( $redirect ); ?>">
					<?php \esc_html_e( 'Skip Wizard - I know how to do this', 'wp-2fa' ); ?>
				</a>
			</div>
			<?php
		}

		/**
		 * Configure backup codes step
		 *
		 * @since 1.7
		 *
		 * @return void
		 */
		public static function backup_codes_configure() {

			$user_type = User_Utils::determine_user_2fa_status( User_Helper::get_user_object() );

			$redirect = self::determine_redirect_url();
			?>
			<div class="step-setting-wrapper active">
			<?php
			if ( in_array( 'user_needs_to_setup_backup_codes', $user_type, true ) ) {
				?>
				<div class="mb-20">
					<?php echo \wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'backup_codes_intro', true ) ); ?>
				</div>
			<?php } else { ?>
				<div class="mb-20">
					<?php echo \wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'backup_codes_intro_continue', true ) ); ?>
				</div>
			<?php } ?>
			<div class="wp2fa-setup-actions">
			<?php if ( in_array( 'user_needs_to_setup_backup_codes', $user_type, true ) ) { ?>
				<button class="button button-primary wp-2fa-button-primary" name="next_step_setting" value="<?php \esc_attr_e( 'Generate backup codes', 'wp-2fa' ); ?>" data-trigger-generate-backup-codes <?php echo WP_Helper::create_data_nonce( self::json_nonce() ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
					<?php \esc_html_e( 'Generate list of backup codes', 'wp-2fa' ); ?>
				</button>
				<?php
				if ( ! empty( $redirect ) ) {
					?>
					<a href="<?php echo \esc_url( $redirect ); ?>" class="button button-secondary wp-2fa-button-secondary wp-2fa-button-secondary close-first-time-wizard">
						<?php \esc_html_e( 'I’ll generate them later', 'wp-2fa' ); ?>
					</a>
					<?php
				} else {
					?>
					<a href="#" class="button wp-2fa-button-secondary" data-close-2fa-modal value="<?php \esc_attr_e( 'I’ll generate them later', 'wp-2fa' ); ?>">
						<?php \esc_html_e( 'I’ll generate them later', 'wp-2fa' ); ?>
					</a>
				<?php } ?>
			<?php } else { ?>
				<?php
				if ( ! empty( $redirect ) ) {
					?>
					<a href="<?php echo \esc_url( $redirect ); ?>" class="button button-secondary wp-2fa-button-secondary close-first-time-wizard">
					<?php \esc_html_e( 'Close wizard', 'wp-2fa' ); ?>
					</a>
					<?php
				} else {
					?>
				<a href="#" class="button button-secondary wp-2fa-button-secondary" data-reload>
					<?php \esc_html_e( 'Close wizard', 'wp-2fa' ); ?>
				</a>
				<?php } ?>
			<?php } ?>
			</div>
			</div>
				<?php
		}

		/**
		 * Generate backup codes step
		 *
		 * @since 1.7
		 *
		 * @return void
		 */
		public static function generate_backup_codes() {
			?>
			<div class="step-setting-wrapper active" data-step-title="<?php \esc_html_e( 'Generate codes', 'wp-2fa' ); ?>">
				<div class="mb-20">
					<?php echo \wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'backup_codes_generate_intro', true ) ); ?>
				</div>
				<div class="wp2fa-setup-actions">
					<button class="button button-primary" name="next_step_setting" value="<?php \esc_attr_e( 'Generate backup codes', 'wp-2fa' ); ?>" data-trigger-generate-backup-codes <?php echo WP_Helper::create_data_nonce( self::json_nonce() ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
						<?php \esc_html_e( 'Generate list of backup codes', 'wp-2fa' ); ?>
					</button>
					<a href="#" class="button button-secondary wp-2fa-button-secondary" value="<?php \esc_attr_e( 'I’ll generate them later', 'wp-2fa' ); ?>" data-close-2fa-modal="">
						<?php \esc_html_e( 'I’ll generate them later', 'wp-2fa' ); ?>
					</a>
				</div>
			</div>

			<?php
		}

		/**
		 * Creates link for generating the backup codes
		 *
		 * @since 1.7
		 *
		 * @return string
		 */
		public static function get_generate_codes_label() {
			$label = __( 'Backup 2FA methods:', 'wp-2fa' );

			return $label . '</th><td>';
		}

		/**
		 * Creates backup codes URL link
		 *
		 * @return string
		 *
		 * @since 2.6.0
		 */
		public static function get_backup_codes_link(): string {
			return '<a href="#" class="button button-primary remove-2fa" data-trigger-generate-backup-codes ' . WP_Helper::create_data_nonce( self::json_nonce() ) . ' onclick="MicroModal.show( \'configure-2fa-backup-codes\' );">' . __( 'Generate list of backup codes', 'wp-2fa' ) . '</a>';
		}

		/**
		 * Shows the wrapper where backup code are generated and showed to the user
		 *
		 * @param boolean $backup_only - If we want to show backup window only - sets the class of the div to active.
		 *
		 * @since 1.7
		 *
		 * @return void
		 */
		public static function generated_backup_codes( $backup_only = false ) {

			$redirect = self::determine_redirect_url();

			?>
			<div class="step-setting-wrapper align-center<?php echo ( $backup_only ) ? ' active' : ''; ?>" data-step-title="<?php \esc_html_e( 'Your backup codes', 'wp-2fa' ); ?>">
				<div class="mb-20">
					<?php echo \wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'backup_codes_generated', true ) ); ?>
				</div>
				<div class="backup-key-wrapper">
					<textarea id="backup-codes-wrapper" readonly rows="4" cols="50" class="app-key"></textarea>
				</div>
				<div class="wp2fa-setup-actions">
					<?php if ( is_ssl() ) { ?>
						<button class="button button-primary wp-2fa-button-primary" type="submit" value="<?php \esc_attr_e( 'Download', 'wp-2fa' ); ?>" data-trigger-backup-code-copy>
							<?php \esc_html_e( 'Copy', 'wp-2fa' ); ?>
						</button>
					<?php } else { ?>
						<button class="button button-primary wp-2fa-button-primary" type="submit" value="<?php \esc_attr_e( 'Download', 'wp-2fa' ); ?>" data-trigger-backup-code-download data-user="<?php echo \esc_attr( User_Helper::get_user_object()->display_name ); ?>" data-website-url="<?php echo \esc_attr( get_home_url() ); ?>">
							<?php \esc_html_e( 'Download', 'wp-2fa' ); ?>
						</button>
					<?php } ?>
					<button class="button button-primary wp-2fa-button-primary" type="submit" value="<?php \esc_attr_e( 'Print', 'wp-2fa' ); ?>" data-trigger-print <?php echo WP_Helper::create_data_nonce( self::json_nonce() ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> data-user-id="<?php echo \esc_attr( User_Helper::get_user_object()->display_name ); ?>" data-website-url="<?php echo \esc_attr( get_home_url() ); ?>">
						<?php \esc_html_e( 'Print', 'wp-2fa' ); ?>
					</button>

					<button class="button button-primary wp-2fa-button-primary" type="submit" value="<?php \esc_attr_e( 'Send me the codes via email', 'wp-2fa' ); ?>" data-trigger-backup-code-email <?php echo WP_Helper::create_data_nonce( 'wp-2fa-send-backup-codes-email-nonce' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> data-user-id="<?php echo \esc_attr( User_Helper::get_user_object()->ID ); ?>" data-website-url="<?php echo \esc_attr( get_home_url() ); ?>">
						<?php \esc_html_e( 'Send me the codes via email', 'wp-2fa' ); ?>
					</button>
					<?php
					if ( ! empty( $redirect ) ) {
						?>
						<a href="<?php echo \esc_url( $redirect ); ?>" class="button button-secondary wp-2fa-button-secondary wp-2fa-button-secondary close-first-time-wizard">
						<?php \esc_html_e( 'I\'m ready, close the wizard', 'wp-2fa' ); ?>
						</a>
						<?php
					} else {
						?>
					<button class="button button-secondary wp-2fa-button-secondary wp-2fa-button-secondary" type="submit" data-close-2fa-modal-and-refresh>
						<?php \esc_html_e( 'I\'m ready, close the wizard', 'wp-2fa' ); ?>
					</button>
					<?php } ?>
				</div>
			</div>
			<?php
		}

		/**
		 * Final step for congratulating the user
		 *
		 * @since 1.7
		 *
		 * @param boolean $setup_wizard - Is that a call from setup wizard or not.
		 *
		 * @return void
		 */
		public static function congratulations_step( $setup_wizard = false ) {

			if ( $setup_wizard ) {
				self::congratulations_step_plugin_wizard();
				return;
			}

			$redirect = ( '' !== self::determine_redirect_url() ) ? self::determine_redirect_url() : '';
			?>

			<div class="step-setting-wrapper active">
			<div class="mb-20">
				<?php echo \wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'no_further_action', true ) ); ?>
			</div>
			<div class="wp2fa-setup-actions">
				<?php if ( '' !== trim( $redirect ) ) { ?>
				<a href="<?php echo \esc_url( $redirect ); ?>" class="button button-secondary wp-2fa-button-secondary close-first-time-wizard">
						<?php \esc_html_e( 'Close wizard', 'wp-2fa' ); ?>
				</a>
				<?php } else { ?>
				<button class="modal__btn wp-2fa-button-secondary button" data-close-2fa-modal aria-label="Close this dialog window"><?php \esc_html_e( 'Close wizard', 'wp-2fa' ); ?></button>
				<?php } ?>
			</div>
			</div>
			<?php
		}

		/**
		 * Final step for congratulating the user
		 *
		 * @since 1.7
		 *
		 * @return void
		 */
		public static function congratulations_step_plugin_wizard() {
			$redirect    = ( '' !== self::determine_redirect_url() ) ? self::determine_redirect_url() : get_edit_profile_url( User_Helper::get_user_object()->ID );
			$slide_title = ( User_Helper::is_excluded( User_Helper::get_user_object()->ID ) ) ? \esc_html__( 'Congratulations.', 'wp-2fa' ) : \esc_html__( 'Congratulations, you\'re almost there...', 'wp-2fa' );
			?>
				<h3><?php echo \esc_html( $slide_title ); ?></h3>
				<p><?php \esc_html_e( 'Great job, the plugin and 2FA policies are now configured. You can always change the plugin settings and 2FA policies at a later stage from the WP 2FA entry in the WordPress menu.', 'wp-2fa' ); ?></p>

					<?php
					if ( User_Helper::is_excluded( User_Helper::get_user_object()->ID ) ) {
						?>
				<div class="wp2fa-setup-actions">
					<a href="<?php echo \esc_url( $redirect ); ?>" class="button button-secondary wp-2fa-button-secondary close-first-time-wizard">
							<?php \esc_html_e( 'Close wizard', 'wp-2fa' ); ?>
					</a>
				</div>
						<?php
					} else {
						?>
				<p><?php \esc_html_e( 'Now you need to configure 2FA for your own user account. You can do this now (recommended) or later.', 'wp-2fa' ); ?></p>
				<div class="wp2fa-setup-actions">
					<a href="<?php echo \esc_url( Settings::get_setup_page_link() ); ?>" class="button button-primary wp-2fa-button-secondary">
						<?php \esc_html_e( 'Configure 2FA now', 'wp-2fa' ); ?>
					</a>
					<a href="<?php echo \esc_url( Settings::get_settings_page_link() ); ?>" class="button button-secondary wp-2fa-button-secondary close-first-time-wizard">
						<?php \esc_html_e( 'Close wizard & configure 2FA later', 'wp-2fa' ); ?>
					</a>
				</div>
					<?php } ?>
			<?php
		}

		/**
		 * Shows the methods in the modal wizard, so the user can choose from the available ones
		 *
		 * @return void
		 */
		public static function show_modal_methods() {
			/**
			 * Add an option for external providers to add their own modal methods options.
			 *
			 * @since 2.0.0
			 */
			\do_action( WP_2FA_PREFIX . 'modal_methods' );
		}

		/**
		 * Choosing backup method step
		 * When there are more than one backup method - give the user ability to choose one
		 *
		 * @return void
		 *
		 * @since 2.0.0
		 */
		public static function choose_backup_method() {
			$redirect = self::determine_redirect_url();
			?>
			<div class="wizard-step" id="2fa-wizard-backup-methods">
				<div class="option-pill mb-20">
					<?php echo \wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'backup_codes_intro_multi', true ) ); ?>
				</div>
				<div class="radio-cells">
				<?php
				$backup_methods = Settings::get_backup_methods();

				$i = 0;
				foreach ( $backup_methods as $method_name => $method ) {
					$checked = '';
					if ( ! $i ) {
						$checked = ' checked="checked"';
					}
					$i = 1;
					?>
					<div class="option-pill"><label for="<?php echo \esc_attr( $method_name ); ?>"><input name="backup_method_select" data-step="<?php echo \esc_attr( $method['wizard-step'] ); ?>" type="radio" id="<?php echo \esc_attr( $method_name ); ?>" <?php echo $checked; ?>><?php echo $method['button_name']; // phpcs:ignore ?></label><br /></div>
					<?php
				}
				?>
				</div>
				<div class="wp2fa-setup-actions">
					<a id="select-backup-method" href="<?php echo \esc_url( Settings::get_setup_page_link() ); ?>" class="button button-primary wp-2fa-button-primary">
						<?php \esc_html_e( 'Configure backup 2FA method', 'wp-2fa' ); ?>
					</a>
					<a href="<?php echo \esc_url( $redirect ); ?>" class="button button-secondary wp-2fa-button-secondary close-first-time-wizard"  <?php echo ( ( '' === trim( (string) $redirect ) ) ? 'data-close-it=""' : '' ); ?>  >
							<?php \esc_html_e( 'Close wizard & configure 2FA later', 'wp-2fa' ); ?>
					</a>
					<script>
						const closeButton = document.querySelector('[data-close-it]');

						if (closeButton) {
							closeButton.addEventListener('click', (event) => {
								event.preventDefault();
								let url = new URL( location.href );
								let params = new URLSearchParams( url.search );
								params.delete('show'); 
								location.replace( `${location.pathname}?${params}` );
							});
						}
					</script>
				</div>
			</div>
			<?php
		}

		/**
		 * Determines the redirect url for the user
		 *
		 * @return string
		 *
		 * @since 2.0.0
		 */
		public static function determine_redirect_url(): string {
			if ( null === self::$redirect_url ) {
				$redirect_page      = Settings::get_role_or_default_setting( 'redirect-user-custom-page-global', User_Helper::get_user_object() );
				self::$redirect_url = ( '' !== trim( (string) $redirect_page ) ) ? \trailingslashit( get_site_url() ) . $redirect_page : '';

				if (
				'yes' === Settings::get_role_or_default_setting( 'create-custom-user-page', User_Helper::get_user_object() ) ||
				'yes' === Settings::get_role_or_default_setting( 'create-custom-user-page' ) ) {
					if (
					'' !== trim( (string) Settings::get_role_or_default_setting( 'redirect-user-custom-page', User_Helper::get_user_object() ) ) ||
					'' !== trim( (string) Settings::get_role_or_default_setting( 'redirect-user-custom-page' ) ) ) {
						if ( 'yes' === Settings::get_role_or_default_setting( 'create-custom-user-page', User_Helper::get_user_object() ) ) {
							self::$redirect_url = trailingslashit( get_site_url() ) . Settings::get_role_or_default_setting( 'redirect-user-custom-page', User_Helper::get_user_object() );
						} else {
							self::$redirect_url = trailingslashit( get_site_url() ) . Settings::get_role_or_default_setting( 'redirect-user-custom-page' );
						}
					}
				}
			}

			return self::$redirect_url;
		}

		/**
		 * Generates nonce for JSON calls
		 *
		 * @since 1.7
		 *
		 * @return string
		 */
		protected static function json_nonce() {
			if ( null === self::$json_nonce ) {
				self::$json_nonce = 'wp-2fa-backup-codes-generate-json-' . User_Helper::get_user_object()->ID;
			}

			return self::$json_nonce;
		}
	}
}
includes/classes/Admin/Views/index.php000064400000000046150755130600013756 0ustar00<?php
/**
 * Nothing to see here.
 */
includes/classes/Admin/Views/class-first-time-wizard-steps.php000064400000062007150755130600020474 0ustar00<?php
/**
 * Settings page render class.
 *
 * @package    wp2fa
 * @subpackage views
 * @since      1.7.0
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 * @link       https://wordpress.org/plugins/wp-2fa/
 */

namespace WP2FA\Admin\Views;

use WP2FA\WP2FA;
use WP2FA\Methods\Backup_Codes;
use WP2FA\Admin\Helpers\WP_Helper;
use WP2FA\Admin\Controllers\Settings;
use WP2FA\Methods\TOTP;

defined( 'ABSPATH' ) || exit; // Exit if accessed directly.

if ( ! class_exists( '\WP2FA\Admin\Views\First_Time_Wizard_Steps' ) ) {
	/**
	 * WP2FA First Wizard Settings view controller
	 *
	 * @since 1.7
	 */
	class First_Time_Wizard_Steps {

		/**
		 * Select method step
		 *
		 * @since 1.7.0
		 *
		 * @param boolean $setup_wizard - Boolean - is that first time wizard setup or settings page call.
		 *
		 * @return void
		 */
		public static function select_method( $setup_wizard = false ) {

			ob_start();
			?>
			<h3><?php \esc_html_e( 'Which 2FA methods can your users use?', 'wp-2fa' ); ?></h3>
			<p class="description">
				<?php \esc_html_e( 'When you uncheck any of the below 2FA methods it won\'t be available for your users to use. You can always change this later on from the plugin\'s settings.', 'wp-2fa' ); ?>
			</p>
				<?php
				$data_role = 'data-role="global"';
				if ( ! $setup_wizard ) {
					?>
			<table class="form-table">
				<tbody>
					<tr>
						<th colspan="2"><?php \esc_html_e( 'Which of the below 2FA methods can users use?', 'wp-2fa' ); ?></th>
					</tr>
					<tr>
						<th><label for="2fa-method"><?php \esc_html_e( 'Select the methods', 'wp-2fa' ); ?></label></th>
						<td>
				<?php } ?>
						<fieldset id="2fa-method-select" class="wp-2fa-method-select">
							<p class="method-title" style="padding-bottom: 20px;"><em><?php \esc_html_e( 'Primary 2FA methods:', 'wp-2fa' ); ?></em></p>
							<?php
							/**
							 * Fired right after the TOTP method HTML rendering.
							 *
							 * @param bool $wizard - Is that a wizard call or settings call.
							 * @param string $data_role - String with the JS data to add to form element.
							 * @param string $name - The name of the role.
							 *
							 * @since 2.0.0
							 */
							\do_action( WP_2FA_PREFIX . 'methods_setup', $setup_wizard, $data_role, null );
							?>
							<br />
								<?php
								if ( ! $setup_wizard ) {
									$class = '';

									if ( '' === trim( (string) Settings::get_role_or_default_setting( TOTP::POLICY_SETTINGS_NAME, null, null, true ) ) && '' === trim( (string) Settings::get_role_or_default_setting( 'enable_email', null, null, true ) ) && '' === trim( (string) Settings::get_role_or_default_setting( 'enable_oob_email', null, null, true ) ) ) {
										$class = 'disabled';
									}
									?>
								<div class="method-title"><em><?php \esc_html_e( 'Secondary 2FA methods:', 'wp-2fa' ); ?></em></div>
								<br>
								<label for="backup-codes" class=" <?php echo $class; // phpcs:ignore ?>">
									<input type="checkbox" class="<?php echo \esc_attr( $class ); ?>" id="backup-codes" name="wp_2fa_policy[backup_codes_enabled]" 
									<?php echo $data_role; // phpcs:ignore ?>
									value="yes"
									<?php checked( WP2FA::get_wp2fa_setting( Backup_Codes::get_settings_name() ), Backup_Codes::get_settings_default_value() ); ?>
									>
									<?php
									\esc_html_e( 'Backup codes', 'wp-2fa' );
									if ( $setup_wizard ) {
										echo '<p class="description">Note: ';
									} else {
										echo ' - ';
									}
									\esc_html_e( 'Backup codes are a secondary method which you can use to log in to the website in case the primary 2FA method is unavailable. Therefore they can\'t be enabled and used as a primary method.', 'wp-2fa' );
									if ( $setup_wizard ) {
										echo '</p>';
									}
									?>
								</label>
									<?php
									/**
									 * Fires after the backup methods HTML rendering is finished.
									 *
									 * @param bool $wizard - Is that wizard ot standard setting.
									 * @param string $data_role - The JS data attribute for the form inputs.
									 * @param string $role - The name of the user role.
									 *
									 * @since 2.0.0
									 */
									\do_action( WP_2FA_PREFIX . 'after_backup_methods_setup', $setup_wizard, $data_role, null );
								}
								?>
						</fieldset>
							<?php
							if ( ! $setup_wizard ) {
								?>
						</td>
					</tr>
				</tbody>
			</table>
			<?php } ?>
			<?php
			$output = ob_get_clean();

			/**
			 * At this point, none of the default providers is set / activated. This filter allows additional providers to change the behavior. Checking the input array for specific values (methods), and based on that we can raise error that none of the allowed methods has bees selected by the user, or dismiss the error otherwise.
			 *
			 * @param string $output - Parsed HTML with the methods.
			 * @param bool $setup_wizard - The type of the wizard (first time wizard / settings).
			 *
			 * @since 2.0.0
			 */
			$output = apply_filters( WP_2FA_PREFIX . 'select_methods', $output, $setup_wizard );

			echo $output; // phpcs:ignore
		}

		/**
		 * Builds the backup methods html
		 *
		 * @param boolean $setup_wizard - Is that call from the Wizard or not.
		 *
		 * @return void
		 *
		 * @since 2.4.1
		 */
		public static function backup_method( $setup_wizard = false ) {

			ob_start();
			?>
			<h3><?php \esc_html_e( 'Which alternative 2FA methods can users use?', 'wp-2fa' ); ?></h3>
			<p class="description">
				<?php \esc_html_e( 'An alternative 2FA method allows users to configure another 2FA method that can be used as a backup should the primary 2FA method fail. This can happen if, for example, a user forgets their smartphone, the smartphone runs out of battery, or there are email deliverability problems.', 'wp-2fa' ); ?>
			</p>
			<p class="description">
				<?php \esc_html_e( 'It is highly recommended to have an alternative 2FA method configured at all times. Below is a list of alternative 2FA methods available through this plugin:', 'wp-2fa' ); ?>
			</p>

			<br>

			<fieldset>
				<label for="backup-codes">
					<input type="checkbox" id="backup-codes-global" name="wp_2fa_policy[backup_codes_enabled]" value="yes"
					<?php checked( WP2FA::get_wp2fa_setting( Backup_Codes::get_settings_name() ), Backup_Codes::get_settings_default_value() ); ?>
					>
					<?php \esc_html_e( 'Backup codes', 'wp-2fa' ); ?>
				</label>

				<?php
					echo '<p class="description">';
					printf( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
						'%1$1s <a href="https://melapress.com/support/kb/wp-2fa-what-are-2fa-backup-codes/?&utm_source=plugin&utm_medium=link&utm_campaign=wp2fa" target="_blank">%2$1s</a> <br><br>',
						\esc_html__( 'Backup codes allow users to log in to WordPress should they find themselves unable to log in via the primary 2FA method. Backup codes are enabled by default and are generated during the 2FA configuration process. Each backup code can be used only once. Once the initial list is exhausted, more backup codes can be generated through the user’s WordPress profile page - ', 'wp-2fa' ),
						\esc_html__( 'More information', 'wp-2fa' )
					);
					echo '</p>';
				?>

				<?php
				/* @free:start */
					echo '<label>';
					printf( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
						'%1$1s <a href="https://melapress.com/wordpress-2fa/features/?&utm_source=plugin&utm_medium=link&utm_campaign=wp2fa" target="_blank">%2$1s</a> %3$1s',
						\esc_html__( 'Upgrade to WP 2FA Premium for', 'wp-2fa' ),
						\esc_html__( 'more alternative 2FA methods', 'wp-2fa' ),
						\esc_html__( 'to give your users more options.', 'wp-2fa' )
					);
					echo '<label>';
				/* @free:end */
				?>
			</fieldset>
				<?php
			?>
			<?php
			$output = ob_get_clean();
			$output = apply_filters( WP_2FA_PREFIX . 'backup_methods', $output, $setup_wizard );

			echo $output; // phpcs:ignore
		}

		/**
		 * Enforcement policy step
		 *
		 * @since 1.7.0
		 *
		 * @param boolean $setup_wizard - Boolean - is that first time wizard setup or settings page call.
		 *
		 * @return void
		 */
		public static function enforcement_policy( $setup_wizard = false ) {
			?>
		<h3 id="enforcement_settings"><?php \esc_html_e( 'Do you want to enforce 2FA for some, or all the users? ', 'wp-2fa' ); ?></h3>
		<p class="description">
			<?php \esc_html_e( 'When you enforce 2FA the users will be prompted to configure 2FA the next time they login. Users have a grace period for configuring 2FA. You can configure the grace period and also exclude user(s) or role(s) in this settings page. ', 'wp-2fa' ); ?> <a href="https://melapress.com/support/kb/wp-2fa-configure-2fa-policies-enforce/?&utm_source=plugin&utm_medium=link&utm_campaign=wp2fa" target="_blank" rel=noopener><?php \esc_html_e( 'Learn more.', 'wp-2fa' ); ?></a>
		</p>
			<?php
			if ( ! $setup_wizard ) {
				?>
		<table class="form-table js-enforcement-policy-section">
			<tbody>
				<tr>
					<th><label for="enforcement-policy"><?php \esc_html_e( 'Enforce 2FA on', 'wp-2fa' ); ?></label></th>
					<td>
			<?php } ?>
						<fieldset class="contains-hidden-inputs">
							<label for="all-users" style="margin:.35em 0 .5em !important; display: block;">
								<input type="radio" name="wp_2fa_policy[enforcement-policy]" id="all-users" value="all-users"
								<?php checked( WP2FA::get_wp2fa_setting( 'enforcement-policy' ), 'all-users' ); ?>
								>
							<span><?php \esc_html_e( 'All users', 'wp-2fa' ); ?></span>
							</label>

							<?php if ( WP_Helper::is_multisite() ) : ?>
								<label for="superadmins-only" style="margin:.35em 0 .5em !important; display: block;">
									<input type="radio" name="wp_2fa_policy[enforcement-policy]" id="superadmins-only" value="superadmins-only"
											<?php checked( WP2FA::get_wp2fa_setting( 'enforcement-policy' ), 'superadmins-only' ); ?> />
									<span><?php \esc_html_e( 'Only super admins', 'wp-2fa' ); ?></span>
								</label>
								<label for="superadmins-siteadmins-only" style="margin:.35em 0 .5em !important; display: block;">
									<input type="radio" name="wp_2fa_policy[enforcement-policy]" id="superadmins-siteadmins-only" value="superadmins-siteadmins-only"
											<?php checked( WP2FA::get_wp2fa_setting( 'enforcement-policy' ), 'superadmins-siteadmins-only' ); ?> />
									<span><?php \esc_html_e( 'Only super admins and site admins', 'wp-2fa' ); ?></span>
								</label>
							<?php endif; ?>

							<label for="certain-roles-only" style="margin:.35em 0 .5em !important; display: block;">
								<?php $checked = in_array( WP2FA::get_wp2fa_setting( 'enforcement-policy' ), array( 'certain-roles-only', 'certain-users-only' ), true ); ?>
								<input type="radio" name="wp_2fa_policy[enforcement-policy]" id="certain-roles-only" value="certain-roles-only"
								<?php ( $setup_wizard ) ? checked( WP2FA::get_wp2fa_setting( 'enforcement-policy' ), 'certain-roles-only' ) : checked( $checked ); ?>
								data-unhide-when-checked=".certain-roles-only-inputs, .certain-users-only-inputs">
								<span><?php \esc_html_e( 'Only for specific users and roles', 'wp-2fa' ); ?></span>
							</label>
							<fieldset class="hidden certain-users-only-inputs">
								<div>
									<p>
										<label for="enforced_users-multi-select"><?php \esc_html_e( 'Users :', 'wp-2fa' ); ?></label> <select multiple="multiple" id="enforced_users-multi-select" name="wp_2fa_policy[enforced_users][]" style=" display:none;width:<?php echo ( $setup_wizard ) ? '100' : '50'; ?>%">
										<?php
										$enforced_users = (array) WP2FA::get_wp2fa_setting( 'enforced_users' );
										foreach ( $enforced_users as $user ) {
											?>
												<option selected="selected" value="<?php echo \esc_attr( $user ); ?>"><?php echo \esc_attr( $user ); ?></option>
												<?php
										}
										?>
										</select>
									</p>
								</div>
							</fieldset>
							<fieldset class="hidden certain-roles-only-inputs">
								<div>
									<p style="margin-top: 0;">
										<label for="enforced-roles-multi-select"><?php \esc_html_e( 'Roles :', 'wp-2fa' ); ?></label>
										<select multiple="multiple" id="enforced-roles-multi-select" name="wp_2fa_policy[enforced_roles][]" style=" display:none;width:<?php echo ( $setup_wizard ) ? '100' : '50'; ?>%">
										<?php
										$all_roles      = WP_Helper::get_roles_wp();
										$enforced_roles = (array) WP2FA::get_wp2fa_setting( 'enforced_roles' );
										foreach ( $all_roles as $role => $role_name ) {
											$selected = '';
											if ( in_array( $role, $enforced_roles, true ) ) {
												$selected = 'selected="selected"';
											}
											?>
														<option <?php echo $selected; // phpcs:ignore ?> value="<?php echo \esc_attr( strtolower( $role ) ); ?>"><?php echo \esc_html( $role_name ); ?></option>
												<?php
										}
										?>
										</select>
									</p>
								</div>
										<?php if ( WP_Helper::is_multisite() ) { ?>
								<p class="description">
									<input type="checkbox" name="wp_2fa_policy[superadmins-role-add]" id="superadmins-role-add" value="yes" style="position: relative; top: -3px;" 
											<?php checked( WP2FA::get_wp2fa_setting( 'superadmins-role-add' ), 'yes' ); ?> />
									<label for="superadmins-role-add"><?php \esc_html_e( 'Also enforce 2FA on network users with super admin privileges', 'wp-2fa' ); ?></label>
								</p>
								<?php } ?>
							</fieldset>
						<?php if ( WP_Helper::is_multisite() ) { ?>
							<div>
								<label for="enforce-on-multisite" style="margin:.35em 0 .5em !important; display: block;">
									<input type="radio" name="wp_2fa_policy[enforcement-policy]" id="enforce-on-multisite" value="enforce-on-multisite"
										<?php checked( WP2FA::get_wp2fa_setting( 'enforcement-policy' ), 'enforce-on-multisite' ); ?>
									data-unhide-when-checked=".all-sites">
									<span><?php \esc_html_e( 'These sub-sites', 'wp-2fa' ); ?></span>
								</label>
								<fieldset class="hidden all-sites">
									<p>
										<label for="enforced-sites-multi-select"><?php \esc_html_e( 'Sites :', 'wp-2fa' ); ?></label> <select multiple="multiple" id="enforced-sites-multi-select" name="wp_2fa_policy[included_sites][]" style="display:none; width:<?php echo ( $setup_wizard ) ? '100' : '50'; ?>%">
											<?php
											$selected_sites = (array) WP2FA::get_wp2fa_setting( 'included_sites' );
											foreach ( WP_Helper::get_multi_sites() as $site ) {
												$args = array(
													'blog_id' => $site->blog_id,
												);

												$current_blog_details = get_blog_details( $args );
												$selected             = '';
												if ( in_array( $site->blog_id, $selected_sites, true ) ) {
													$selected = 'selected="selected"';
												}
												?>
												<option <?php echo $selected; // phpcs:ignore ?> value="<?php echo \esc_attr( $site->blog_id ); ?>"><?php echo \esc_html( $current_blog_details->blogname ); ?></option>
												<?php
											}
											?>
										</select>
									</p>
								</fieldset>
							</div>
					<?php } ?>
							<div>
								<label for="do-not-enforce" style="margin:.35em 0 .5em !important; display: block;">
									<input type="radio" name="wp_2fa_policy[enforcement-policy]" id="do-not-enforce" value="do-not-enforce"
										<?php checked( WP2FA::get_wp2fa_setting( 'enforcement-policy' ), 'do-not-enforce' ); ?>
									>
									<span><?php \esc_html_e( 'Do not enforce on any users', 'wp-2fa' ); ?></span>
								</label>
							</div>
							<br/>
						</fieldset>
							<?php
							if ( ! $setup_wizard ) {
								?>
					</td>
				</tr>
			</tbody>
		</table>
								<?php
							}
		}

		/**
		 * Exclude users and groups
		 *
		 * @since 1.7.0
		 *
		 * @param boolean $setup_wizard - Boolean - is that first time wizard setup or settings page call.
		 *
		 * @return void
		 */
		public static function exclude_users( $setup_wizard = false ) {
			?>
		<h3><?php \esc_html_e( 'Do you want to exclude any users or roles from 2FA? ', 'wp-2fa' ); ?></h3>
		<p class="description">
			<?php \esc_html_e( 'If you are enforcing 2FA on all users but for some reason you would like to exclude individual user(s) or users with a specific role, you can exclude them below', 'wp-2fa' ); ?>
		</p>
			<?php
			if ( ! $setup_wizard ) {
				?>
		<table class="form-table js-enforcement-policy-section">
			<tbody>
				<tr>
					<th><label id="exclude-users" for="excluded-users-multi-select"><?php \esc_html_e( 'Exclude the following users', 'wp-2fa' ); ?></label></th>
					<td>
			<?php } else { ?>
					<label for="excluded-users-multi-select"><?php \esc_html_e( 'Exclude the following users', 'wp-2fa' ); ?>
		<?php } ?>
						<fieldset>
							<div>
								<select multiple="multiple" id="excluded-users-multi-select" name="wp_2fa_policy[excluded_users][]" style=" display:none;width:<?php echo ( $setup_wizard ) ? '100' : '50'; ?>%">
								<?php
								$excluded_users = (array) WP2FA::get_wp2fa_setting( 'excluded_users' );
								foreach ( $excluded_users as $user ) {
									?>
									<option selected="selected" value="<?php echo \esc_attr( $user ); ?>"><?php echo \esc_html( $user ); ?></option>
									<?php
								}
								?>
								</select>
							</div>
							<?php
							if ( ! $setup_wizard ) {
								?>

							</td>
					</tr>
					<tr>
						<th><label for="excluded-roles-multi-select"><?php \esc_html_e( 'Exclude the following roles', 'wp-2fa' ); ?></label></th>
						<td>
							<p>
							<?php } else { ?>
							<br>
								<label for="excluded-roles-multi-select"><?php \esc_html_e( 'Exclude the following roles', 'wp-2fa' ); ?></label>
							<?php } ?>
									<select multiple="multiple" id="excluded-roles-multi-select" name="wp_2fa_policy[excluded_roles][]" style=" display:none;width:<?php echo ( $setup_wizard ) ? '100' : '50'; ?>%">
									<?php
									$all_roles      = WP_Helper::get_roles_wp();
									$excluded_roles = (array) WP2FA::get_wp2fa_setting( 'excluded_roles' );
									foreach ( $all_roles as $role => $role_name ) {
										$selected = '';
										if ( in_array( strtolower( $role ), $excluded_roles, true ) ) {
											$selected = 'selected="selected"';
										}
										?>
											<option <?php echo $selected;  // phpcs:ignore ?> value="<?php echo \esc_attr( strtolower( $role ) ); ?>"><?php echo \esc_html( $role_name ); ?></option>
											<?php
									}
									?>
									</select>
							<br>
								<?php if ( WP_Helper::is_multisite() ) { ?>
							<div style="margin-top:10px;">
								<input type="checkbox" name="wp_2fa_policy[superadmins-role-exclude]" id="superadmins-role-exclude" value="yes"
									<?php checked( WP2FA::get_wp2fa_setting( 'superadmins-role-exclude' ), 'yes' ); ?> />
								<label for="superadmins-role-exclude"><?php \esc_html_e( 'Also exclude users with super admin privilege', 'wp-2fa' ); ?></label>
							</div>
							<?php } ?>
						</fieldset>
							<?php
							if ( ! $setup_wizard ) {
								?>
					</td>
				</tr>
			</tbody>
		</table>
								<?php } ?>
			<?php
		}

		/**
		 * Which network sites to exclude (for multisite instal)
		 *
		 * @since 1.7.0
		 *
		 * @param boolean $setup_wizard - Boolean - is that first time wizard setup or settings page call.
		 *
		 * @return void
		 */
		public static function excluded_network_sites( $setup_wizard = false ) {
			?>
		<h3><?php \esc_html_e( 'Do you want to exclude all the users of a site from 2FA? ', 'wp-2fa' ); ?></h3>
			<p class="description">
				<?php \esc_html_e( 'If you are enforcing 2FA on all users but for some reason you do not want to enforce it on a specific sub site, specify the sub site name below:', 'wp-2fa' ); ?>
			</p>
			<?php
			if ( ! $setup_wizard ) {
				?>
				<table class="form-table js-enforcement-policy-section">
					<tbody>
						<tr>
							<th><label for="excluded-sites-multi-select"><?php \esc_html_e( 'Exclude the following sites', 'wp-2fa' ); ?></label></th>
							<td>
					<?php } ?>
								<fieldset>
								<?php
								if ( $setup_wizard ) {
									?>

								<div class="option-pill">
									<label for="excluded_sites_search"><?php \esc_html_e( 'Exclude the following sites', 'wp-2fa' ); ?>
								<?php } ?>
										<select multiple="multiple" id="excluded-sites-multi-select" name="wp_2fa_policy[excluded_sites][]" style=" display:none;width:<?php echo ( $setup_wizard ) ? '100' : '50'; ?>%">
										<?php
											$excluded_sites = (array) WP2FA::get_wp2fa_setting( 'excluded_sites' );
										if ( ! empty( $excluded_sites ) ) {
											foreach ( $excluded_sites as $site_id ) {
												$site = get_blog_details( $site_id )->blogname;
												?>
														<option selected="selected" value="<?php echo \esc_attr( $site_id ); ?>"><?php echo \esc_html( $site ); ?></option>
													<?php
											}
										}
										?>
										</select>
										<?php
										if ( $setup_wizard ) {
											?>
									</label>
								</div>
								<?php } ?>
								</fieldset>
									<?php
									if ( ! $setup_wizard ) {
										?>
							</td>
						</tr>
					</tbody>
				</table>
							<?php } ?>
			<?php
		}

		/**
		 * Set the grace period
		 *
		 * @since 1.7.0
		 *
		 * @param boolean $setup_wizard - Boolean - is that first time wizard setup or settings page call.
		 *
		 * @return void
		 */
		public static function grace_period( $setup_wizard = false ) {
			$grace_period = (int) WP2FA::get_wp2fa_setting( 'grace-period', true );
			/**
			 * Via that, you can change the grace period TTL.
			 *
			 * @param bool - Default at this point is true - no method is selected.
			 */
			$testing = apply_filters( WP_2FA_PREFIX . 'allow_grace_period_in_seconds', false );
			if ( $testing ) {
				$grace_max = 600;
			} else {
				$grace_max = 10;
			}
			?>
		<fieldset class="contains-hidden-inputs">
			<label for="no-grace-period" style="margin-bottom: 10px; display: block;">
				<input type="radio" name="wp_2fa_policy[grace-policy]" id="no-grace-period" value="no-grace-period"
				<?php checked( WP2FA::get_wp2fa_setting( 'grace-policy' ), 'no-grace-period' ); ?>
				>
			<span><?php \esc_html_e( 'Users have to configure 2FA straight away.', 'wp-2fa' ); ?></span>
			</label>

			<label for="use-grace-period">
				<input type="radio" name="wp_2fa_policy[grace-policy]" id="use-grace-period" value="use-grace-period"
				<?php checked( WP2FA::get_wp2fa_setting( 'grace-policy' ), 'use-grace-period' ); ?>
				data-unhide-when-checked=".grace-period-inputs">
				<span><?php \esc_html_e( 'Give users a grace period to configure 2FA', 'wp-2fa' ); ?></span>
			</label>
			<fieldset class="hidden grace-period-inputs">
				<br/>
				<input type="number" id="grace-period" name="wp_2fa_policy[grace-period]" value="<?php echo \esc_attr( $grace_period ); ?>" min="1" max="<?php echo \esc_attr( $grace_max ); ?>">
				<label class="radio-inline">
					<input class="js-nested" type="radio" name="wp_2fa_policy[grace-period-denominator]" value="hours"
					<?php checked( WP2FA::get_wp2fa_setting( 'grace-period-denominator' ), 'hours' ); ?>
					>
					<?php \esc_html_e( 'hours', 'wp-2fa' ); ?>
				</label>
				<label class="radio-inline">
					<input class="js-nested" type="radio" name="wp_2fa_policy[grace-period-denominator]" value="days"
					<?php checked( WP2FA::get_wp2fa_setting( 'grace-period-denominator' ), 'days' ); ?>
					>
					<?php \esc_html_e( 'days', 'wp-2fa' ); ?>
				</label>
				<?php
					/**
					 * Fires after the grace period. Gives the ability to change the parsed code.
					 *
					 * @param string $content - HTML content.
					 * @param string $role - The name of the role.
					 * @param string $name_prefix - Name prefix for the input name, includes the role name if provided.
					 * @param string $data_role - Data attribute - used by the JS.
					 * @param string $role_id - The role name, used to identify the inputs.
					 *
					 * @since 2.0.0
					 */
					$after_grace_content = \apply_filters( WP_2FA_PREFIX . 'after_grace_period', '', '', 'wp_2fa_policy' );
					echo $after_grace_content; // phpcs:ignore
				?>
				<?php
				/**
				 * Via that, you can change the grace period TTL.
				 *
				 * @param bool - Default at this point is true - no method is selected.
				 */
				$testing = apply_filters( WP_2FA_PREFIX . 'allow_grace_period_in_seconds', false );
				if ( $testing ) {
					?>
					<label class="radio-inline">
						<input class="js-nested" type="radio" name="wp_2fa_policy[grace-period-denominator]" value="seconds"
						<?php checked( WP2FA::get_wp2fa_setting( 'grace-period-denominator' ), 'seconds' ); ?>
						>
						<?php \esc_html_e( 'Seconds', 'wp-2fa' ); ?>
					</label>
					<?php
				}

				if ( $setup_wizard ) {
					$user                         = wp_get_current_user();
					$last_user_to_update_settings = $user->ID;

					?>
				<input type="hidden" id="2fa_main_user" name="wp_2fa_policy[2fa_settings_last_updated_by]" value="<?php echo \esc_attr( $last_user_to_update_settings ); ?>">
				<?php } else { ?>
					<p><?php \esc_html_e( 'Note: If users do not configure it within the configured stipulated time, their account will be locked and have to be unlocked manually.', 'wp-2fa' ); ?></p>
				<?php } ?>
			</fieldset>
			<br/>
		</fieldset>
			<?php
		}
	}
}
includes/classes/Admin/Views/class-passord-reset-2fa.php000064400000012411150755130600017212 0ustar00<?php
/**
 * Roles and main settings password reset class.
 *
 * @package    wp2fa
 * @subpackage views
 *
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 *
 * @see       https://wordpress.org/plugins/wp-2fa/
 */

declare(strict_types=1);

namespace WP2FA\Admin\Views;

use WP2FA\Admin\Controllers\Settings;
use WP2FA\Extensions\RoleSettings\Role_Settings_Controller;

if ( ! class_exists( '\WP2FA\Admin\Views\Password_Reset_2FA' ) ) {
	/**
	 * Password_Reset_2FA - Class for rendering the plugin settings related to 2fa when  user resets the password.
	 *
	 * @since 2.5.0
	 */
	class Password_Reset_2FA {
		public const PASSWORD_RESET_SETTINGS_NAME = 'password-reset-2fa-show';

		public const ENABLED_SETTING_VALUE = 'password-reset-2fa';

		/**
		 * Inits all the class related hooks.
		 *
		 * @return void
		 *
		 * @since 2.5.0
		 */
		public static function init() {
			if ( is_admin() ) {
				\add_filter( WP_2FA_PREFIX . 'before_grace_period', array( __CLASS__, 'password_reset_setting' ), 10, 5 );
				\add_filter( WP_2FA_PREFIX . 'loop_settings', array( __CLASS__, 'add_setting_value' ) );
			}
			\add_filter( WP_2FA_PREFIX . 'default_settings', array( __CLASS__, 'add_default_settings' ) );
		}

		/**
		 * Shows the settings for the grace period notifications behavior.
		 *
		 * @param string $role        - The name of the role.
		 * @param string $name_prefix - Name prefix for the input name, includes the role name if provided.
		 * @param string $data_role   - Data attribute - used by the JS.
		 * @param string $role_id     - The role name, used to identify the inputs.
		 *
		 * @return string
		 *
		 * @since 2.5.0
		 */
		public static function reset_settings( string $role = '', string $name_prefix = '', string $data_role = '', string $role_id = '' ) {
			ob_start();

			if ( class_exists( 'WP2FA\Extensions\RoleSettings\Role_Settings_Controller' ) ) {
				$password_reset_action = Role_Settings_Controller::get_setting( $role, self::PASSWORD_RESET_SETTINGS_NAME, true );
			} else {
				$password_reset_action = Settings::get_role_or_default_setting( self::PASSWORD_RESET_SETTINGS_NAME, null, null, true );
			}
			?>
			<div class="sub-setting-indent">
				<fieldset>
					<label for="<?php echo \esc_attr( self::ENABLED_SETTING_VALUE ); ?><?php echo \esc_attr( $role_id ); ?>" style="margin-bottom: 10px; display: inline-block;">
						<input type="checkbox" name="<?php echo \esc_attr( $name_prefix ); ?>[<?php echo \esc_attr( self::PASSWORD_RESET_SETTINGS_NAME ); ?>]" 
						id="<?php echo \esc_attr( self::ENABLED_SETTING_VALUE ); ?><?php echo \esc_attr( $role_id ); ?>" 
						<?php echo $data_role; // phpcs:ignore?> 
						value="<?php echo \esc_attr( self::ENABLED_SETTING_VALUE ); ?>" <?php checked( $password_reset_action, self::ENABLED_SETTING_VALUE ); ?> class="js-nested">
						<span><?php echo \esc_html__( 'Require 2FA on password reset', 'wp-2fa' ); ?></span>
					</label>
				</fieldset>
			</div>
			<?php
			$html_content = ob_get_contents();
			ob_end_clean();

			return $html_content;
		}

		/**
		 * Adds global plugin setting options.
		 *
		 * @param array $loop_settings - Array with current plugin settings.
		 *
		 * @return array
		 *
		 * @since 2.5.0
		 */
		public static function add_setting_value( array $loop_settings ) {
			$loop_settings[] = self::PASSWORD_RESET_SETTINGS_NAME;

			return $loop_settings;
		}

		/**
		 * Adds the extension default settings to the main plugin settings.
		 *
		 * @param array $default_settings - array with plugin default settings.
		 *
		 * @return array
		 *
		 * @since 2.5.0
		 */
		public static function add_default_settings( array $default_settings ) {
			$default_settings[ self::PASSWORD_RESET_SETTINGS_NAME ] = self::PASSWORD_RESET_SETTINGS_NAME;

			return $default_settings;
		}

		/**
		 * Password reset settings.
		 *
		 * @param string $content     - HTML content.
		 * @param string $role        - The name of the role.
		 * @param string $name_prefix - Name prefix for the input name, includes the role name if provided.
		 * @param string $data_role   - Data attribute - used by the JS.
		 * @param string $role_id     - The role name, used to identify the inputs.
		 *
		 * @return string
		 *
		 * @since 2.5.0
		 */
		public static function password_reset_setting( string $content, string $role = '', string $name_prefix = '', string $data_role = '', string $role_id = '' ) {
			ob_start();
			?>
		<h3><?php \esc_html_e( 'Do you want to require 2FA when users reset their password?', 'wp-2fa' ); ?></h3>
		<p class="description">
			<?php \esc_html_e( 'When you enable this setting users will be required to enter a one-time code sent to them via email when resetting the password.', 'wp-2fa' ); ?>
		</p>

		<table class="form-table">
			<tbody>
				<tr>
					<th><label for="<?php echo \esc_attr( self::ENABLED_SETTING_VALUE ); ?><?php echo \esc_attr( $role_id ); ?>"><?php \esc_html_e( 'Password reset', 'wp-2fa' ); ?></label></th>
					<td>
					<fieldset class="contains-hidden-inputs">
					<?php echo self::reset_settings( $role, $name_prefix, $data_role, $role_id ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
					</fieldset>
					</td>
				</tr>
			</tbody>
		</table>
			<?php

			$content .= ob_get_contents();
			ob_end_clean();

			return $content;
		}
	}
}
includes/classes/Admin/Views/class-grace-period-notifications.php000064400000012233150755130600021163 0ustar00<?php
/**
 * Roles and main settings grace period notifications class.
 *
 * @package    wp2fa
 * @subpackage views
 *
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 *
 * @see       https://wordpress.org/plugins/wp-2fa/
 */

declare(strict_types=1);

namespace WP2FA\Admin\Views;

use WP2FA\Admin\Controllers\Settings;
use WP2FA\Extensions\RoleSettings\Role_Settings_Controller;

if ( ! class_exists( '\WP2FA\Admin\Views\Grace_Period_Notifications' ) ) {
	/**
	 * Grace_Period_Notifications - Class for rendering the grace period notification settings.
	 *
	 * @since 2.5.0
	 */
	class Grace_Period_Notifications {
		public const GRACE_PERIOD_NOTIFICATION_SETTINGS_NAME = 'grace-policy-notification-show';

		/**
		 * Inits all the class related hooks.
		 *
		 * @return void
		 *
		 * @since 2.5.0
		 */
		public static function init() {

			if ( is_admin() ) {

				\add_filter( WP_2FA_PREFIX . 'after_grace_period', array( __CLASS__, 'grace_period_notification_settings' ), 11, 5 );
				\add_filter( WP_2FA_PREFIX . 'loop_settings', array( __CLASS__, 'add_setting_value' ) );
			}
			\add_filter( WP_2FA_PREFIX . 'default_settings', array( __CLASS__, 'add_default_settings' ) );
		}

		/**
		 * Shows the settings for the grace period notifications behavior.
		 *
		 * @param string $content     - HTML content.
		 * @param string $role        - The name of the role.
		 * @param string $name_prefix - Name prefix for the input name, includes the role name if provided.
		 * @param string $data_role   - Data attribute - used by the JS.
		 * @param string $role_id     - The role name, used to identify the inputs.
		 *
		 * @return string
		 *
		 * @since 2.5.0
		 */
		public static function grace_period_notification_settings( string $content, string $role = '', string $name_prefix = '', string $data_role = '', string $role_id = '' ) {
			ob_start();

			if ( class_exists( 'WP2FA\Extensions\RoleSettings\Role_Settings_Controller' ) ) {
				$expire_action = Role_Settings_Controller::get_setting( $role, self::GRACE_PERIOD_NOTIFICATION_SETTINGS_NAME, true );
			} else {
				$expire_action = Settings::get_role_or_default_setting( self::GRACE_PERIOD_NOTIFICATION_SETTINGS_NAME, null, null, true );
			}
			?>
			<div class="sub-setting-indent">
				<p class="description" style="margin-top: 15px; margin-bottom: 8px;">
					<?php echo \esc_html__( 'How do you want users to be informed they are enforced to setup 2FA?', 'wp-2fa' ); ?>
				</p>
				<fieldset>
					<label for="dashboard-notification<?php echo \esc_attr( $role_id ); ?>" style="margin-bottom: 10px; display: inline-block;">
						<input type="radio" name="<?php echo \esc_attr( $name_prefix ); ?>[<?php echo \esc_attr( self::GRACE_PERIOD_NOTIFICATION_SETTINGS_NAME ); ?>]" 
						id="dashboard-notification<?php echo \esc_attr( $role_id ); ?>" 
						<?php echo $data_role; // phpcs:ignore?> 
						value="dashboard-notification" <?php checked( $expire_action, 'dashboard-notification' ); ?> class="js-nested">
						<span><?php echo \esc_html__( 'Show an admin notice in the dashboard', 'wp-2fa' ); ?></span>
					</label>

					<br>
					<div style="clear:both">
					<label for="after-login-notification<?php echo \esc_attr( $role_id ); ?>">
						<input type="radio" name="<?php echo \esc_attr( $name_prefix ); ?>[<?php echo \esc_attr( self::GRACE_PERIOD_NOTIFICATION_SETTINGS_NAME ); ?>]" <?php checked( $expire_action, 'after-login-notification' ); ?> 
						id="after-login-notification<?php echo \esc_attr( $role_id ); ?>"
						<?php echo $data_role; // phpcs:ignore?> 
						value="after-login-notification" <?php checked( $expire_action, 'after-login-notification' ); ?> class="js-nested">
						<span><?php echo \esc_html__( 'Show a notification on a page on its own after the user authenticates and before accessing the dashboard', 'wp-2fa' ); ?></span>
					</label>
					</div>
				</fieldset>
			</div>
			<?php
			$html_content = ob_get_contents();
			ob_end_clean();

			return $content . $html_content;
		}

		/**
		 * Adds global plugin setting options.
		 *
		 * @param array $loop_settings - Array with current plugin settings.
		 *
		 * @return array
		 *
		 * @since 2.5.0
		 */
		public static function add_setting_value( array $loop_settings ) {
			$loop_settings[] = self::GRACE_PERIOD_NOTIFICATION_SETTINGS_NAME;

			return $loop_settings;
		}

		/**
		 * Checks the grace policy setting for the given user.
		 *
		 * @param \WP_User $user - The user for which we have to check the settings.
		 *
		 * @return bool
		 *
		 * @since 2.5.0
		 */
		public static function notify_using_dashboard( \WP_User $user ) {
			if ( 'dashboard-notification' !== Settings::get_role_or_default_setting( self::GRACE_PERIOD_NOTIFICATION_SETTINGS_NAME, $user ) ) {
				return false;
			}

			return true;
		}

		/**
		 * Adds the extension default settings to the main plugin settings
		 *
		 * @param array $default_settings - array with plugin default settings.
		 *
		 * @return array
		 *
		 * @since 2.5.0
		 */
		public static function add_default_settings( array $default_settings ) {
			$default_settings[ self::GRACE_PERIOD_NOTIFICATION_SETTINGS_NAME ] = 'after-login-notification';
			return $default_settings;
		}
	}
}
includes/classes/Admin/Views/class-re-login-2fa.php000064400000013113150755130600016133 0ustar00<?php
/**
 * Roles and main settings user login again after 2FA setup class.
 *
 * @package    wp2fa
 * @subpackage views
 *
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 *
 * @see       https://wordpress.org/plugins/wp-2fa/
 */

declare(strict_types=1);

namespace WP2FA\Admin\Views;

use WP2FA\Admin\Controllers\Settings;
use WP2FA\Extensions\RoleSettings\Role_Settings_Controller;

if ( ! class_exists( '\WP2FA\Admin\Views\Re_Login_2FA' ) ) {
	/**
	 * Re_Login_2FA - Class for rendering the plugin settings related to 2fa when user sets the 2FA method.
	 *
	 * @since 2.7.0
	 */
	class Re_Login_2FA {
		public const RE_LOGIN_SETTINGS_NAME = 're-login-2fa-show';

		public const ENABLED_SETTING_VALUE = 're-login-2fa';

		/**
		 * Inits all the class related hooks.
		 *
		 * @return void
		 *
		 * @since 2.7.0
		 */
		public static function init() {
			if ( is_admin() ) {
				\add_filter( WP_2FA_PREFIX . 'before_grace_period', array( __CLASS__, 're_login_setting' ), 11, 5 );
				\add_filter( WP_2FA_PREFIX . 'loop_settings', array( __CLASS__, 'add_setting_value' ) );
				\add_action( 'wp_ajax_custom_ajax_logout', array( __CLASS__, 'redirect_after_logout' ) );
			}
			\add_filter( WP_2FA_PREFIX . 'default_settings', array( __CLASS__, 'add_default_settings' ) );
		}

		/**
		 * Logs out the current user and sends success message to the ajax request.
		 *
		 * @return void
		 *
		 * @since 2.7.0
		 */
		public static function redirect_after_logout() {
			\wp_logout();
			ob_clean(); // probably overkill for this, but good habit.
			\wp_send_json_success();
		}

		/**
		 * Shows the settings for the grace period notifications behavior.
		 *
		 * @param string $role        - The name of the role.
		 * @param string $name_prefix - Name prefix for the input name, includes the role name if provided.
		 * @param string $data_role   - Data attribute - used by the JS.
		 * @param string $role_id     - The role name, used to identify the inputs.
		 *
		 * @return string
		 *
		 * @since 2.7.0
		 */
		public static function reset_settings( string $role = '', string $name_prefix = '', string $data_role = '', string $role_id = '' ) {
			ob_start();

			if ( class_exists( 'WP2FA\Extensions\RoleSettings\Role_Settings_Controller' ) ) {
				$password_reset_action = Role_Settings_Controller::get_setting( $role, self::RE_LOGIN_SETTINGS_NAME, true );
			} else {
				$password_reset_action = Settings::get_role_or_default_setting( self::RE_LOGIN_SETTINGS_NAME, null, null, true );
			}
			?>
			<div class="sub-setting-indent">
				<fieldset>
					<label for="<?php echo \esc_attr( self::ENABLED_SETTING_VALUE ); ?><?php echo \esc_attr( $role_id ); ?>" style="margin-bottom: 10px; display: inline-block;">
						<input type="checkbox" name="<?php echo \esc_attr( $name_prefix ); ?>[<?php echo \esc_attr( self::RE_LOGIN_SETTINGS_NAME ); ?>]" 
						id="<?php echo \esc_attr( self::ENABLED_SETTING_VALUE ); ?><?php echo \esc_attr( $role_id ); ?>" 
						<?php echo $data_role; // phpcs:ignore?> 
						value="<?php echo \esc_attr( self::ENABLED_SETTING_VALUE ); ?>" <?php checked( $password_reset_action, self::ENABLED_SETTING_VALUE ); ?> class="js-nested">
						<span><?php echo \esc_html__( 'Log out user after 2FA setup', 'wp-2fa' ); ?></span>
					</label>
				</fieldset>
			</div>
			<?php
			$html_content = ob_get_contents();
			ob_end_clean();

			return $html_content;
		}

		/**
		 * Adds global plugin setting options.
		 *
		 * @param array $loop_settings - Array with current plugin settings.
		 *
		 * @return array
		 *
		 * @since 2.7.0
		 */
		public static function add_setting_value( array $loop_settings ) {
			$loop_settings[] = self::RE_LOGIN_SETTINGS_NAME;

			return $loop_settings;
		}

		/**
		 * Adds the extension default settings to the main plugin settings.
		 *
		 * @param array $default_settings - array with plugin default settings.
		 *
		 * @return array
		 *
		 * @since 2.7.0
		 */
		public static function add_default_settings( array $default_settings ) {
			$default_settings[ self::RE_LOGIN_SETTINGS_NAME ] = self::RE_LOGIN_SETTINGS_NAME;

			return $default_settings;
		}

		/**
		 * Password reset settings.
		 *
		 * @param string $content     - HTML content.
		 * @param string $role        - The name of the role.
		 * @param string $name_prefix - Name prefix for the input name, includes the role name if provided.
		 * @param string $data_role   - Data attribute - used by the JS.
		 * @param string $role_id     - The role name, used to identify the inputs.
		 *
		 * @return string
		 *
		 * @since 2.7.0
		 */
		public static function re_login_setting( string $content, string $role = '', string $name_prefix = '', string $data_role = '', string $role_id = '' ) {
			ob_start();
			?>
		<h3><?php \esc_html_e( 'Do you want to logout users after setting up 2FA on their account?', 'wp-2fa' ); ?></h3>
		<p class="description">
			<?php \esc_html_e( 'When you enable this setting users will be logged out automatically after configuring 2FA and they will need to log back in.', 'wp-2fa' ); ?>
		</p>

		<table class="form-table">
			<tbody>
				<tr>
					<th><label for="<?php echo \esc_attr( self::ENABLED_SETTING_VALUE ); ?><?php echo \esc_attr( $role_id ); ?>"><?php \esc_html_e( 'Re-login', 'wp-2fa' ); ?></label></th>
					<td>
					<fieldset class="contains-hidden-inputs">
					<?php echo self::reset_settings( $role, $name_prefix, $data_role, $role_id ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
					</fieldset>
					</td>
				</tr>
			</tbody>
		</table>
			<?php

			$content .= ob_get_contents();
			ob_end_clean();

			return $content;
		}
	}
}
includes/classes/Admin/class-user-listing.php000064400000016315150755130600015310 0ustar00<?php
/**
 * Responsible for user listing in admin manipulation.
 *
 * @package    wp2fa
 * @subpackage user-utils
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 * @link       https://wordpress.org/plugins/wp-2fa/
 */

declare(strict_types=1);

namespace WP2FA\Admin;

use WP2FA\Utils\User_Utils;
use WP2FA\Admin\Helpers\User_Helper;
use WP2FA\Extensions\TrustedDevices\Core;

defined( 'ABSPATH' ) || exit; // Exit if accessed directly.

/**
 * User_Listing class with user listing filters
 */
if ( ! class_exists( '\WP2FA\Admin\User_Listing' ) ) {

	/**
	 * User_Listing - Shows extra column in user table wit WP2FA status forevery user
	 */
	class User_Listing {

		/**
		 * The users table column name
		 *
		 * @var string
		 */
		private static $column_name = '2fa-status';

		/**
		 * Inits all the hooks used for showing the extra user data in the users column
		 *
		 * @return void
		 */
		public static function init() {
			\add_filter( 'manage_users_columns', array( __CLASS__, 'add_wp_2fa_column' ) );
			\add_filter( 'wpmu_users_columns', array( __CLASS__, 'add_wp_2fa_column' ) );
			\add_filter( 'manage_users_custom_column', array( __CLASS__, 'show_column_data' ), 10, 3 );
			\add_filter( 'bulk_actions-users', array( __CLASS__, 'add_bulk_action' ), 10, 1 );
			\add_filter( 'handle_bulk_actions-users', array( __CLASS__, 'handle_bulk_actions' ), 10, 3 );
			\add_action( 'admin_notices', array( __CLASS__, 'show_admin_notice' ) );
			\add_filter( 'user_row_actions', array( __CLASS__, 'add_users_hover' ), 10, 2 );
		}

		/**
		 * Sets the column in the admin users table
		 *
		 * @param array $columns - Array with all the columns.
		 *
		 * @return array
		 */
		public static function add_wp_2fa_column( array $columns ): array {
			$columns[ self::$column_name ] = __( '2FA Status', 'wp-2fa' );
			return $columns;
		}

		/**
		 * Shows the user WP 2FA status data in the users table
		 *
		 * @param [type] $value - The value of the column.
		 * @param string $column_name - The name of the column.
		 * @param int    $user_id - the ID of the user.
		 *
		 * @return mixed
		 */
		public static function show_column_data( $value, string $column_name, $user_id ) {

			switch ( $column_name ) {
				case self::$column_name:
					return self::get_user2fa_status( $user_id );
				default:
			}

			return $value;
		}

		/**
		 * Retrieves the translated 2FA status label for given user.
		 *
		 * This is performance optimized version that bypasses the User class on purpose. It loads the 2FA status meta
		 * field directly and turns it into a label.
		 *
		 * There is also some temporary code to figure out the 2FA status meta field if it doesn't exist. This will be
		 * removed in future versions and exist purely so we don't end up with no values in the column after migration
		 * to version 1.7.0 when this was introduced.
		 *
		 * @param int $user_id - The id of the user for which the info should be extracted.
		 *
		 * @return string
		 *
		 * @since 1.7.0
		 */
		private static function get_user2fa_status( $user_id ) {
			// try to get the user status "id" from user's meta data.
			$status_meta_value = User_Helper::get_2fa_status( $user_id );
			if ( ! empty( $status_meta_value ) ) {
				// the status id is available, grab the label to display.
				$status_data = User_Utils::extract_statuses( array( $status_meta_value ) );
				if ( ! empty( $status_data ) ) {
					return $status_data['label'];
				}
			}

			// If the user status is not saved in user meta (this can be the case prior to version 1.7.0), we figure it
			// out and store it against the user in DB. This is not ideal in terms of performance and this is only
			// a temporary solution.
			// @todo remove this in future versions.
			return User_Helper::set_user_status( new \WP_User( $user_id ) );
		}

		/**
		 * Returns the users table column name
		 *
		 * @return string
		 */
		public static function get_column_name(): string {
			return self::$column_name;
		}

		/**
		 * Adds bulk action to the WP users menu
		 *
		 * @param array $bulk_actions - Array of bulk actions.
		 *
		 * @return array
		 *
		 * @since 2.2.2
		 */
		public static function add_bulk_action( $bulk_actions ) {
			$bulk_actions['remove-2fa']         = __( 'Remove 2FA', 'wp-2fa' );
			$bulk_actions['remove-2fa-trusted'] = __( 'Reset list of 2FA trusted devices', 'wp-2fa' );

			return $bulk_actions;
		}

		/**
		 * Removes the 2fa from the list of the selected users.
		 *
		 * @param string $redirect_url - The redirect URL to redirect to when action is performed.
		 * @param string $action - The action to perform.
		 * @param array  $user_ids - The user IDs to remove from.
		 *
		 * @return string
		 *
		 * @since 2.2.2
		 */
		public static function handle_bulk_actions( $redirect_url, $action, $user_ids ) {
			if ( 'remove-2fa' === $action ) {
				if ( is_array( $user_ids ) ) {
					foreach ( $user_ids as $user_id ) {
						User_Helper::remove_2fa_for_user(
							$user_id
						);
					}
					$num_of_ids = count( $user_ids );
				} else {
					User_Helper::remove_2fa_for_user(
						$user_ids
					);
					$num_of_ids = 1;
				}
				$redirect_url = add_query_arg( '2fa-removed', $num_of_ids, $redirect_url );
			}

			if ( class_exists( '\WP2FA\Extensions\TrustedDevices\Core' ) && 'remove-2fa-trusted' === $action ) {
				if ( is_array( $user_ids ) ) {
					Core::remove_trusted_devices_for_users(
						$user_ids
					);
					$num_of_ids = count( $user_ids );
				} else {
					Core::remove_trusted_devices_for_users(
						array( $user_ids )
					);
					$num_of_ids = 1;
				}
				$redirect_url = add_query_arg( '2fa-trusted-removed', $num_of_ids, $redirect_url );
			}
			return $redirect_url;
		}

		/**
		 * Adds links to the on hover state of the users table row
		 *
		 * @param array    $actions - Array with all the actions for the current row.
		 * @param \WP_User $user_object - The user object from the current row.
		 *
		 * @return array
		 *
		 * @since 2.4.0
		 */
		public static function add_users_hover( $actions, $user_object ): array {
			if ( class_exists( '\WP2FA\Extensions\TrustedDevices\Core' ) ) {
				$actions['remove-2fa-trusted'] = "<a class='resetpassword' href='" . \wp_nonce_url( "users.php?action=remove-2fa-trusted&amp;users=$user_object->ID", 'bulk-users' ) . "'>" . __( 'Reset list of 2FA trusted devices', 'wp-2fa' ) . '</a>';
			}
			return $actions;
		}

		/**
		 * Handles the Admin notice for the users removed 2FA.
		 *
		 * @return void
		 *
		 * @since 2.2.2
		 */
		public static function show_admin_notice() {
			if ( ! empty( $_REQUEST['2fa-removed'] ) ) {
				$num_changed = (int) $_REQUEST['2fa-removed'];
				printf(
					'<div id="message" class="updated notice is-dismissable"><p>' .
					// translators: The number of the affected users.
					\esc_html__( 'Removed 2FA from %d users.', 'wp-2fa' ) .
					'</p></div>',
					(int) $num_changed
				);
			}
			if ( ! empty( $_REQUEST['2fa-trusted-removed'] ) ) {
				$num_changed = (int) $_REQUEST['2fa-trusted-removed'];
				printf(
					'<div id="message" class="updated notice is-dismissable"><p>' .
					// translators: The number of the affected users.
					\esc_html__( 'Removed 2FA trusted devices from %d users.', 'wp-2fa' ) .
					'</p></div>',
					(int) $num_changed
				);
			}
		}
	}
}
includes/classes/Admin/Helpers/class-classes-helper.php000064400000011605150755130600017174 0ustar00<?php
/**
 * Responsible for the User's operations.
 *
 * @package    wp2fa
 * @subpackage helpers
 *
 * @since      2.4.0
 *
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 *
 * @see       https://wordpress.org/plugins/wp-2fa/
 */

declare(strict_types=1);

namespace WP2FA\Admin\Helpers;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

if ( ! class_exists( '\WP2FA\Admin\Helpers\Classes_Helper' ) ) {
	/**
	 * Responsible for the proper class loading.
	 */
	class Classes_Helper {
		/**
		 * Holds the classmap array for more info check @see autoload_classmap.php from the auto generated Composer file.
		 *
		 * @var array
		 *
		 * @since 2.4.0
		 */
		private static $class_map = array();

		/**
		 * Caches and returns the classmap structure of the plugin.
		 *
		 * @since 2.4.0
		 */
		public static function get_class_map(): array {
			if ( empty( self::$class_map ) ) {
				self::$class_map = require WP_2FA_PATH . 'vendor/composer/autoload_classmap.php';
			}

			return self::$class_map;
		}

		/**
		 * Returns the class by its filename. Checks if it exists and returns it as string. Returns false otherwise.
		 *
		 * @param string $file - The filename of the class to check.
		 *
		 * @return string|false
		 *
		 * @since 2.4.0
		 */
		public static function get_class_by_filename( string $file ) {
			if ( in_array( $file, self::get_class_map(), true ) ) {
				$class = array_search( $file, self::get_class_map(), true );

				if ( class_exists( $class ) ) {
					return $class;
				}
			}

			return false;
		}

		/**
		 * Extracts subclasses of the given class, optionally abstract classes could be included as well.
		 *
		 * @param string $current_class     - The calling class.
		 * @param string $base_class        - The class which subclasses should be extracted.
		 * @param bool   $exclude_abstracts - Should we exclude abstract classes.
		 *
		 * @since 2.4.0
		 */
		public static function get_subclasses_of_class( string $current_class, string $base_class, bool $exclude_abstracts = true ): array {
			$matching_classes = array();
			foreach ( array_keys( self::get_class_map() ) as $class_name ) {
				if ( $current_class !== $class_name && is_subclass_of( $class_name, $base_class ) ) {
					if ( $exclude_abstracts && ( false !== strpos( $class_name, 'Abstract' ) ) ) {
						continue;
					}
					$matching_classes[ $class_name ] = $class_name;
				}
			}

			return $matching_classes;
		}

		/**
		 * Returns all the classes which are part of the given namespace.
		 *
		 * @param string $extract_namespace - The extract_namespace to search for.
		 *
		 * @return array
		 *
		 * @since 2.4.0
		 */
		public static function get_classes_by_namespace( string $extract_namespace ) {
			if ( 0 === strpos( $extract_namespace, '\\' ) ) {
				$extract_namespace = ltrim( $extract_namespace, '\\' );
			}

			$extract_namespace = rtrim( $extract_namespace, '\\' );

			$term_upper = strtoupper( $extract_namespace );

			return array_filter(
				array_keys( self::get_class_map() ),
				function ( $found_class ) use ( $term_upper ) {
					$class_name = strtoupper( $found_class );

					/**
					 * Find class name, by finding the last occurrence of the \
					 * if it is false  (from the strrchr) then class does not belong to any namespace currently.
					 */
					$esc_position = strrchr( $class_name, '\\' );

					if ( false !== $esc_position ) {
						$class_name_no_ns = substr( $esc_position, 1 );
					} else {
						return false;
					}

					if ( $class_name_no_ns &&
						$term_upper . '\\' . $class_name_no_ns === $class_name &&
						false === strpos( $class_name, strtoupper( 'Abstract' ) ) &&
						false === strpos( $class_name, strtoupper( 'Interface' ) )
					) {
						return $found_class;
					}

					return false;
				}
			);
		}

		/**
		 * Search for classes by given term.
		 *
		 * @param string $term - The term to search for.
		 *
		 * @return array
		 *
		 * @since 2.4.0
		 */
		public static function get_classes_with_term( $term ) {
			$term_upper = strtoupper( $term );

			return array_filter(
				self::get_class_map(),
				function ( $found_class ) use ( $term_upper ) {
					$class_name = strtoupper( $found_class );
					if (
						false !== strpos( $class_name, $term_upper ) &&
						false === strpos( $class_name, strtoupper( 'Abstract' ) ) &&
						false === strpos( $class_name, strtoupper( 'Interface' ) )
					) {
						return $found_class;
					}

					return false;
				}
			);
		}

		/**
		 * Adds a class (or classes) to the class map.
		 *
		 * @param array $class_add - Array with class or classes to add.
		 *
		 * @return void
		 *
		 * @since 2.7.0
		 */
		public static function add_to_class_map( array $class_add ) {
			if ( empty( self::$class_map ) ) {
				self::$class_map = require WP_2FA_PATH . 'vendor/composer/autoload_classmap.php';
			}

			self::$class_map = \array_merge( self::$class_map, $class_add );
		}
	}
}
includes/classes/Admin/Helpers/class-file-writer.php000064400000052650150755130600016520 0ustar00<?php
/**
 * Responsible for the File writing operations
 *
 * @package    wp2fa
 * @subpackage helpers
 * @since      2.4.0
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 * @link       https://wordpress.org/plugins/wp-2fa/
 */

namespace WP2FA\Admin\Helpers;

defined( 'ABSPATH' ) || exit; // Exit if accessed directly.

/**
 * File writer settings class
 */
if ( ! class_exists( '\WP2FA\Admin\Helpers\File_Writer' ) ) {

	/**
	 * All the file operations must go trough this class.
	 *
	 * @since 2.4.0
	 */
	class File_Writer {

		public const SECRET_NAME = 'WP2FA_ENCRYPT_KEY';

		public const WP2FA_UPLOADS_DIR = 'wp-2fa-data';

		/**
		 * Saves a secret key in `wp-config.php`.
		 *
		 * @param string $secret The secret key to save.
		 *
		 * @return bool
		 *
		 * @since 2.4.0
		 */
		public static function save_secret_key( string $secret ): bool {
			if ( ! self::can_write_to_file( self::get_wp_config_file_path() ) ) {
				return false;
			}

			$file     = self::get_wp_config_file_path();
			$contents = self::read( $file );

			if ( false === $contents ) {
				return false;
			}

			\set_error_handler( // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_set_error_handler
				function ( $err_severity, $err_msg, $err_file, $err_line, array $err_context ) {
					throw new \Error( $err_msg, 0, $err_severity, $err_file, $err_line ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
				},
				E_WARNING
			);

			try {
				$current_secret = constant( self::SECRET_NAME );
			} catch ( \Error $e ) {
				$current_secret = null;
			}

			restore_error_handler();

			$matches_found = $current_secret ? substr_count( $contents, $current_secret ) : 0;

			if ( ! $current_secret || ! $matches_found ) {
				if ( substr_count( $contents, self::SECRET_NAME ) ) {

					$line_ending = self::get_line_ending( $contents );

					$contents = explode( $line_ending, $contents );

					foreach ( $contents as $key => $line ) {
						if ( stristr( $line, self::SECRET_NAME ) ) {
							unset( $contents[ $key ] );
						}
					}

					$contents = implode( $line_ending, array_values( $contents ) );
					self::write( $file, $contents );
				}
				self::write_wp_config( '/** WP 2FA plugin data encryption key. For more information please visit melapress.com */' . "\n" . 'define( \'' . self::SECRET_NAME . '\', \'' . $secret . '\' );' );
				return true;
			}

			if ( $matches_found > 1 ) {
				return false;
			}

			$replaced = str_replace( $current_secret, $secret, $contents );

			if ( ! $replaced ) {
				return false;
			}

			$written = self::write( $file, $replaced );

			if ( false === $written ) {
				return false;
			}

			return true;
		}

		/**
		 * Gets the permissions of given directory
		 *
		 * @param string $dir - The name of the directory to check.
		 *
		 * @return bool|int
		 *
		 * @since 2.4.0
		 */
		public static function get_permissions( string $dir ) {
			if ( ! is_dir( $dir ) ) {
				return false;
			}

			if ( ! PHP_Helper::is_callable( 'fileperms' ) ) {
				return false;
			}

			$dir = rtrim( $dir, '/' );
			// phpcs:ignore -- Have Tide ignore the following line. We use arguments that don't exist in early versions, but these versions ignore the arguments.
			@clearstatcache( true, $dir );

			return fileperms( $dir ) & 0777;
		}

		/**
		 * Writes a content to a given file
		 *
		 * @param string  $file - The file to write to.
		 * @param string  $contents - The contents of the file to write.
		 * @param boolean $append - Append the contents of the file or overwrite.
		 *
		 * @return mixed
		 *
		 * @since 2.4.0
		 */
		public static function write( string $file, string $contents, $append = false ) {
			$callable = array();

			if ( PHP_Helper::is_callable( 'fopen' ) && PHP_Helper::is_callable( 'fwrite' ) && PHP_Helper::is_callable( 'flock' ) ) {
				$callable[] = 'fopen';
			}
			if ( PHP_Helper::is_callable( 'file_put_contents' ) ) {
				$callable[] = 'file_put_contents';
			}

			if ( empty( $callable ) ) {
				return false;
			}

			if ( is_dir( $file ) ) {
				return false;
			}

			if ( ! is_dir( dirname( $file ) ) ) {
				$result = self::create_dir( dirname( $file ) );

				if ( false === $result ) {
					return false;
				}
			}

			$file_existed = is_file( $file );
			$success      = false;

			// Different permissions to try in case the starting set of permissions are prohibiting write.
			$trial_perms = array(
				false,
				0644,
				0664,
				0666,
			);

			foreach ( $trial_perms as $perms ) {
				if ( false !== $perms ) {
					if ( ! isset( $original_file_perms ) ) {
						$original_file_perms = self::get_permissions( $file );
					}

					self::chmod( $file, $perms );
				}

				if ( in_array( 'fopen', $callable, true ) ) {
					if ( $append ) {
						$mode = 'ab';
					} else {
						$mode = 'wb';
					}

					if ( false !== ( $fh = @fopen( $file, $mode ) ) ) { // phpcs:ignore -- Ignored the assignment on the same line
						flock( $fh, LOCK_EX );

						mbstring_binary_safe_encoding();

						$data_length   = strlen( $contents );
						$bytes_written = @fwrite( $fh, $contents ); // phpcs:ignore -- Ignored the error silencing

						reset_mbstring_encoding();

						@flock( $fh, LOCK_UN ); // phpcs:ignore -- Ignored the error silencing
						@fclose( $fh ); // phpcs:ignore -- Ignored the error silencing

						if ( $data_length === $bytes_written ) {
							$success = true;
						}
					}
				}

				if ( ! $success && in_array( 'file_put_contents', $callable, true ) ) {
					if ( $append ) {
						$flags = FILE_APPEND;
					} else {
						$flags = 0;
					}

					mbstring_binary_safe_encoding();

					$data_length   = strlen( $contents );
					$bytes_written = @file_put_contents( $file, $contents, $flags ); // phpcs:ignore -- Ignored the silencing warning

					reset_mbstring_encoding();

					if ( $data_length === $bytes_written ) {
						$success = true;
					}
				}

				if ( $success ) {
					if ( ! $file_existed ) {
						// Set default file permissions for the new file.
						self::chmod( $file, self::get_default_permissions() );
					} elseif ( isset( $original_file_perms ) && ! is_wp_error( $original_file_perms ) ) {
						// Reset the original file permissions if they were modified.
						self::chmod( $file, $original_file_perms );
					}

					return true;
				}

				if ( ! $file_existed ) {
					// If the file is new, there is no point attempting different permissions.
					break;
				}
			}

			return false;
		}

		/**
		 * Adds index.php and .htaccess files to the given directory
		 *
		 * @param string $dir - The directory to protect.
		 *
		 * @return bool
		 *
		 * @since 2.4.0
		 */
		public static function add_file_listing_protection( string $dir ) {
			$dir = rtrim( $dir, \DIRECTORY_SEPARATOR );

			if ( ! is_dir( $dir ) ) {
				return false;
			}

			if ( self::exists( $dir . \DIRECTORY_SEPARATOR . 'index.php' ) ) {
				return true;
			}

			return self::write( $dir . \DIRECTORY_SEPARATOR . '.htaccess', 'Deny from all' ) &&
			self::write( $dir . \DIRECTORY_SEPARATOR . 'index.php', "<?php\n// Silence is golden." );
		}

		/**
		 * Checks if given file exists
		 *
		 * @param string $file - The name of the file to check.
		 *
		 * @return bool
		 *
		 * @since 2.4.0
		 */
		public static function exists( string $file ): bool {

			@clearstatcache( true, $file ); // phpcs:ignore -- Have Tide ignore the following line. We use arguments that don't exist in early versions, but these versions ignore the arguments.

			return @file_exists( $file ); // phpcs:ignore -- Have Tide ignore the following line. We use arguments that don't exist in early versions, but these versions ignore the arguments.
		}

		/**
		 * Check the setting that allows writing files.
		 *
		 * @param string $filename - The name of the file and path.
		 *
		 * @since 2.4.0
		 *
		 * @return bool True if files can be written to, false otherwise.
		 */
		public static function can_write_to_file( string $filename ) {
			return is_writable( $filename );
		}

		/**
		 * Get full file path to the site's wp-config.php file.
		 *
		 * @since 2.4.0
		 *
		 * @return string Full path to the wp-config.php file or a blank string if modifications for the file are disabled.
		 */
		public static function get_wp_config_file_path() {

			if ( file_exists( ABSPATH . 'wp-config.php' ) ) {

				/** The config file resides in ABSPATH */
				$path = ABSPATH . 'wp-config.php';

			} elseif ( @file_exists( dirname( ABSPATH ) . '/wp-config.php' ) && ! @file_exists( dirname( ABSPATH ) . '/wp-settings.php' ) ) {

				/** The config file resides one level above ABSPATH */
				$path = dirname( ABSPATH ) . '/wp-config.php';

			} else {
				$path = '';
			}

			/**
			 * Gives the ability to manually change the path to the config file.
			 *
			 * @param string - The current value for WP config file path.
			 *
			 * @since 2.6.2
			 */
			$path = \apply_filters( WP_2FA_PREFIX . 'config_file_path', (string) $path );

			return $path;
		}

		/**
		 * Creates a directory structure
		 *
		 * @param string $dir - The directory to create.
		 *
		 * @return boolean
		 *
		 * @since 2.4.0
		 */
		public static function create_dir( string $dir ): bool {
			$dir = rtrim( $dir, '/' );

			if ( is_dir( $dir ) ) {
				self::add_file_listing_protection( $dir );

				return true;
			}

			if ( self::exists( $dir ) ) {
				return false;
			}

			if ( ! PHP_Helper::is_callable( 'mkdir' ) ) {
				return false;
			}

			$parent = dirname( $dir );

			while ( ! empty( $parent ) && ! is_dir( $parent ) && dirname( $parent ) !== $parent ) {
				$parent = dirname( $parent );
			}

			if ( empty( $parent ) ) {
				return false;
			}

			$perms = self::get_permissions( $parent );

			if ( ! is_int( $perms ) ) {
				$perms = self::get_default_permissions();
			}

			$cached_umask = umask( 0 );
			$result       = @mkdir( $dir, $perms, true ); // phpcs:ignore -- We don't want to have fatalities here.
			umask( $cached_umask );

			if ( $result ) {
				self::add_file_listing_protection( $dir );

				return true;
			}

			return false;
		}

		/**
		 * Retrieves the full path to plugin's working directory. Returns a folder path with a trailing slash. It also
		 * creates the folder unless the $skip_creation parameter is set to true.
		 *
		 * Default path is "{uploads folder}/self::WP2FA_UPLOADS_DIR/"
		 *
		 * @param string $path          Optional path relative to the working directory.
		 * @param bool   $skip_creation If true, the folder will not be created.
		 * @param bool   $ignore_site   If true, there will be no sub-site specific subfolder in multisite context.
		 *
		 * @return string|\WP_Error
		 *
		 * @since 2.6.0
		 */
		public static function get_upload_path( $path = '', $skip_creation = false, $ignore_site = false ) {
			$result = '';

			$upload_dir = wp_upload_dir( null, false );
			if ( is_array( $upload_dir ) && array_key_exists( 'basedir', $upload_dir ) ) {
				$result = $upload_dir['basedir'] . \DIRECTORY_SEPARATOR . self::WP2FA_UPLOADS_DIR . \DIRECTORY_SEPARATOR;
			} elseif ( defined( 'WP_CONTENT_DIR' ) ) {
				// Fallback in case there is a problem with filesystem.
				$result = WP_CONTENT_DIR . \DIRECTORY_SEPARATOR . 'uploads' . \DIRECTORY_SEPARATOR . self::WP2FA_UPLOADS_DIR . \DIRECTORY_SEPARATOR;
			}

			if ( empty( $result ) ) {
				// Empty result here means invalid custom path or a problem with WordPress (uploads folder issue or mission WP_CONTENT_DIR).
				return new \WP_Error( '2fa_uplaods_dir_missing', __( 'The base of WSAL working directory cannot be determined. Custom path is invalid or there is some other issue with your WordPress installation.', 'wp-2fa' ) );
			}

			// Append site specific subfolder in multisite context.
			if ( ! $ignore_site && WP_Helper::is_multisite() ) {
				$site_id = \get_current_blog_id();
				if ( $site_id > 0 ) {
					$result .= 'sites' . \DIRECTORY_SEPARATOR . $site_id . \DIRECTORY_SEPARATOR;
				}
			}

			// Append optional path passed as a parameter.
			if ( $path && is_string( $path ) ) {
				$result .= $path . \DIRECTORY_SEPARATOR;
			}

			if ( ! file_exists( $result ) ) {
				if ( ! $skip_creation ) {
					if ( ! \wp_mkdir_p( $result ) ) {
						return new \WP_Error(
							'mkdir_failed',
							sprintf(
								/* translators: %s: Directory path. */
								__( 'Unable to create directory %s. Is its parent directory writable by the server?', 'wp-2fa' ),
								esc_html( $result )
							)
						);
					}
				}

				self::add_file_listing_protection( $result );
			}

			return $result;
		}

		/**
		 * Remove the supplied file.
		 *
		 * @param string $file - The name of the file and path.
		 *
		 * @return bool|WP_Error Boolean true on success or a WP_Error object if an error occurs.
		 *
		 * @since 2.6.0
		 */
		public static function remove( $file ) {
			if ( ! self::exists( $file ) ) {
				return true;
			}

			if ( ! PHP_Helper::is_callable( 'unlink' ) ) {
				return new \WP_Error(
					'wp-2fa',
					// translators: the name of the file.
					sprintf( __( 'The file %s could not be removed as the unlink() function is disabled. This is a system configuration issue.', 'wp-2fa' ), $file )
				);
			}

			$result = @unlink( $file ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.unlink_unlink

			@clearstatcache( true, $file ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged

			if ( $result ) {
				return true;
			}

			return new \WP_Error(
				'wp-2fa',
				sprintf(
					// translators: the name of the file.
					__( 'Unable to remove %s due to an unknown error.', 'wp-2fa' ),
					$file
				)
			);
		}

		/**
		 * Gets the content of a file
		 *
		 * @param string $file - The name of the file.
		 *
		 * @return bool|string
		 *
		 * @since 2.4.0
		 */
		protected static function get_file_contents( string $file ) {
			if ( ! self::exists( $file ) ) {
				return '';
			}

			$contents = self::read( $file );

			if ( is_wp_error( $contents ) ) {
				return false;
			}

			return $contents;
		}

		/**
		 * Write the supplied modification to the wp-config.php file.
		 *
		 * @since 2.4.0
		 *
		 * @param string $modification - The modification to add to the wp-config.php file.
		 *
		 * @return bool
		 */
		private static function write_wp_config( $modification ) {
			$file_path = self::get_wp_config_file_path();

			return self::update( $file_path, $modification );
		}

		/**
		 * Updates the content of a file
		 *
		 * @param string $file - The name of the file to update.
		 * @param string $modification - The modification to be added to the file.
		 *
		 * @return boolean
		 *
		 * @since 2.4.0
		 */
		private static function update( string $file, string $modification ): bool {
			// Check to make sure that the settings give permission to write files.
			if ( ! self::can_write_to_file( $file ) ) {

				return false;
			}

			$contents = self::read( $file );

			if ( is_wp_error( $contents ) ) {
				return $contents;
			}

			if ( ! $contents ) {
				return false;
			}

			$modification = ltrim( $modification, "\x0B\r\n\0" );
			$modification = rtrim( $modification, " \t\x0B\r\n\0" );

			if ( empty( $modification ) ) {
				// If there isn't a new modification, write the content without any modification and return the result.

				if ( empty( $contents ) ) {
					$contents = PHP_EOL;
				}

				return false;
			}

			$placeholder = self::get_placeholder();

			// Ensure that the generated placeholder can be uniquely identified in the contents.
			while ( false !== strpos( $contents, $placeholder ) ) {
				$placeholder = self::get_placeholder();
			}

			// Put the placeholder at the beginning of the file, after the <?php tag.
			$contents = preg_replace( '/^(.*?<\?(?:php)?)\s*(?:\r\r\n|\r\n|\r|\n)/', "\${1}$placeholder", $contents, 1 );

			if ( false === strpos( $contents, $placeholder ) ) {
				$contents = preg_replace( '/^(.*?<\?(?:php)?)\s*(.+(?:\r\r\n|\r\n|\r|\n))/', "\${1}$placeholder$2", $contents, 1 );
			}

			if ( false === strpos( $contents, $placeholder ) ) {
				$contents = "<?php$placeholder?" . ">$contents";
			}

			// Pad away from existing sections when adding iThemes Security modifications.
			$line_ending = self::get_line_ending( $contents );

			while ( ! preg_match( "/(?:^|(?:(?<!\r)\n|\r(?!\n)|(?<!\r)\r\n|\r\r\n)(?:(?<!\r)\n|\r(?!\n)|(?<!\r)\r\n|\r\r\n))$placeholder/", $contents ) ) {
				$contents = preg_replace( "/$placeholder/", "$line_ending$placeholder", $contents );
			}
			while ( ! preg_match( "/$placeholder(?:$|(?:(?<!\r)\n|\r(?!\n)|(?<!\r)\r\n|\r\r\n)(?:(?<!\r)\n|\r(?!\n)|(?<!\r)\r\n|\r\r\n))/", $contents ) ) {
				$contents = preg_replace( "/$placeholder/", "$placeholder$line_ending", $contents );
			}

			// Ensure that the file ends in a newline if the placeholder is at the end.
			$contents = preg_replace( "/$placeholder$/", "$placeholder$line_ending", $contents );

			if ( ! empty( $modification ) ) {
				// Normalize line endings of the modification to match the file's line endings.
				$modification = self::normalize_line_endings( $modification, $line_ending );

				// Exchange the placeholder with the modification.
				$contents = preg_replace( "/$placeholder/", $modification, $contents );
			}

			// Write the new contents to the file and return the results.
			return self::write( $file, $contents );
		}

		/**
		 * Generates unique placeholder to be used in the string
		 *
		 * @return string
		 *
		 * @since 2.4.0
		 */
		private static function get_placeholder(): string {
			$characters = str_split( 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' );

			$string = '';

			for ( $x = 0; $x < 100; $x++ ) {
				$string .= array_rand( $characters );
			}

			return $string;
		}

		/**
		 * Returns to proper line endings of a given content
		 *
		 * @param string $contents - The text to be checked.
		 *
		 * @return string
		 *
		 * @since 2.4.0
		 */
		private static function get_line_ending( string $contents ) {
			if ( empty( $contents ) ) {
				return PHP_EOL;
			}

			$count["\n"]     = preg_match_all( "/(?<!\r)\n/", $contents, $matches );
			$count["\r"]     = preg_match_all( "/\r(?!\n)/", $contents, $matches );
			$count["\r\n"]   = preg_match_all( "/(?<!\r)\r\n/", $contents, $matches );
			$count["\r\r\n"] = preg_match_all( "/\r\r\n/", $contents, $matches );

			if ( 0 === array_sum( $count ) ) {
				return PHP_EOL;
			}

			$maxes = array_keys( $count, max( $count ), true );

			if ( in_array( "\r\r\n", $maxes, true ) ) {
				return "\r\r\n";
			}

			return $maxes[0];
		}

		/**
		 * Normalizing fileendings for different platforms
		 *
		 * @param string $content - The file content to be checked.
		 * @param string $line_ending - Line endings to be used.
		 *
		 * @return string
		 *
		 * @since 2.4.0
		 */
		private static function normalize_line_endings( string $content, string $line_ending = "\n" ): string {
			return preg_replace( '/(?<!\r)\n|\r(?!\n)|(?<!\r)\r\n|\r\r\n/', $line_ending, $content );
		}

		/**
		 * Reads the content of a file
		 *
		 * @param string $file - The file to read.
		 *
		 * @return bool|string
		 *
		 * @since 2.4.0
		 */
		private static function read( string $file ) {
			if ( ! is_file( $file ) ) {
				return false;
			}

			$callable = array();

			if ( PHP_Helper::is_callable( 'file_get_contents' ) ) {
				$callable[] = 'file_get_contents';
			}
			if ( PHP_Helper::is_callable( 'fopen' ) && PHP_Helper::is_callable( 'feof' ) && PHP_Helper::is_callable( 'fread' ) && PHP_Helper::is_callable( 'flock' ) ) {
				$callable[] = 'fopen';
			}

			if ( empty( $callable ) ) {
				return false;
			}

			$contents = false;

			// Different permissions to try in case the starting set of permissions are prohibiting read.
			$trial_perms = array(
				false,
				0644,
				0664,
				0666,
			);

			foreach ( $trial_perms as $perms ) {
				if ( false !== $perms ) {
					if ( ! isset( $original_file_perms ) ) {
						$original_file_perms = self::get_permissions( $file );
					}

					self::chmod( $file, $perms );
				}

				if ( in_array( 'fopen', $callable, true ) ) {
					if ( false !== ( $fh = fopen( $file, 'rb' ) ) ) { // phpcs:ignore -- Ignored the assigned on the same line error
						flock( $fh, LOCK_SH );

						$contents = '';

						while ( ! feof( $fh ) ) {
							$contents .= fread( $fh, 1024 ); // phpcs:ignore -- Ignored the file operation notification
						}

						flock( $fh, LOCK_UN );
						fclose( $fh ); // phpcs:ignore -- Ignored the file operation notification
					}
				}

				if ( ( false === $contents ) && in_array( 'file_get_contents', $callable, true ) ) {
					$contents = file_get_contents( $file );  // phpcs:ignore -- Ignored the wp_remote_get usage
				}

				if ( false !== $contents ) {
					if ( isset( $original_file_perms ) && is_int( $original_file_perms ) ) {
						// Reset the original file permissions if they were modified.
						self::chmod( $file, $original_file_perms );
					}

					return $contents;
				}
			}

			return false;
		}

		/**
		 * Changes the permissions of a file
		 *
		 * @param string $file - The file to change permissions to.
		 * @param mixed  $perms - The permissions to be set.
		 *
		 * @return bool
		 *
		 * @since 2.4.0
		 */
		private static function chmod( string $file, $perms ): bool {
			if ( ! is_int( $perms ) ) {
				return \CURLOPT_SSL_FALSESTART;
			}

			if ( ! PHP_Helper::is_callable( 'chmod' ) ) {
				return false;
			}

			return @chmod( $file, $perms ); // phpcs:ignore -- Don't need fatalities here.
		}

		/**
		 * Returns the default filesystem permissions
		 *
		 * @return integer
		 *
		 * @since 2.4.0
		 */
		private static function get_default_permissions() {

			$perms = self::get_permissions( ABSPATH );

			if ( ! is_wp_error( $perms ) ) {
				return $perms;
			}

			return 0755;
		}
	}
}
includes/classes/Admin/Helpers/class-wp-helper.php000064400000020263150755130600016165 0ustar00<?php
/**
 * Responsible for the WP core functionalities.
 *
 * @package    wp2fa
 * @subpackage helpers
 *
 * @since      2.2.0
 *
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 *
 * @see       https://wordpress.org/plugins/wp-2fa/
 */

namespace WP2FA\Admin\Helpers;

defined( 'ABSPATH' ) || exit; // Exit if accessed directly.

/*
 * WP helper class
 */
if ( ! class_exists( '\WP2FA\Admin\Helpers\WP_Helper' ) ) {
	/**
	 * All the WP functionality must go trough this class.
	 *
	 * @since 2.2.0
	 */
	class WP_Helper {
		/**
		 * Hold the user roles as array - Human readable is used for key of the array, and the internal role name is the value.
		 *
		 * @var array
		 *
		 * @since 2.2.0
		 */
		private static $user_roles = array();

		/**
		 * Hold the user roles as array - Internal role name is used for key of the array, and the human readable format is the value.
		 *
		 * @var array
		 *
		 * @since 2.2.0
		 */
		private static $user_roles_wp = array();

		/**
		 * Keeps the value of the multisite install of the WP.
		 *
		 * @var bool
		 *
		 * @since 2.2.0
		 */
		private static $is_multisite = null;

		/**
		 * Holds array with all the sites in multisite WP installation.
		 *
		 * @var array
		 */
		private static $sites = array();

		/**
		 * Inits the class, and fires all the necessarily methods.
		 *
		 * @return void
		 *
		 * @since 2.2.0
		 */
		public static function init() {
			if ( self::is_multisite() ) {
				\add_action( 'network_admin_notices', array( __CLASS__, 'show_critical_admin_notice' ) );
			} else {
				\add_action( 'admin_notices', array( __CLASS__, 'show_critical_admin_notice' ) );
			}
		}

		/**
		 * Checks if specific role exists.
		 *
		 * @param string $role - The name of the role to check.
		 *
		 * @since 2.2.0
		 */
		public static function is_role_exists( string $role ): bool {
			self::set_roles();

			if ( in_array( $role, self::$user_roles, true ) ) {
				return true;
			}

			return false;
		}

		/**
		 * Returns the currently available WP roles - the Human readable format is the key.
		 *
		 * @return array
		 *
		 * @since 2.2.0
		 */
		public static function get_roles() {
			self::set_roles();

			return self::$user_roles;
		}

		/**
		 * Returns the currently available WP roles.
		 *
		 * @return array
		 *
		 * @since 2.2.0
		 */
		public static function get_roles_wp() {
			if ( empty( self::$user_roles_wp ) ) {
				self::set_roles();
				self::$user_roles_wp = array_flip( self::$user_roles );
			}

			return self::$user_roles_wp;
		}

		/**
		 * Shows critical notices to the admin.
		 *
		 * @return void
		 *
		 * @since 2.2.0
		 */
		public static function show_critical_admin_notice() {
			if ( User_Helper::is_admin() ) {
				/*
				 * Gives the ability to show notices to the admins
				 */
				\do_action( WP_2FA_PREFIX . 'critical_notice' );
			}
		}

		/**
		 * Check is this is a multisite setup.
		 *
		 * @return bool
		 *
		 * @since 2.2.0
		 */
		public static function is_multisite() {
			if ( null === self::$is_multisite ) {
				self::$is_multisite = function_exists( 'is_multisite' ) && is_multisite();
			}

			return self::$is_multisite;
		}

		/**
		 * Collects all the sites from multisite WP installation.
		 *
		 * @since 2.5.0
		 */
		public static function get_multi_sites(): array {
			if ( self::is_multisite() ) {
				if ( empty( self::$sites ) ) {
					self::$sites = \get_sites();
				}

				return self::$sites;
			}

			return array();
		}

		/**
		 * Calculating the signature.
		 *
		 * @param array $data - Array with data to create a signature for.
		 *
		 * @since 2.2.2
		 */
		public static function calculate_api_signature( array $data ): string {
			$now   = new \DateTime( 'now', new \DateTimeZone( 'UTC' ) );
			$nonce = $now->getTimestamp();

			$pk_hash               = hash( 'sha512', $data['license_key'] . '|' . $nonce );
			$authentication_string = base64_encode( $pk_hash . '|' . $nonce );

			return $authentication_string;
		}

		/**
		 * Checks if that is the WP login page or not.
		 *
		 * @return bool
		 *
		 * @since 2.4.1
		 */
		public static function is_wp_login() {
			$abs_path = str_replace( array( '\\', '/' ), DIRECTORY_SEPARATOR, ABSPATH );

			if ( function_exists( 'is_account_page' ) && is_account_page() ) {
				// The user is on the WooCommerce login page.

				return true;
			}

			return ( in_array( $abs_path . 'wp-login.php', get_included_files() ) || in_array( $abs_path . 'wp-register.php', get_included_files() ) ) || ( isset( $GLOBALS['pagenow'] ) && 'wp-login.php' === $GLOBALS['pagenow'] ) || '/wp-login.php' == $_SERVER['PHP_SELF']; // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual, WordPress.Security.ValidatedSanitizedInput.InputNotValidated
		}

		/**
		 * Check whether we are on an admin and plugin page.
		 *
		 * @since 2.4.1
		 *
		 * @param array|string $slug ID(s) of a plugin page. Possible values: 'general', 'logs', 'about' or array of them.
		 *
		 * @return bool
		 */
		public static function is_admin_page( $slug = array() ) { // phpcs:ignore Generic.Metrics.NestingLevel.MaxExceeded

			$cur_page = isset( $_GET['page'] ) ? sanitize_key( $_GET['page'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
			$check    = WP_2FA_PREFIX_PAGE;

			return \is_admin() && ( false !== strpos( $cur_page, $check ) );
		}

		/**
		 * Remove all non-WP Mail SMTP plugin notices from our plugin pages.
		 *
		 * @since 2.4.1
		 */
		public static function hide_unrelated_notices() {
			// Bail if we're not on our screen or page.
			if ( ! self::is_admin_page() ) {
				return;
			}

			self::remove_unrelated_actions( 'user_admin_notices' );
			self::remove_unrelated_actions( 'admin_notices' );
			self::remove_unrelated_actions( 'all_admin_notices' );
			self::remove_unrelated_actions( 'network_admin_notices' );
		}

		/**
		 * Creates a nonce for HTML field by given name.
		 *
		 * @param string $nonce_name -The name of the nonce to create.
		 *
		 * @return string
		 *
		 * @since 2.6.0
		 */
		public static function create_data_nonce( string $nonce_name ): string {
			return ' data-nonce="' . \esc_attr( \wp_create_nonce( $nonce_name ) ) . '"';
		}

		/**
		 * Extracts the domain part of the given string.
		 *
		 * @param string $url_to_check - The URL string to be checked.
		 *
		 * @return string
		 *
		 * @since 2.6.0
		 */
		public static function extract_domain( string $url_to_check ): string {
			// get the full domain.
			// $urlparts = parse_url( \site_url() );.

			if ( false !== strpos( $url_to_check, '@' ) ) {
				$domain = \explode( '@', $url_to_check )[1];

				return $domain;
			}
			$urlparts = parse_url( $url_to_check );
			$domain   = $urlparts ['host'];

			// get the TLD and domain.
			$domainparts = explode( '.', $domain );
			$domain      = $domainparts[ count( $domainparts ) - 2 ] . '.' . $domainparts[ count( $domainparts ) - 1 ];

			return $domain;
		}

		/**
		 * Remove all non-WP Mail SMTP notices from the our plugin pages based on the provided action hook.
		 *
		 * @since 2.4.1
		 *
		 * @param string $action The name of the action.
		 */
		private static function remove_unrelated_actions( $action ) {
			global $wp_filter;

			if ( empty( $wp_filter[ $action ]->callbacks ) || ! is_array( $wp_filter[ $action ]->callbacks ) ) {
				return;
			}

			foreach ( $wp_filter[ $action ]->callbacks as $priority => $hooks ) {
				foreach ( $hooks as $name => $arr ) {
					if (
						( // Cover object method callback case.
							is_array( $arr['function'] ) &&
							isset( $arr['function'][0] ) &&
							is_object( $arr['function'][0] ) &&
							false !== strpos( ( get_class( $arr['function'][0] ) ), 'WP2FA' )
						) ||
						( // Cover class static method callback case.
							! empty( $name ) &&
							false !== strpos( ( $name ), 'WP2FA' )
						)
					) {
						continue;
					}

					unset( $wp_filter[ $action ]->callbacks[ $priority ][ $name ] );
				}
			}
		}

		/**
		 * Sets the internal variable with all the existing WP roles.
		 *
		 * @return void
		 *
		 * @since 2.2.0
		 */
		private static function set_roles() {
			if ( empty( self::$user_roles ) ) {
				global $wp_roles;

				if ( null === $wp_roles ) {
					wp_roles();
				}

				self::$user_roles = array_flip( $wp_roles->get_names() );
			}
		}
	}
}
includes/classes/Admin/Helpers/class-methods-helper.php000064400000014010150755130600017173 0ustar00<?php
/**
 * Responsible for the User's operations
 *
 * @package    wp2fa
 * @subpackage helpers
 * @since      2.6.0
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 * @link       https://wordpress.org/plugins/wp-2fa/
 */

namespace WP2FA\Admin\Helpers;

use WP2FA\Admin\Helpers\Classes_Helper;

defined( 'ABSPATH' ) || exit; // Exit if accessed directly.

/**
 * User's settings class
 */
if ( ! class_exists( '\WP2FA\Admin\Helpers\Methods_Helper' ) ) {

	/**
	 * All the user related settings must go trough this class.
	 *
	 * @since 2.6.0
	 */
	class Methods_Helper {
		const METHODS_NAMESPACE = '\WP2FA\Methods';

		const POLICY_SETTINGS_NAME = 'methods_order';

		/**
		 * Cached methods array
		 *
		 * @var array
		 *
		 * @since 2.7.0
		 */
		public static $methods = array();

		/**
		 * Inits the class and initializes all the methods
		 *
		 * @return void
		 *
		 * @since 2.6.0
		 */
		public static function init() {

			foreach ( self::get_methods() as $method ) {
				if ( method_exists( $method, 'init' ) ) {
					call_user_func_array( array( $method, 'init' ), array() );
				}
			}

			\add_action( WP_2FA_PREFIX . 'methods_setup', array( __CLASS__, 'methods_settings' ), 10, 3 );
			\add_filter( WP_2FA_PREFIX . 'filter_output_content', array( __CLASS__, 'settings_store' ), 10, 2 );
			\add_action( WP_2FA_PREFIX . 'methods_options', array( __CLASS__, 'methods_options' ) );
			\add_action( WP_2FA_PREFIX . 'methods_reconfigure_options', array( __CLASS__, 'methods_re_configure' ) );
		}

		/**
		 * Sets the methods in correct order for re-configuring. Checks the selected method for the user and puts it on top of the list
		 *
		 * @return void
		 *
		 * @since 2.6.0
		 */
		public static function methods_re_configure() {
			$role = User_Helper::get_user_role();

			/**
			 * Option to re-configure the methods - all the methods are called and their order and code is collected. Then the currently selected method is positioned on top and methods are shown in order. That is called in the user profile page.
			 *
			 * @param array - All the collected methods and their order.
			 * @param string $role - The role of the current user
			 *
			 * @since 2.6.0
			 */
			$methods = \apply_filters( WP_2FA_PREFIX . 'methods_re_configure', array(), $role );

			$enabled_method = User_Helper::get_enabled_method_for_user();

			foreach ( $methods as $order => $method ) {
				if ( $enabled_method === $method['name'] ) {
					$methods[-1] = $method;
					unset( $methods[ $order ] );

					break;
				}
			}

			ksort( $methods );

			foreach ( $methods as $method ) {
				echo $method['output']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
			}
		}

		/**
		 * Collects the methods options and shows them in order
		 *
		 * @return void
		 *
		 * @since 2.6.0
		 */
		public static function methods_options() {
			$role = User_Helper::get_user_role();

			/**
			 * Shows methods in order. Every method is called and its code and order is collected. That is used when there are no methods selected from the user.
			 *
			 * @param array - All the collected methods and their order.
			 * @param string $role - The role of the current user
			 *
			 * @since 2.6.0
			 */
			$methods = \apply_filters( WP_2FA_PREFIX . 'methods_modal_options', array(), $role );

			ksort( $methods );

			foreach ( $methods as $method ) {
				echo $method; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
			}
		}

		/**
		 * Settings page and first time wizard settings render
		 *
		 * @param boolean $setup_wizard - Is that the first time setup wizard.
		 * @param string  $data_role - Additional HTML data attribute.
		 * @param mixed   $role - Name of the role.
		 *
		 * @return void
		 *
		 * @since 2.6.0
		 */
		public static function methods_settings( bool $setup_wizard, string $data_role, $role = null ) {

			/**
			 * Shows methods in order. Every method is called and its code and order is collected. Used in the wizards.
			 *
			 * @param array - All the collected methods and their order.
			 * @param bool - Is that a setup wizard call or not?
			 * @param string - Additional HTML data attribute.
			 * @param string $role - The role, that is when global settings of the plugin are selected.
			 *
			 * @since 2.6.0
			 */
			$methods = \apply_filters( WP_2FA_PREFIX . 'methods_settings', array(), $setup_wizard, $data_role, $role );

			ksort( $methods );

			foreach ( $methods as $method ) {
				echo $method; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
			}
		}

		/**
		 * Adds and filters extension values in the settings store array ($output).
		 *
		 * @param array $output - Array with the currently stored settings.
		 * @param array $input  - Array with the input ($_POST) values.
		 *
		 * @return array
		 *
		 * @since 2.6.0
		 */
		public static function settings_store( array $output, array $input ) {
			if ( isset( $input[ self::POLICY_SETTINGS_NAME ] ) && \is_array( $input[ self::POLICY_SETTINGS_NAME ] ) ) {
				foreach ( $input[ self::POLICY_SETTINGS_NAME ] as $order => $method ) {
					$output[ self::POLICY_SETTINGS_NAME ][ $order ] = $method;
				}
			}

			return $output;
		}

		/**
		 * Returns the method by its slug.
		 *
		 * @param string $provider_name - The slug to search for.
		 *
		 * @return bool|\WP2FA\Methods
		 *
		 * @since 2.7.0
		 */
		public static function get_method_by_provider_name( string $provider_name ) {
			foreach ( self::get_methods() as $method ) {
				if ( $provider_name === $method::METHOD_NAME ) {
					return $method;
				}
			}

			return \false;
		}

		/**
		 * Returns all of the registered methods.
		 *
		 * @return array
		 *
		 * @since 2.7.0
		 */
		private static function get_methods(): array {
			if ( empty( self::$methods ) ) {
				/**
				 * Gives the ability to add classes to the Class_Helper array.
				 *
				 * @since 2.7.0
				 */
				\do_action( WP_2FA_PREFIX . 'add_to_class_map' );

				self::$methods = Classes_Helper::get_classes_by_namespace( self::METHODS_NAMESPACE );
			}

			return self::$methods;
		}
	}
}
includes/classes/Admin/Helpers/class-php-helper.php000064400000001660150755130600016326 0ustar00<?php
/**
 * Responsible for the User's operations
 *
 * @package    wp2fa
 * @subpackage helpers
 * @since      2.4.0
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 * @link       https://wordpress.org/plugins/wp-2fa/
 */

namespace WP2FA\Admin\Helpers;

defined( 'ABSPATH' ) || exit; // Exit if accessed directly.

/**
 * User's settings class
 */
if ( ! class_exists( '\WP2FA\Admin\Helpers\PHP_Helper' ) ) {

	/**
	 * All the user related settings must go trough this class.
	 *
	 * @since 2.4.0
	 */
	class PHP_Helper {

		/**
		 * Checks if given function is callable (exists) or not
		 *
		 * @param string $function_name - The name of the function to check.
		 *
		 * @return boolean
		 *
		 * @since 2.4.0
		 */
		public static function is_callable( string $function_name ): bool {
			if ( ! is_callable( $function_name ) ) {
				return false;
			}

			return true;
		}
	}
}
includes/classes/Admin/Helpers/index.php000064400000000046150755130600014263 0ustar00<?php
/**
 * Nothing to see here.
 */
includes/classes/Admin/Helpers/class-ajax-helper.php000064400000027552150755130600016472 0ustar00<?php
/**
 * Responsible for the AJAX calls.
 *
 * @package    wp2fa
 * @subpackage helpers
 *
 * @since      2.6.0
 *
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 *
 * @see       https://wordpress.org/plugins/wp-2fa/
 */

declare(strict_types=1);

namespace WP2FA\Admin\Helpers;

use WP2FA\WP2FA;
use WP2FA\Utils\User_Utils;
use WP2FA\Admin\Settings_Page;
use WP2FA\Utils\Settings_Utils;
use WP2FA\Admin\Helpers\WP_Helper;
use WP2FA\Admin\SettingsPages\Settings_Page_Email;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

if ( ! class_exists( '\WP2FA\Admin\Helpers\Ajax_Helper' ) ) {
	/**
	 * Responsible for the proper AJAX calls and responses.
	 */
	class Ajax_Helper {

		/**
		 * Get all users in AJAX matter and returns them
		 *
		 * @SuppressWarnings(PHPMD.ExitExpression)
		 *
		 * @since 2.6.0
		 */
		public static function get_all_users() {
			// Die if user does not have permission to view.
			if ( ! current_user_can( 'manage_options' ) ) {
				die( 'Access Denied.' );
			}
			// Filter $_GET array for security.
			$get_array = filter_input_array( INPUT_GET );

			// Die if nonce verification failed.
			if ( ! wp_verify_nonce( sanitize_text_field( $get_array['wp_2fa_nonce'] ), 'wp-2fa-settings-nonce' ) ) {
				die( esc_html__( 'Nonce verification failed.', 'wp-2fa' ) );
			}

			$users_args = array(
				'fields' => array( 'ID', 'user_login' ),
			);
			if ( WP_Helper::is_multisite() ) {
				$users_args['blog_id'] = 0;
			}
			$users_data = User_Utils::get_all_user_ids_and_login_names( 'query', $users_args );

			// Create final array which we will fill in below.
			$users = array();

			foreach ( $users_data as $user ) {
				if ( stripos( $user['user_login'], $get_array['term'] ) !== false ) {
					array_push(
						$users,
						array(
							'value' => $user['user_login'],
							'label' => $user['user_login'],
						)
					);
				}
			}

			echo wp_json_encode( $users );
			exit;
		}

		/**
		 * Get all network sites in AJAX way
		 *
		 * @SuppressWarnings(PHPMD.ExitExpression)
		 *
		 * @since 2.6.0
		 */
		public static function get_all_network_sites() {
			// Die if user does not have permission to view.
			if ( ! current_user_can( 'manage_options' ) ) {
				die( 'Access Denied.' );
			}
			// Filter $_GET array for security.
			$get_array = filter_input_array( INPUT_GET );
			// Die if nonce verification failed.
			if ( ! wp_verify_nonce( sanitize_text_field( $get_array['wp_2fa_nonce'] ), 'wp-2fa-settings-nonce' ) ) {
				die( esc_html__( 'Nonce verification failed.', 'wp-2fa' ) );
			}
			// Fetch sites.
			$sites_found = array();

			foreach ( get_sites() as $site ) {
				$subsite_id                  = get_object_vars( $site )['blog_id'];
				$subsite_name                = get_blog_details( $subsite_id )->blogname;
				$site_details                = '';
				$site_details[ $subsite_id ] = $subsite_name;
				if ( false !== stripos( $subsite_name, $get_array['term'] ) ) {
					array_push(
						$sites_found,
						array(
							'label' => $subsite_id,
							'value' => $subsite_name,
						)
					);
				}
			}
			echo wp_json_encode( $sites_found );
			exit;
		}

		/**
		 * Unlock users accounts if they have overrun grace period it ia also used in AJAX calls
		 *
		 * @param  int $user_id User ID.
		 *
		 * @SuppressWarnings(PHPMD.ExitExpression)
		 *
		 * @since 2.6.0
		 */
		public static function unlock_account( $user_id ) {
			// Die if user does not have permission to view.
			if ( ! current_user_can( 'manage_options' ) ) {
				die( 'Access Denied.' );
			}

			$grace_period             = WP2FA::get_wp2fa_setting( 'grace-period' );
			$grace_period_denominator = WP2FA::get_wp2fa_setting( 'grace-period-denominator' );
			$create_a_string          = $grace_period . ' ' . $grace_period_denominator;
			// Turn that string into a time.
			$grace_expiry = strtotime( $create_a_string );

			// Filter $_GET array for security.
			$get_array = filter_input_array( INPUT_GET );
			$nonce     = sanitize_text_field( $get_array['wp_2fa_nonce'] );

			// Die if nonce verification failed.
			if ( ! wp_verify_nonce( $nonce, 'wp-2fa-unlock-account-nonce' ) ) {
				die( esc_html__( 'Nonce verification failed.', 'wp-2fa' ) );
			}

			if ( isset( $get_array['user_id'] ) ) {
				User_Helper::remove_meta( WP_2FA_PREFIX . 'locked_account_notification', intval( $get_array['user_id'] ) );
				User_Helper::remove_grace_period( intval( $get_array['user_id'] ) );

				User_Helper::set_user_expiry_date( (string) $grace_expiry, intval( $get_array['user_id'] ) );
				Settings_Page::send_account_unlocked_email( intval( $get_array['user_id'] ) );

				/*
				* Fires after the user is unlocked.
				*
				* @param \WP_User $user - The user for which the method has been set.
				*
				* @since 2.6.0
				*/
				\do_action( WP_2FA_PREFIX . 'user_is_unlocked', User_Helper::get_user( intval( $get_array['user_id'] ) ) );

				\add_action( 'admin_notices', array( __CLASS__, 'user_unlocked_notice' ) );
			}
		}

		/**
		 * Sets the salt key into the wp-config.php file via AJAX request.
		 *
		 * @return void
		 *
		 * @since 2.4.0
		 */
		public static function set_salt_key() {
			if ( \wp_doing_ajax() ) {
				if ( isset( $_REQUEST['_wpnonce'] ) ) {
					$nonce_check = \wp_verify_nonce( \sanitize_text_field( \wp_unslash( $_REQUEST['_wpnonce'] ) ), 'wp-2fa-set-salt-nonce' );
					if ( ! $nonce_check ) {
						\wp_send_json_error( new \WP_Error( 500, \esc_html__( 'Nonce checking failed', 'wp-2fa' ) ), 400 );
					} elseif ( \current_user_can( 'manage_options' ) ) {
						if ( ! File_Writer::can_write_to_file( File_Writer::get_wp_config_file_path() ) ) {
							\wp_send_json_error(
								new \WP_Error(
									500,
									\esc_html__(
										'Unable to write to wp-config.php',
										'wp-2fa'
									)
								),
								400
							);
						} else {
							$secret_key = Settings_Utils::get_option( 'secret_key' );
							if ( ! empty( $secret_key ) ) {
								File_Writer::save_secret_key( $secret_key );
								Settings_Utils::delete_option( 'secret_key' );
								\wp_send_json_success(
									\esc_html__(
										'wp-config.php successfully update, global setting deleted',
										'wp-2fa'
									)
								);
							} else {
								\wp_send_json_error(
									new \WP_Error(
										500,
										\esc_html__(
											'Unable to find global secret key',
											'wp-2fa'
										)
									),
									400
								);
							}
						}
					}
				}
			}
		}

		/**
		 * Remove user 2fa config
		 *
		 * @param  int $user_id User ID.
		 *
		 * @SuppressWarnings(PHPMD.ExitExpression)
		 *
		 * @since 2.6.0
		 */
		public static function remove_user_2fa( $user_id ) {
			// Filter $_GET array for security.
			$get_array = filter_input_array( INPUT_GET );
			$nonce     = sanitize_text_field( $get_array['wp_2fa_nonce'] );

			if ( ! wp_verify_nonce( $nonce, 'wp-2fa-remove-user-2fa-nonce' ) ) {
				die( esc_html__( 'Nonce verification failed.', 'wp-2fa' ) );
			}

			if ( isset( $get_array['user_id'] ) ) {
				$user_id = intval( $get_array['user_id'] );

				if ( ! current_user_can( 'manage_options' ) && get_current_user_id() !== $user_id ) {
					return;
				}

				User_Helper::remove_2fa_for_user( $user_id );

				if ( isset( $get_array['admin_reset'] ) ) {
					add_action( 'admin_notices', array( __CLASS__, 'admin_deleted_2fa_notice' ) );
				} else {
					add_action( 'admin_notices', array( __CLASS__, 'user_deleted_2fa_notice' ) );
				}
			}
		}

		/**
		 * Returns the user roles in AJAX matter.
		 *
		 * @return void
		 *
		 * @since 2.6.0
		 */
		public static function get_ajax_user_roles() {
			if ( \wp_doing_ajax() ) {
				// Filter $_GET array for security.
				$get_array = filter_input_array( INPUT_GET );

				// Die if nonce verification failed.
				if ( ! wp_verify_nonce( sanitize_text_field( $get_array['wp_2fa_nonce'] ), 'wp-2fa-settings-nonce' ) ) {
					die( esc_html__( 'Nonce verification failed.', 'wp-2fa' ) );
				}
				$roles = array();

				foreach ( WP_Helper::get_roles_wp() as $role => $human_readable ) {
					if ( stripos( $human_readable, $get_array['term'] ) !== false ) {
						array_push(
							$roles,
							array(
								'label' => $role,
								'value' => $human_readable,
							)
						);
					}
				}

				echo wp_json_encode( $roles );
				exit;
			}
		}

		/**
		 * Handles AJAX calls for sending test emails.
		 *
		 * @return void
		 *
		 * @since 2.6.0
		 */
		public static function handle_send_test_email_ajax() {

			// check user permissions.
			if ( ! current_user_can( 'manage_options' ) ) {
				wp_send_json_error();
			}

			// check email id.
			$email_id = isset( $_POST['email_id'] ) ? sanitize_text_field( \wp_unslash( $_POST['email_id'] ) ) : null;
			if ( null === $email_id || false === $email_id ) {
				wp_send_json_error();
			}

			// check nonce.
			$nonce = isset( $_POST['_wpnonce'] ) ? sanitize_text_field( \wp_unslash( $_POST['_wpnonce'] ) ) : null;
			if ( null === $nonce || false === $nonce || ! wp_verify_nonce( $nonce, 'wp-2fa-email-test-' . $email_id ) ) {
				wp_send_json_error();
			}

			$user_id = get_current_user_id();
			// Grab user data.
			$user = get_userdata( $user_id );
			// Grab user email.
			$email = $user->user_email;

			if ( 'config_test' === $email_id ) {
				$email_sent = Settings_Page::send_email(
					$email,
					esc_html__( 'Test email from WP 2FA', 'wp-2fa' ),
					esc_html__( 'This email was sent by the WP 2FA plugin to test the email delivery.', 'wp-2fa' )
				);
				if ( $email_sent ) {
					wp_send_json_success( 'Test email was successfully sent to <strong>' . $email . '</strong>' );
				}

				wp_send_json_error();
			}

			/**
			 * All email templates
			 *
			 *  @var Email_Template[] $email_templates
			 */
			$email_templates = Settings_Page_Email::get_email_notification_definitions();
			foreach ( $email_templates as $email_template ) {
				if ( $email_id === $email_template->get_email_content_id() ) {
					// send the test email.

					// Setup the email contents.
					$subject = wp_strip_all_tags( \WP2FA\WP2FA::replace_email_strings( \WP2FA\WP2FA::get_wp2fa_email_templates( $email_id . '_email_subject' ) ) );
					$message = wpautop( \WP2FA\WP2FA::replace_email_strings( \WP2FA\WP2FA::get_wp2fa_email_templates( $email_id . '_email_body' ), $user_id ) );

					$email_sent = Settings_Page::send_email( $email, $subject, $message );
					if ( $email_sent ) {
						wp_send_json_success( 'Test email <strong>' . $email_template->get_title() . '</strong> was successfully sent to <strong>' . $email . '</strong>' );
					}

					wp_send_json_error();
				}
			}
		}

		/**
		 * User deleted 2FA settings notification
		 *
		 * @since 2.6.0
		 */
		public static function user_deleted_2fa_notice() {
			?>
			<div class="notice notice-success is-dismissible">
				<p><?php esc_html_e( 'Your 2FA settings have been removed.', 'wp-2fa' ); ?></p>
				<button type="button" class="notice-dismiss">
					<span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
				</button>
			</div>
			<?php
		}

		/**
		 * Admin deleted user 2FA settings notification
		 *
		 * @since 2.6.0
		 */
		public static function admin_deleted_2fa_notice() {
			?>
			<div class="notice notice-success is-dismissible">
				<p><?php esc_html_e( 'User 2FA settings have been removed.', 'wp-2fa' ); ?></p>
				<button type="button" class="notice-dismiss">
					<span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
				</button>
			</div>
			<?php
		}

		/**
		 * User unlocked notice.
		 *
		 * @since 2.6.0
		 */
		public static function user_unlocked_notice() {
			?>
			<div class="notice notice-success is-dismissible">
				<p><?php esc_html_e( 'User account successfully unlocked. User can login again.', 'wp-2fa' ); ?></p>
				<button type="button" class="notice-dismiss">
					<span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
				</button>
			</div>
			<?php
		}
	}
}
includes/classes/Admin/Helpers/class-user-helper.php000064400000172647150755130600016533 0ustar00<?php
/**
 * Responsible for the User's operations.
 *
 * @package    wp2fa
 * @subpackage helpers
 *
 * @since      2.2.0
 *
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 *
 * @see       https://wordpress.org/plugins/wp-2fa/
 */

declare(strict_types=1);

namespace WP2FA\Admin\Helpers;

defined( 'ABSPATH' ) || exit; // Exit if accessed directly.

use wpdb;
use WP2FA\WP2FA;
use WP2FA\Utils\User_Utils;
use WP2FA\Extensions_Loader;
use WP2FA\Admin\Settings_Page;
use WP2FA\Utils\Settings_Utils;
use WP2FA\Freemius\User_Licensing;
use WP2FA\Admin\Controllers\Methods;
use WP2FA\Admin\Controllers\Settings;

/*
 * User's settings class
 */
if ( ! class_exists( '\WP2FA\Admin\Helpers\User_Helper' ) ) {
	/**
	 * All the user related settings must go trough this class.
	 *
	 * @since 2.2.0
	 */
	class User_Helper {
		/**
		 * Enabled 2fa method for user meta name.
		 */
		public const ENABLED_METHODS_META_KEY = WP_2FA_PREFIX . 'enabled_methods';
		/**
		 * Email token for user meta name.
		 */
		public const TOKEN_META_KEY = WP_2FA_PREFIX . 'email_token';
		/**
		 * Global settings hash for user meta name
		 * That is used to check if user needs to be re-checked / re-configured, if the settings of the plugin are changed, probably the user settings also need to be changed - that meta holds the key to check against.
		 */
		public const USER_SETTINGS_HASH = WP_2FA_PREFIX . 'global_settings_hash';
		/**
		 * The meta name for the user 2FA status in the plugin.
		 */
		public const USER_2FA_STATUS = WP_2FA_PREFIX . '2fa_status';
		/**
		 * The user grace period expired meta key.
		 */
		public const USER_GRACE_KEY = WP_2FA_PREFIX . 'user_grace_period_expired';
		/**
		 * The user grace period expiry date meta key.
		 */
		public const USER_GRACE_EXPIRY_KEY = WP_2FA_PREFIX . 'grace_period_expiry';
		/**
		 * The user enforcement status.
		 */
		public const USER_ENFORCED_INSTANTLY = WP_2FA_PREFIX . 'user_enforced_instantly';
		/**
		 * The user reconfigure 2fa status.
		 */
		public const USER_NEEDS_TO_RECONFIGURE_2FA = WP_2FA_PREFIX . 'user_needs_to_reconfigure_2fa';
		/**
		 * The user enforcement state.
		 */
		public const USER_ENFORCEMENT_STATE = WP_2FA_PREFIX . 'enforcement_state';
		/**
		 * The user nag dismissed flag.
		 */
		public const USER_NAG_DISMISSED = WP_2FA_PREFIX . 'update_nag_dismissed';
		/**
		 * The default status of the user which has no status set yet.
		 */
		public const USER_UNDETERMINED_STATUS = 'no_determined_yet';
		/**
		 * The last login date for the user.
		 */
		public const USER_LOGIN_DATE = 'login_date';
		/**
		 * The reset password for the user is valid.
		 */
		public const USER_RESET_PASSWORD_VALID = 'reset_password_valid';
		/**
		 * The nominated global email address for the user.
		 */
		public const USER_NOMINATED_EMAIL = WP_2FA_PREFIX . 'nominated_email_address';
		/**
		 * The backup email address for the user - for backup methods when app is in use.
		 */
		public const USER_BACKUP_EMAIL = WP_2FA_PREFIX . 'backup_email_address';
		/**
		 * The default user statuses.
		 */
		public const USER_STATE_STATUSES = array(
			'optional',
			'excluded',
			'enforced',
		);

		/**
		 * The class user variable.
		 *
		 * @var \WP_User
		 *
		 * @since 2.2.0
		 */
		private static $user = null;

		/**
		 * All global excluded roles
		 *
		 * @var array
		 *
		 * @since 2.5.0
		 */
		private static $excluded_roles = null;

		/**
		 * All global excluded sites
		 *
		 * @var array
		 *
		 * @since 2.5.0
		 */
		private static $excluded_sites = null;

		/**
		 * All global excluded users
		 *
		 * @var array
		 *
		 * @since 2.5.0
		 */
		private static $excluded_users = null;

		/**
		 * All global included sites
		 *
		 * @var array
		 *
		 * @since 2.5.0
		 */
		private static $included_sites = null;

		/**
		 * All global enforced users
		 *
		 * @var array
		 *
		 * @since 2.5.0
		 */
		private static $enforced_users = null;

		/**
		 * All global enforced roles
		 *
		 * @var array
		 *
		 * @since 2.5.0
		 */
		private static $enforced_roles = null;

		/**
		 * Marks the status of the updating process
		 *
		 * @var boolean
		 *
		 * @since 2.4.1
		 */
		private static $update_started = false;

		/**
		 * Returns the enable 2fa backup methods for the given user
		 *
		 * @param \WP_User] $user - The user which has to be checked.
		 *
		 * @return mixed
		 *
		 * @since 2.6.0
		 */
		public static function get_enabled_backup_methods_for_user( $user = null ) {
			self::set_proper_user( $user );

			/*
			 * Checks the enabled methods for the user.
			 *
			 * @param mixed - Value of the method.
			 * @param array - Array of enabled methods for the user.
			 * @param \WP_User - The user which must be checked.
			 *
			 * @since 2.6.0
			 */
			return apply_filters( WP_2FA_PREFIX . 'user_enabled_backup_methods', array(), $user );
		}

		/**
		 * Returns the enabled 2FA method for the user.
		 *
		 * @param int|\WP_User|null $user - The WP user we should extract the meta data for.
		 *
		 * @return mixed
		 *
		 * @since 2.2.0
		 */
		public static function get_enabled_method_for_user( $user = null ) {
			self::set_proper_user( $user );

			/*
			 * Checks the enabled methods for the user.
			 *
			 * @param mixed - Value of the method.
			 * @param string|null $user - Currently enabled method.
			 * @param \WP_User - The user which must be checked.
			 *
			 * @since 2.0.0
			 */
			return apply_filters( WP_2FA_PREFIX . 'user_enabled_methods', self::get_meta( self::ENABLED_METHODS_META_KEY ), $user );
		}

		/**
		 * Sets the enabled 2FA method for the user.
		 *
		 * @param string            $method - The name of the method to set.
		 * @param int|\WP_User|null $user   - The WP user we should extract the meta data for.
		 *
		 * @return mixed
		 *
		 * @since 2.2.0
		 */
		public static function set_enabled_method_for_user( string $method, $user = null ) {
			self::set_proper_user( $user );

			/*
			 * Fires before the user method is set.
			 *
			 * @param string - Current user method.
			 * @param \WP_User $user - The user for which the method has been set.
			 *
			 * @since 2.6.0
			 */
			\do_action( WP_2FA_PREFIX . 'before_method_been_set', self::get_enabled_method_for_user( self::get_user() ), self::get_user() );

			$set_method = self::set_meta( self::ENABLED_METHODS_META_KEY, $method );

			/*
			 * Fires when the user method is set.
			 *
			 * @param string - The method set for the user.
			 * @param \WP_User $user - The user for which the method has been set.
			 *
			 * @since 2.2.2
			 */
			\do_action( WP_2FA_PREFIX . 'method_has_been_set', $method, self::get_user() );

			return $set_method;
		}

		/**
		 * Removes the 2FA method for the user.
		 *
		 * @param int|\WP_User|null $user - The WP user we should extract the meta data for.
		 *
		 * @return void
		 *
		 * @since 2.2.0
		 */
		public static function remove_enabled_method_for_user( $user = null ) {
			self::set_proper_user( $user );

			/*
			 * Fires before the user method is removed.
			 *
			 * @param string - Current user method.
			 * @param \WP_User $user - The user for which the method has been set.
			 *
			 * @since 2.6.0
			 */
			\do_action( WP_2FA_PREFIX . 'before_method_is_removed', self::get_enabled_method_for_user( self::get_user() ), self::get_user() );

			self::remove_meta( self::ENABLED_METHODS_META_KEY, self::$user );

			/*
			 * Fires after the user method is removed.
			 *
			 * @param \WP_User $user - The user for which the method has been set.
			 *
			 * @since 2.6.0
			 */
			\do_action( WP_2FA_PREFIX . 'after_method_is_removed', self::get_user() );

			if ( class_exists( '\WP2FA\Freemius\User_Licensing' ) ) {
				if ( Extensions_Loader::use_proxytron() ) {
					$user_blog_id = 1;
					if ( WP_Helper::is_multisite() ) {
						$user_blog_id = \get_active_blog_for_user( self::$user->ID )->blog_id;
					}
					if ( ( $current_blog = \get_current_blog_id() ) !== $user_blog_id ) { // phpcs:ignore
						if ( WP_Helper::is_multisite() ) {
							\switch_to_blog( $user_blog_id );
						}
						User_Licensing::method_has_been_set();
						if ( WP_Helper::is_multisite() ) {
							\switch_to_blog( $current_blog );
						}
					}
				}
			}
		}

		/**
		 * Returns the email token for the user.
		 *
		 * @param int|\WP_User|null $user - The WP user we should extract the meta data for.
		 *
		 * @return mixed
		 *
		 * @since 2.2.0
		 */
		public static function get_email_token_for_user( $user = null ) {
			self::set_proper_user( $user );

			return self::get_meta( self::TOKEN_META_KEY );
		}

		/**
		 * Sets the email token for the user.
		 *
		 * @param string            $token - The token to set for the user.
		 * @param int|\WP_User|null $user  - The WP user we should extract the meta data for.
		 *
		 * @return mixed
		 *
		 * @since 2.2.0
		 */
		public static function set_email_token_for_user( string $token, $user = null ) {
			self::set_proper_user( $user );

			return self::set_meta( self::TOKEN_META_KEY, $token );
		}

		/**
		 * Removes the email token for the user.
		 *
		 * @param int|\WP_User|null $user - The WP user we should extract the meta data for.
		 *
		 * @return void
		 *
		 * @since 2.2.0
		 */
		public static function remove_email_token_for_user( $user = null ) {
			self::set_proper_user( $user );

			self::remove_meta( self::TOKEN_META_KEY, self::$user );
		}

		/**
		 * Returns the last login date for the user.
		 *
		 * @param int|\WP_User|null $user - The WP user we should extract the meta data for.
		 *
		 * @return mixed
		 *
		 * @since 2.5.0
		 */
		public static function get_login_date_for_user( $user = null ) {
			self::set_proper_user( $user );

			return self::get_meta( self::USER_RESET_PASSWORD_VALID );
		}

		/**
		 * Sets  last login date for the user.
		 *
		 * @param bool              $valid - The reset password is valid user.
		 * @param int|\WP_User|null $user  - The WP user we should extract the meta data for.
		 *
		 * @return mixed
		 *
		 * @since 2.5.0
		 */
		public static function set_reset_password_valid_for_user( bool $valid, $user = null ) {
			self::set_proper_user( $user );

			return self::set_meta( self::USER_RESET_PASSWORD_VALID, $valid );
		}

		/**
		 * Removes  last login date  for the user.
		 *
		 * @param int|\WP_User|null $user - The WP user we should extract the meta data for.
		 *
		 * @return void
		 *
		 * @since 2.5.0
		 */
		public static function remove_reset_password_valid_for_user( $user = null ) {
			self::set_proper_user( $user );

			self::remove_meta( self::USER_RESET_PASSWORD_VALID, self::$user );
		}

		/**
		 * Returns the last login date for the user.
		 *
		 * @param int|\WP_User|null $user - The WP user we should extract the meta data for.
		 *
		 * @return mixed
		 *
		 * @since 2.5.0
		 */
		public static function get_reset_password_valid_for_user( $user = null ) {
			self::set_proper_user( $user );

			return self::get_meta( self::USER_RESET_PASSWORD_VALID );
		}

		/**
		 * Sets last login date for the user.
		 *
		 * @param int               $login_date - The login date to set for the user.
		 * @param int|\WP_User|null $user  - The WP user we should extract the meta data for.
		 *
		 * @return mixed
		 *
		 * @since 2.5.0
		 */
		public static function set_login_date_for_user( int $login_date, $user = null ) {
			self::set_proper_user( $user );

			return self::set_meta( self::USER_LOGIN_DATE, $login_date );
		}

		/**
		 * Removes  last login date  for the user.
		 *
		 * @param int|\WP_User|null $user - The WP user we should extract the meta data for.
		 *
		 * @return void
		 *
		 * @since 2.5.0
		 */
		public static function remove_login_date_for_user( $user = null ) {
			self::set_proper_user( $user );

			self::remove_meta( self::USER_LOGIN_DATE, self::$user );
		}

		/**
		 * Returns the global settings hash for the user.
		 *
		 * @param int|\WP_User|null $user - The WP user we should extract the meta data for.
		 *
		 * @return mixed
		 *
		 * @since 2.2.0
		 */
		public static function get_global_settings_hash_for_user( $user = null ) {
			self::set_proper_user( $user );

			return self::get_meta( self::USER_SETTINGS_HASH );
		}

		/**
		 * Sets the global settings hash for the user.
		 *
		 * @param string            $hash - The global settings hash to set for the user.
		 * @param int|\WP_User|null $user - The WP user we should extract the meta data for.
		 *
		 * @return mixed
		 *
		 * @since 2.2.0
		 */
		public static function set_global_settings_hash_for_user( string $hash, $user = null ) {
			self::set_proper_user( $user );

			return self::set_meta( self::USER_SETTINGS_HASH, $hash );
		}

		/**
		 * Removes the global settings hash for the user.
		 *
		 * @param int|\WP_User|null $user - The WP user we should extract the meta data for.
		 *
		 * @return void
		 *
		 * @since 2.2.0
		 */
		public static function remove_global_settings_hash_for_user( $user = null ) {
			self::set_proper_user( $user );

			self::remove_meta( self::USER_SETTINGS_HASH, self::$user );
		}

		/**
		 * Returns the current 2FA status for the user.
		 *
		 * @param int|\WP_User|null $user - The WP user we should extract the meta data for.
		 *
		 * @return mixed
		 *
		 * @since 2.2.0
		 */
		public static function get_2fa_status( $user = null ) {
			self::set_proper_user( $user );

			$status = (string) self::get_meta( self::USER_2FA_STATUS );

			if ( '' === trim( $status ) ) {
				$status = self::USER_UNDETERMINED_STATUS;
				self::set_2fa_status( self::USER_UNDETERMINED_STATUS );
			}

			return $status;
		}

		/**
		 * Sets the 2FA status for the user.
		 *
		 * @param string            $status - The name of the status to set.
		 * @param int|\WP_User|null $user   - The WP user we should extract the meta data for.
		 *
		 * @return mixed
		 *
		 * @since 2.2.0
		 */
		public static function set_2fa_status( string $status, $user = null ) {
			self::set_proper_user( $user );

			return self::set_meta( self::USER_2FA_STATUS, $status );
		}

		/**
		 * Removes the 2FA status for the user.
		 *
		 * @param int|\WP_User|null $user - The WP user we should extract the meta data for.
		 *
		 * @return void
		 *
		 * @since 2.2.0
		 */
		public static function remove_2fa_status( $user = null ) {
			self::set_proper_user( $user );

			self::remove_meta( self::USER_2FA_STATUS, self::$user );
		}

		/**
		 * Returns the current nag status for the user.
		 *
		 * @param int|\WP_User|null $user - The WP user we should extract the meta data for.
		 *
		 * @return mixed
		 *
		 * @since 2.3
		 */
		public static function get_nag_status( $user = null ) {
			self::set_proper_user( $user );

			return self::get_meta( self::USER_NAG_DISMISSED );
		}

		/**
		 * Sets the nag status for the user.
		 *
		 * @param bool              $status - The name of the status to set.
		 * @param int|\WP_User|null $user   - The WP user we should extract the meta data for.
		 *
		 * @return mixed
		 *
		 * @since 2.3
		 */
		public static function set_nag_status( bool $status, $user = null ) {
			self::set_proper_user( $user );

			return self::set_meta( self::USER_NAG_DISMISSED, $status );
		}

		/**
		 * Removes the nag status for the user.
		 *
		 * @param int|\WP_User|null $user - The WP user we should extract the meta data for.
		 *
		 * @return void
		 *
		 * @since 2.3
		 */
		public static function remove_nag_status( $user = null ) {
			self::set_proper_user( $user );

			self::remove_meta( self::USER_NAG_DISMISSED, self::$user );
		}

		/**
		 * Returns the current 2FA status for the user.
		 *
		 * @param int|\WP_User|null $user - The WP user we should extract the meta data for.
		 *
		 * @return mixed
		 *
		 * @since 2.2.0
		 */
		public static function get_user_expiry_date( $user = null ) {
			self::set_proper_user( $user );

			return self::get_meta( self::USER_GRACE_EXPIRY_KEY );
		}

		/**
		 * Sets the 2FA status for the user.
		 *
		 * @param string            $date - The period to set.
		 * @param int|\WP_User|null $user - The WP user we should extract the meta data for.
		 *
		 * @return mixed
		 *
		 * @since 2.2.0
		 */
		public static function set_user_expiry_date( string $date, $user = null ) {
			self::set_proper_user( $user );

			return self::set_meta( self::USER_GRACE_EXPIRY_KEY, $date );
		}

		/**
		 * Removes the 2FA status for the user.
		 *
		 * @param int|\WP_User|null $user - The WP user we should extract the meta data for.
		 *
		 * @return void
		 *
		 * @since 2.2.0
		 */
		public static function remove_user_expiry_date( $user = null ) {
			self::set_proper_user( $user );

			self::remove_meta( self::USER_GRACE_EXPIRY_KEY, self::$user );
		}

		/**
		 * Returns the current 2FA enforcement status for the user.
		 *
		 * @param int|\WP_User|null $user - The WP user we should extract the meta data for.
		 *
		 * @return mixed
		 *
		 * @since 2.2.0
		 */
		public static function get_user_enforced_instantly( $user = null ) {
			self::set_proper_user( $user );

			return self::get_meta( self::USER_ENFORCED_INSTANTLY );
		}

		/**
		 * Sets the 2FA enforcement status for the user.
		 *
		 * @param bool              $status - The status for user enforcement.
		 * @param int|\WP_User|null $user   - The WP user we should extract the meta data for.
		 *
		 * @return mixed
		 *
		 * @since 2.2.0
		 */
		public static function set_user_enforced_instantly( bool $status, $user = null ) {
			self::set_proper_user( $user );

			return self::set_meta( self::USER_ENFORCED_INSTANTLY, $status );
		}

		/**
		 * Removes the 2FA enforcement status for the user.
		 *
		 * @param int|\WP_User|null $user - The WP user we should extract the meta data for.
		 *
		 * @return void
		 *
		 * @since 2.2.0
		 */
		public static function remove_user_enforced_instantly( $user = null ) {
			self::set_proper_user( $user );

			self::remove_meta( self::USER_ENFORCED_INSTANTLY, self::$user );
		}

		/**
		 * Returns the current 2FA needs to reconfigure status for the user.
		 *
		 * @param int|\WP_User|null $user - The WP user we should extract the meta data for.
		 *
		 * @return mixed
		 *
		 * @since 2.2.0
		 */
		public static function get_user_needs_to_reconfigure_2fa( $user = null ) {
			self::set_proper_user( $user );

			return self::get_meta( self::USER_NEEDS_TO_RECONFIGURE_2FA );
		}

		/**
		 * Sets the 2FA needs to reconfigure status for the user.
		 *
		 * @param bool              $status - The status for user enforcement.
		 * @param int|\WP_User|null $user   - The WP user we should extract the meta data for.
		 *
		 * @return mixed
		 *
		 * @since 2.2.0
		 */
		public static function set_user_needs_to_reconfigure_2fa( bool $status, $user = null ) {
			self::set_proper_user( $user );

			return self::set_meta( self::USER_NEEDS_TO_RECONFIGURE_2FA, $status );
		}

		/**
		 * Removes the 2FA needs to reconfigure status for the user.
		 *
		 * @param int|\WP_User|null $user - The WP user we should extract the meta data for.
		 *
		 * @return void
		 *
		 * @since 2.2.0
		 */
		public static function remove_user_needs_to_reconfigure_2fa( $user = null ) {
			self::set_proper_user( $user );

			self::remove_meta( self::USER_NEEDS_TO_RECONFIGURE_2FA, self::$user );
		}

		/**
		 * Every meta call for the user must go through this method, so we can unify the code.
		 *
		 * @param string            $meta - The meta name that we should check.
		 * @param int|\WP_User|null $user - The WP user we should extract the meta data for.
		 * @param mixed             $default_value - The default value to be returned if meta is not presented.
		 *
		 * @return mixed
		 *
		 * @since 2.2.0
		 */
		public static function get_meta( string $meta, $user = null, $default_value = true ) {
			self::set_proper_user( $user );

			return \get_user_meta( self::$user->ID, $meta, $default_value );
		}

		/**
		 * Every meta storing call for the user must go through this method.
		 *
		 * @param string            $meta  - The meta name that we should check.
		 * @param mixed             $value - The value which should be stored.
		 * @param int|\WP_User|null $user  - The WP user we should extract the meta data for.
		 *
		 * @return mixed
		 *
		 * @since 2.2.0
		 */
		public static function set_meta( string $meta, $value, $user = null ) {
			self::set_proper_user( $user );

			return \update_user_meta( self::$user->ID, $meta, $value );
		}

		/**
		 * Removes meta for the given user.
		 *
		 * @param string            $meta - The name of the meta.
		 * @param int|\WP_User|null $user - The WP user we should extract the meta data for.
		 *
		 * @return mixed
		 *
		 * @since 2.2.0
		 */
		public static function remove_meta( string $meta, $user = null ) {
			self::set_proper_user( $user );

			return \delete_user_meta( self::$user->ID, $meta );
		}

		/**
		 * Returns the currently set user.
		 *
		 * @return \WP_User
		 *
		 * @since 2.2.0
		 */
		public static function get_user() {
			if ( null === self::$user ) {
				self::set_user();
			}

			return self::$user;
		}

		/**
		 * Returns WP User object.
		 *
		 * @param int|\WP_User|null $user - The WP user that must be used.
		 *
		 * @return \WP_User
		 *
		 * @since 2.2.0
		 */
		public static function get_user_object( $user = null ) {
			if ( null === $user && null !== self::$user ) {
				return self::$user;
			}

			self::set_user( $user );

			return self::$user;
		}

		/**
		 * Sets the user.
		 *
		 * @param int|\WP_User|null $user - The WP user that must be used.
		 *
		 * @return void
		 *
		 * @since 2.2.0
		 */
		public static function set_user( $user = null ) {
			if ( $user instanceof \WP_User ) {
				if ( isset( self::$user ) && $user === self::$user ) {
					return;
				}
				self::$user = $user;
			} elseif ( false !== ( filter_var( $user, FILTER_VALIDATE_INT ) ) ) {
				if ( isset( self::$user ) && $user instanceof \WP_User && $user === self::$user->ID ) {
					return;
				}
				if ( ! function_exists( 'get_user_by' ) ) {
					require ABSPATH . WPINC . '/pluggable.php';
				}
				self::$user = \get_user_by( 'id', $user );
				if ( \is_bool( self::$user ) ) {
					self::$user = \wp_get_current_user();
				}
			} elseif ( is_string( $user ) && ! empty( trim( (string) $user ) ) ) {
				if ( isset( self::$user ) && $user instanceof \WP_User && $user === self::$user->ID ) {
					return;
				}
				if ( ! function_exists( 'get_user_by' ) ) {
					require ABSPATH . WPINC . '/pluggable.php';
				}
				self::$user = \get_user_by( 'login', $user );
			} else {
				if ( ! function_exists( 'wp_get_current_user' ) ) {
					require ABSPATH . WPINC . '/pluggable.php';
					wp_cookie_constants();
				}
				self::$user = \wp_get_current_user();
			}
		}

		/**
		 * Returns the default role for the given user.
		 *
		 * @param int|\WP_User|null $user - The WP user.
		 *
		 * @since 2.2.0
		 */
		public static function get_user_role( $user = null ): string {
			self::set_proper_user( $user );

			if ( 0 === self::$user->ID || \is_bool( self::$user ) ) {
				return '';
			}

			if ( \is_multisite() ) {
				$blog_id = \get_current_blog_id();

				if ( ! is_user_member_of_blog( self::$user->ID, $blog_id ) ) {
					$user_blog_id = \get_active_blog_for_user( self::$user->ID );

					if ( null !== $user_blog_id ) {
						self::$user = new \WP_User(
							// $user_id
							self::$user->ID,
							// $name | login, ignored if $user_id is set
							'',
							// $blog_id
							$user_blog_id->blog_id
						);
					}
				}
			}

			$role = reset( self::$user->roles );

			/*
			 * The code looks like this for clearness only
			 */
			if ( \is_multisite() ) {
				/*
				 * On multi site we can have user which has no assigned role, but it is superadmin.
				 * If the check confirms that - assign the role of the administrator to the user in order not to break our code.
				 *
				 * Unfortunately we could never be sure what is the name of the administrator role (someone could change this default value),
				 * in order to continue working we will use the presumption that if given role has 'manage_options' capability, then it is
				 * most probably administrator - so we will assign that role to the user.
				 */
				if ( false === $role && is_super_admin( self::$user->ID ) ) {
					$wp_roles = WP_Helper::get_roles_wp();
					foreach ( $wp_roles as $role_name => $wp_role ) {
						$admin_role_set = get_role( $role_name )->capabilities;
						if ( $admin_role_set['manage_options'] ) {
							$role = $role_name;

							break;
						}
					}
				}
			}

			return (string) $role;
		}

		/**
		 * Returns the default blog_id for the given user.
		 *
		 * @param int|\WP_User|null $user - The WP user.
		 *
		 * @since 2.5.0
		 */
		public static function get_user_default_blog( $user = null ): int {
			self::set_proper_user( $user );

			if ( 0 === self::$user->ID ) {
				return 0;
			}

			if ( \is_multisite() ) {
				$blog_id = \get_current_blog_id();

				if ( ! is_user_member_of_blog( self::$user->ID, $blog_id ) ) {
					$blog_id = \get_active_blog_for_user( self::$user->ID );

					if ( $blog_id instanceof \WP_Site ) {
						return (int) $blog_id->blog_id;
					} else {
						return 1;
					}
				}
			} else {
				return 1;
			}

			return (int) $blog_id;
		}

		/**
		 * Checks if the user method is within the selected methods for the given role.
		 *
		 * @param int|\WP_User|null $user - The WP user.
		 *
		 * @since 2.2.0
		 */
		public static function is_user_method_in_role_enabled_methods( $user = null ): bool {
			$enabled_method = self::get_enabled_method_for_user( $user );
			if ( empty( $enabled_method ) ) {
				return false;
			}
			$is_method_available = Settings::is_provider_enabled_for_role( self::get_user_role( $user ), $enabled_method );

			return $is_method_available;
		}

		/**
		 * Removes all the meta keys associated with the given user.
		 *
		 * @param int|\WP_User|null $user - The WP user for which we have to remove the meta data.
		 *
		 * @return void
		 *
		 * @since 2.2.0
		 */
		public static function remove_all_2fa_meta_for_user( $user = null ) {
			self::set_proper_user( $user );

			$user_meta_values = array_filter(
				\get_user_meta( self::$user->ID ),
				function ( $key ) {
					return 0 === strpos( $key, WP_2FA_PREFIX );
				},
				ARRAY_FILTER_USE_KEY
			);

			foreach ( array_keys( $user_meta_values ) as $meta_name ) {
				self::remove_meta( $meta_name, $user );
			}

			if ( class_exists( '\WP2FA\Freemius\User_Licensing' ) ) {
				if ( Extensions_Loader::use_proxytron() ) {
					$user_blog_id = 1;
					if ( WP_Helper::is_multisite() ) {
						$user_blog_id = \get_active_blog_for_user( self::$user->ID )->blog_id;
					}
					if ( ( $current_blog = \get_current_blog_id() ) !== $user_blog_id ) { // phpcs:ignore Generic.CodeAnalysis.AssignmentInCondition.Found, Squiz.PHP.DisallowMultipleAssignments.FoundInControlStructure
						if ( WP_Helper::is_multisite() ) {
							wp2fa_freemius()->switch_to_blog( $user_blog_id );
						}
						User_Licensing::method_has_been_set();
						if ( WP_Helper::is_multisite() ) {
							wp2fa_freemius()->switch_to_blog( $current_blog );
						}
					}
				}
			}

			/*
			 * Fires when the user method is removed.
			 *
			 * @param \WP_User $user - The user for which the method has been removed.
			 *
			 * @since 2.2.2
			 */
			\do_action( WP_2FA_PREFIX . 'method_has_been_removed', self::get_user() );
		}

		/**
		 * Quick boolean check for whether a given user is using two-step.
		 *
		 * @since 2.2.0
		 *
		 * @param int|\WP_User|null $user - The WP user that must be used.
		 *
		 * @return bool
		 */
		public static function is_user_using_two_factor( $user = null ) {
			self::set_proper_user( $user );

			return ! empty( self::get_enabled_method_for_user() );
		}

		/**
		 * Gets the user grace period from meta.
		 *
		 * @param int|\WP_User|null $user - The WP user that must be used.
		 *
		 * @return mixed
		 *
		 * @since 2.2.0
		 */
		public static function get_grace_period( $user = null ) {
			self::set_proper_user( $user );

			return self::get_meta( self::USER_GRACE_KEY, self::$user );
		}

		/**
		 * Sets the user grace period from meta.
		 *
		 * @param string            $value - The value of the meta key.
		 * @param int|\WP_User|null $user  - The WP user that must be used.
		 *
		 * @return mixed
		 *
		 * @since 2.2.0
		 */
		public static function set_grace_period( $value, $user = null ) {
			self::set_proper_user( $user );

			return self::set_meta( self::USER_GRACE_KEY, $value, self::$user );
		}

		/**
		 * Sets the user grace period from meta.
		 *
		 * @param int|\WP_User|null $user - The WP user that must be used.
		 *
		 * @return void
		 *
		 * @since 2.2.0
		 */
		public static function remove_grace_period( $user = null ) {
			self::set_proper_user( $user );

			self::remove_meta( self::USER_GRACE_KEY, $user );
		}

		/**
		 * Checks if the user is locked. It only checks a single user meta field to keep this as fast as possible. The
		 * value of the field is updated elsewhere.
		 *
		 * @param int|\WP_User|null $user - The WP user that must be used.
		 *
		 * @return bool True if the user account is locked. False otherwise.
		 *
		 * @since 2.2.0
		 */
		public static function is_user_locked( $user = null ): bool {
			return (bool) self::get_grace_period( $user );
		}

		/**
		 * Checks if the given user has administrator or super administrator privileges.
		 *
		 * @param int|\WP_User|null $user - The WP user that must be used.
		 *
		 * @since 2.2.0
		 */
		public static function is_admin( $user = null ): bool {
			self::set_proper_user( $user );

			$is_admin = in_array( 'administrator', self::$user->roles, true ) || ( function_exists( 'is_super_admin' ) && is_super_admin( self::$user->ID ) );

			if ( ! $is_admin ) {
				return false;
			}

			return true;
		}

		/**
		 * Checks if user is excluded.
		 *
		 * @param int|\WP_User|null $user - The WP user we should extract the meta data for.
		 *
		 * @return mixed
		 *
		 * @since 2.2.0
		 */
		public static function is_excluded( $user = null ) {
			$state = self::get_user_state( $user );

			if ( 'excluded' !== $state ) {
				$user_role = self::get_user_role( $user );

				if ( Settings_Utils::string_to_bool( WP2FA::get_wp2fa_setting( 'superadmins-role-exclude' ) ) && is_super_admin( self::$user->ID ) ) {
					$state = 'excluded';
					self::set_user_state( $state, $user );
					self::remove_enabled_method_for_user( $user );
				}

				// User does not have role assigned, exclude them.
				if ( '' === $user_role ) {
					$state = 'excluded';
					self::set_user_state( $state, $user );
				}
			}

			return 'excluded' === $state;
		}

		/**
		 * Updates the user state based on the current plugin settings.
		 *
		 * @param int|\WP_User|null $user - The WP user we should extract the meta data for.
		 *
		 * @return string
		 *
		 * @since 2.3
		 */
		public static function update_user_state( $user = null ) {
			self::set_proper_user( $user );

			$enforcement_state = 'optional';
			if ( self::run_user_exclusion_check( self::get_user() ) ) {
				$enforcement_state = 'excluded';
			} elseif ( self::run_user_enforcement_check( self::get_user() ) ) {
				$enforcement_state = 'enforced';
			}

			self::set_user_state( $enforcement_state );

			// Clear enabled methods if excluded.
			if ( 'excluded' === $enforcement_state ) {
				self::remove_enabled_method_for_user();
			}

			return $enforcement_state;
		}

		/**
		 * Checks if user is enforced.
		 *
		 * @param int|\WP_User|null $user - The WP user we should extract the meta data for.
		 *
		 * @return mixed
		 *
		 * @since 2.2.0
		 */
		public static function is_enforced( $user = null ) {
			$state = self::get_user_state( $user );

			return 'enforced' === $state;
		}

		/**
		 * Returns the current user state stored.
		 *
		 * @param int|\WP_User|null $user - The WP user we should extract the meta data for.
		 *
		 * @return string
		 *
		 * @since 2.2.0
		 */
		public static function get_user_state( $user = null ) {
			self::set_proper_user( $user );

			$state = self::get_meta( self::USER_ENFORCEMENT_STATE );

			if ( empty( $state ) ) {
				$state = self::update_user_state();
			}

			return $state;
		}

		/**
		 * Returns the current user state stored.
		 *
		 * @param string            $state - The 2FA user state.
		 * @param int|\WP_User|null $user  - The WP user we should extract the meta data for.
		 *
		 * @return void
		 *
		 * @since 2.2.0
		 */
		public static function set_user_state( $state, $user = null ) {
			self::set_proper_user( $user );

			if ( ! in_array( $state, self::USER_STATE_STATUSES, true ) ) {
				$state = self::USER_STATE_STATUSES[0];
			}

			self::set_meta( self::USER_ENFORCEMENT_STATE, $state );
		}

		/**
		 * Removes 2FA meta for the given user.
		 *
		 * @param int|\WP_User|null $user - The WP user we should extract the meta data for.
		 *
		 * @return void
		 *
		 * @since 2.2.2
		 */
		public static function remove_2fa_for_user( $user = null ) {
			self::set_proper_user( $user );

			self::remove_all_2fa_meta_for_user( $user );
		}

		/**
		 * Figures out the correct 2FA status of a user and stores it against the user in DB. The method is static
		 * because it is temporarily used in user listing to update user accounts created prior to version 1.7.0.
		 *
		 * @param \WP_User $user - The user which status should be set.
		 *
		 * @return string
		 *
		 * @see \WP2FA\Admin\User_Listing
		 * @since 1.7.0
		 */
		public static function set_user_status( \WP_User $user ) {
			$status      = User_Utils::determine_user_2fa_status( $user );
			$status_data = User_Utils::extract_statuses( $status );
			if ( ! empty( $status_data ) ) {
				self::set_2fa_status( $status_data['id'], $user );

				return $status_data['label'];
			}

			return '';
		}

		/**
		 * Send email to setup authentication.
		 *
		 * @param [type] $user_id - The ID of the user.
		 *
		 * @return bool
		 */
		public static function send_expired_grace_email( $user_id ) {
			// Bail if the user has not enabled this email.
			if ( 'enable_account_locked_email' !== WP2FA::get_wp2fa_email_templates( 'send_account_locked_email' ) ) {
				return false;
			}

			// Grab user data.
			$user = get_userdata( $user_id );
			// Grab user email.
			$email = $user->user_email;

			$subject = wp_strip_all_tags( WP2FA::replace_email_strings( WP2FA::get_wp2fa_email_templates( 'user_account_locked_email_subject' ), $user_id ) );
			$message = wpautop( WP2FA::replace_email_strings( WP2FA::get_wp2fa_email_templates( 'user_account_locked_email_body' ), $user_id ) );

			return Settings_Page::send_email( $email, $subject, $message );
		}

		/**
		 * Checks if user needs to reconfigure the method
		 *
		 * @param int|\WP_User|null $user - The WP user we should extract the meta data for.
		 *
		 * @return boolean
		 */
		public static function needs_to_reconfigure_method( $user = null ): bool {

			self::set_proper_user( $user );

			return ( ! empty( self::get_user_needs_to_reconfigure_2fa( self::get_user() ) ) && ! self::get_nag_status() && empty( self::get_enabled_method_for_user( self::get_user() ) ) );
		}

		/**
		 * Locks the user account if the grace period setting is configured and the user is currently out of their grace
		 * period. It also takes care of sending the "account locked" email to the user if not already sent before.
		 *
		 * @return bool True if the user account is locked. False otherwise.
		 */
		private static function lock_user_account_if_needed() {
			$settings = Settings_Utils::get_option( WP_2FA_POLICY_SETTINGS_NAME );
			if ( ! is_array( $settings ) || ( isset( $settings['enforcement-policy'] ) && 'do-not-enforce' === $settings['enforcement-policy'] ) ) {
				// 2FA is not enforced, make sure to clear any related user meta previously created
				self::remove_meta( WP_2FA_PREFIX . 'is_locked' );
				self::remove_user_expiry_date();
				self::remove_meta( WP_2FA_PREFIX . 'locked_account_notification' );

				return false;
			}

			if ( self::is_excluded() ) {
				return false;
			}

			$is_user_instantly_enforced = self::get_user_enforced_instantly();
			if ( $is_user_instantly_enforced ) {
				// no need to lock the account if the user is enforced to set 2FA up instantly.
				return false;
			}

			// Do not lock if user has 2FA configured.
			$has_enabled_method = self::get_2fa_status();
			if ( 'has_enabled_methods' === $has_enabled_method ) {
				return false;
			}

			$grace_period_expiry_time = self::get_user_expiry_date();
			$grace_period_expired     = ( ! empty( $grace_period_expiry_time ) && $grace_period_expiry_time < time() );
			if ( $grace_period_expired ) {

				/**
				 * Filter can be used to prevent locking of the user account when the grace period expires.
				 *
				 * @param boolean $should_be_locked Should account be locked? True by default.
				 * @param \WP_User $user WP_User object.
				 *
				 * @return boolean True if the user account should be locked.
				 * @since 2.0.0
				 */
				$should_be_locked = apply_filters( WP_2FA_PREFIX . 'should_account_be_locked_on_grace_period_expiration', true, self::get_user() );
				if ( ! $should_be_locked ) {
					return false;
				}

				// set "grace period expired" flag.
				self::set_grace_period( true );

				/**
				 * Allow 3rd party developers to execute additional code when grace period expires (account is locked)
				 *
				 * @param \WP_User $user WP_User object.
				 *
				 * @since 2.0.0
				 */
				do_action( WP_2FA_PREFIX . 'after_grace_period_expired', self::get_user() );

				/**
				 * Filter can be used to disable the email notification about locked user account.
				 *
				 * @param boolean $can_send Can the email notification be sent? True by default.
				 * @param \WP_User $user WP_User object.
				 *
				 * @return boolean True if the email notification can be sent.
				 * @since 2.0.0
				 */
				$notify_user = apply_filters( WP_2FA_PREFIX . 'send_account_locked_notification', true, self::get_user() );
				if ( $notify_user ) {
					// Send the email to alert the user, only if we have not done so before.
					$account_notification = get_user_meta( self::get_user()->ID, WP_2FA_PREFIX . 'locked_account_notification', true );
					if ( ! $account_notification ) {
						self::send_expired_grace_email( self::get_user()->ID );
						self::set_meta( WP_2FA_PREFIX . 'locked_account_notification', true );
					}
				}

				// Grab user session and kill it, preferably with fire.
				$manager = \WP_Session_Tokens::get_instance( self::get_user()->ID );
				$manager->destroy_all();

				return true;
			}

			return false;
		}

		/**
		 * Caches and returns the globally set excluded roles
		 *
		 * @return array
		 *
		 * @since 2.5.0
		 */
		private static function get_excluded_roles() {
			if ( null === self::$excluded_roles ) {
				self::$excluded_roles = WP2FA::get_wp2fa_setting( 'excluded_roles' );
			}

			return self::$excluded_roles;
		}

		/**
		 * Caches and returns the globally set enforced users
		 *
		 * @return array
		 *
		 * @since 2.5.0
		 */
		private static function get_enforced_users() {
			if ( null === self::$enforced_users ) {
				self::$enforced_users = WP2FA::get_wp2fa_setting( 'enforced_users' );
			}

			return self::$enforced_users;
		}

		/**
		 * Caches and returns the globally set excluded sites
		 *
		 * @return array
		 *
		 * @since 2.5.0
		 */
		private static function get_excluded_sites() {
			if ( null === self::$excluded_sites ) {
				self::$excluded_sites = WP2FA::get_wp2fa_setting( 'excluded_sites' );
			}

			return self::$excluded_sites;
		}

		/**
		 * Caches and returns the globally set excluded users
		 *
		 * @return array
		 *
		 * @since 2.5.0
		 */
		private static function get_excluded_users() {
			if ( null === self::$excluded_users ) {
				self::$excluded_users = WP2FA::get_wp2fa_setting( 'excluded_users' );
			}

			return self::$excluded_users;
		}

		/**
		 * Caches and returns the globally set included sites
		 *
		 * @return array
		 *
		 * @since 2.5.0
		 */
		private static function get_included_sites() {
			if ( null === self::$included_sites ) {
				self::$included_sites = WP2FA::get_wp2fa_setting( 'included_sites' );
			}

			return self::$included_sites;
		}

		/**
		 * Caches and returns the globally set enforced roles
		 *
		 * @return array
		 *
		 * @since 2.5.0
		 */
		private static function get_enforced_roles() {
			if ( null === self::$enforced_roles ) {
				self::$enforced_roles = WP2FA::get_wp2fa_setting( 'enforced_roles' );
			}

			return self::$enforced_roles;
		}

		/**
		 * Runs the necessary checks to figure out if the user is excluded based on current plugin settings.
		 *
		 * @param \WP_User $user User to evaluate.
		 * @param array    $roles - Array with user roles.
		 * @param string   $user_login - User login name.
		 * @param int      $user_id - The id of the user.
		 *
		 * @return bool True if the user is excluded based on current plugin settings.
		 * @since 2.0.0
		 *
		 * @since 2.5.0 added params $roles, $user_login, $user_id . $user is with highest priority
		 */
		public static function run_user_exclusion_check( $user = null, $roles = null, $user_login = null, $user_id = null ) {
			if ( null !== $user ) {
				$user_roles = $user->roles;
				$user_login = $user->user_login;
				$user_id    = $user->ID;
			} else {
				/**
				 * Setting that inner class flag because if we are here that means reports are generated, and we dont need to update users meta but just to check what is currently there.
				 */
				self::$update_started = true;
				$user_roles           = $roles;
			}
			$user_excluded  = false;
			$excluded_users = self::get_excluded_users();
			if ( ! empty( $excluded_users ) ) {

				// Compare our roles with the users and see if we get a match.
				$result = in_array( $user_login, $excluded_users, true );
				if ( $result ) {
					return true;
				}
			}

			$excluded_roles = self::get_excluded_roles();
			if ( ! empty( $excluded_roles ) ) {
				$excluded_roles = array_map( 'strtolower', $excluded_roles );
				// Compare our roles with the users and see if we get a match.
				$result = array_intersect( $excluded_roles, $user_roles );
				if ( ! empty( $result ) ) {
					return true;
				}
			}

			if ( WP_Helper::is_multisite() ) {
				$excluded_sites = self::get_excluded_sites();
				if ( ! empty( $excluded_sites ) && is_array( $excluded_sites ) ) {

					foreach ( $excluded_sites as $site_id ) {
						if ( is_user_member_of_blog( $user_id, $site_id ) ) {
							// User is a member of the blog we are excluding from 2FA.
							return true;
						} else {
							// User is NOT a member of the blog we are excluding.
							$user_excluded = false;
						}
					}
				}

				$included_sites = self::get_included_sites();
				if ( $included_sites && is_array( $included_sites ) ) {
					foreach ( $included_sites as $site_id ) {
						if ( is_user_member_of_blog( $user_id, $site_id ) ) {
							$user_excluded = false;
						}
					}
				}
			}

			return $user_excluded;
		}

		/**
		 * Runs the necessary checks to figure out if the user is enforced based on current plugin settings.
		 *
		 * @param \WP_User $user User to evaluate.
		 * @param array    $roles - Array with user roles.
		 * @param string   $user_login - User login name.
		 * @param int      $user_id - The id of the user.
		 *
		 * @return bool True if the user is enforced based on current plugin settings.
		 *
		 * @since 2.0.0
		 *
		 * @since 2.5.0 added params $roles, $user_login, $user_id . $user is with highest priority
		 */
		public static function run_user_enforcement_check( $user = null, $roles = null, $user_login = null, $user_id = null ) {
			if ( null !== $user ) {
				$user_roles = $user->roles;
				$user_login = $user->user_login;
				$user_id    = $user->ID;
			} else {
				/**
				 * Setting that inner class flag because if we are here that means reports are generated, and we dont need to update users meta but just to check what is currently there.
				 */
				self::$update_started = true;
				$user_roles           = $roles;
			}

			$current_policy = WP2FA::get_wp2fa_setting( 'enforcement-policy' );
			$enabled_method = self::get_enabled_method_for_user( $user_id );
			$user_eligible  = false;

			if ( Settings_Utils::string_to_bool( WP2FA::get_wp2fa_setting( 'superadmins-role-exclude' ) ) && is_super_admin( $user_id ) ) {
				return false;
			}

			// Let's check the policy settings and if the user has setup totp/email by checking for the usermeta.
			if ( empty( $enabled_method ) && WP_Helper::is_multisite() && 'superadmins-only' === $current_policy ) {
				return is_super_admin( $user_id );
			} elseif ( empty( $enabled_method ) && WP_Helper::is_multisite() && 'superadmins-siteadmins-only' === $current_policy ) {
				return self::is_admin( $user_id );
			} elseif ( 'all-users' === $current_policy && empty( $enabled_method ) ) {

				$excluded_users = self::get_excluded_users();
				if ( ! empty( $excluded_users ) ) {
					// Compare our roles with the users and see if we get a match.
					$result = in_array( $user_login, $excluded_users, true );
					if ( $result ) {
						return false;
					}

					$user_eligible = true;
				}

				$excluded_roles = self::get_excluded_roles();
				if ( ! empty( $excluded_roles ) ) {

					if ( ! WP_Helper::is_multisite() ) {
						// Compare our roles with the users and see if we get a match.
						$result = array_intersect( $excluded_roles, $user_roles );

						if ( ! empty( $result ) ) {
							return false;
						}
					} else {
						$users_caps = array();
						$subsites   = get_sites();
						// Check each site and add to our array so we know each users actual roles.
						foreach ( $subsites as $subsite ) {
							$subsite_id = get_object_vars( $subsite )['blog_id'];
							global $wpdb;

							if ( 1 === (int) $subsite_id ) {
								$users_caps[] = get_user_meta( $user_id, $wpdb->base_prefix . 'capabilities', true );
							} else {
								$users_caps[] = get_user_meta( $user_id, $wpdb->base_prefix . $subsite_id . '_capabilities', true );
							}
						}

						foreach ( $users_caps as $key => $value ) {
							if ( ! empty( $value ) ) {
								foreach ( $value as $key => $value ) {
									$result = in_array( $key, $excluded_roles, true );
								}
							}
						}
						if ( ! empty( $result ) ) {
							return false;
						}
					}
				}

				if ( true === $user_eligible || empty( $enabled_method ) ) {
					return true;
				}
			} elseif ( ( 'certain-roles-only' === $current_policy || 'certain-users-only' === $current_policy ) && empty( $enabled_method ) ) {
				$enforced_users = self::get_enforced_users();
				if ( ! empty( $enforced_users ) ) {

					// Compare our roles with the users and see if we get a match.
					$result = in_array( $user_login, $enforced_users, true );
					// The user is one of the chosen roles we are forcing 2FA onto, so lets show the nag.
					if ( ! empty( $result ) ) {
						return true;
					}
				}

				$enforced_roles = self::get_enforced_roles();
				if ( ! empty( $enforced_roles ) ) {
					// Turn it into an array.
					$enforced_roles_array = Settings_Page::extract_roles_from_input( $enforced_roles );

					if ( ! WP_Helper::is_multisite() ) {
						// Compare our roles with the users and see if we get a match.
						$result = array_intersect( $enforced_roles_array, $user_roles );

						// The user is one of the chosen roles we are forcing 2FA onto, so lets show the nag.
						if ( ! empty( $result ) ) {
							return true;
						}
					} else {
						$users_caps = array();
						$subsites   = get_sites();
						// Check each site and add to our array so we know each users actual roles.
						foreach ( $subsites as $subsite ) {
							$subsite_id = get_object_vars( $subsite )['blog_id'];

							global $wpdb;

							if ( 1 === (int) $subsite_id ) {
								$users_caps[] = get_user_meta( $user_id, $wpdb->prefix . 'capabilities', true );
							} else {
								$users_caps[] = get_user_meta( $user_id, $wpdb->prefix . $subsite_id . '_capabilities', true );
							}
						}

						foreach ( $users_caps as $role_in_site ) {
							if ( ! empty( $role_in_site ) ) {
								foreach ( array_keys( $role_in_site ) as $role ) {
									if ( in_array( $role, $enforced_roles_array, true ) ) {
										// User is enforced somewhere.
										return true;
									}
								}
							}
						}
						return false;
					}
				}

				if ( Settings_Utils::string_to_bool( WP2FA::get_wp2fa_setting( 'superadmins-role-add' ) ) ) {
					return is_super_admin( $user_id );
				}
			} elseif ( 'enforce-on-multisite' === $current_policy ) {
				$included_sites = self::get_included_sites();

				foreach ( $included_sites as $site_id ) {
					if ( is_user_member_of_blog( $user_id, $site_id ) ) {
						return true;
					}
				}
			} elseif ( 'all-users' === $current_policy && ! empty( $enabled_method ) ) {
				return true;
			}

			return false;
		}

		/**
		 * Runs the necessary checks to figure out if the user is enforced based on current plugin settings.
		 *
		 * @param \WP_User $user User to evaluate.
		 * @param array    $roles - Array with user roles.
		 * @param string   $user_login - User login name.
		 * @param int      $user_id - The id of the user.
		 *
		 * @return bool True if the user is enforced based on current plugin settings.
		 *
		 * @since 2.0.0
		 *
		 * @since 2.5.0 added params $roles, $user_login, $user_id . $user is with highest priority
		 */
		public static function is_user_enforced( $user = null, $roles = null, $user_login = null, $user_id = null ) {
			if ( null !== $user ) {
				$user_roles = $user->roles;
				$user_login = $user->user_login;
				$user_id    = $user->ID;
			} else {
				/**
				 * Setting that inner class flag because if we are here that means reports are generated, and we dont need to update users meta but just to check what is currently there.
				 */
				self::$update_started = true;
				$user_roles           = $roles;
			}

			$current_policy = WP2FA::get_wp2fa_setting( 'enforcement-policy' );
			$user_eligible  = false;

			if ( Settings_Utils::string_to_bool( WP2FA::get_wp2fa_setting( 'superadmins-role-exclude' ) ) && is_super_admin( $user_id ) ) {
				return false;
			}

			// Let's check the policy settings and if the user has setup totp/email by checking for the usermeta.
			if ( WP_Helper::is_multisite() && 'superadmins-only' === $current_policy ) {
				return is_super_admin( $user_id );
			} elseif ( WP_Helper::is_multisite() && 'superadmins-siteadmins-only' === $current_policy ) {
				return self::is_admin( $user_id );
			} elseif ( 'all-users' === $current_policy ) {

				$excluded_users = self::get_excluded_users();
				if ( ! empty( $excluded_users ) ) {
					// Compare our roles with the users and see if we get a match.
					$result = in_array( $user_login, $excluded_users, true );
					if ( $result ) {
						return false;
					}

					$user_eligible = true;
				}

				$excluded_roles = self::get_excluded_roles();
				if ( ! empty( $excluded_roles ) ) {

					if ( ! WP_Helper::is_multisite() ) {
						// Compare our roles with the users and see if we get a match.
						$result = array_intersect( $excluded_roles, $user_roles );

						if ( ! empty( $result ) ) {
							return false;
						}
					} else {
						$users_caps = array();
						$subsites   = get_sites();
						// Check each site and add to our array so we know each users actual roles.
						foreach ( $subsites as $subsite ) {
							$subsite_id = get_object_vars( $subsite )['blog_id'];
							global $wpdb;

							if ( 1 === (int) $subsite_id ) {
								$users_caps[] = get_user_meta( $user_id, $wpdb->base_prefix . 'capabilities', true );
							} else {
								$users_caps[] = get_user_meta( $user_id, $wpdb->base_prefix . $subsite_id . '_capabilities', true );
							}
						}

						foreach ( $users_caps as $key => $value ) {
							if ( ! empty( $value ) ) {
								foreach ( $value as $key => $value ) {
									$result = in_array( $key, $excluded_roles, true );
								}
							}
						}
						if ( ! empty( $result ) ) {
							return false;
						}
					}
				}

				if ( true === $user_eligible ) {
					return true;
				}
			} elseif ( ( 'certain-roles-only' === $current_policy || 'certain-users-only' === $current_policy ) ) {
				$enforced_users = self::get_enforced_users();
				if ( ! empty( $enforced_users ) ) {

					// Compare our roles with the users and see if we get a match.
					$result = in_array( $user_login, $enforced_users, true );
					// The user is one of the chosen roles we are forcing 2FA onto, so lets show the nag.
					if ( ! empty( $result ) ) {
						return true;
					}
				}

				$enforced_roles = self::get_enforced_roles();
				if ( ! empty( $enforced_roles ) ) {
					// Turn it into an array.
					$enforced_roles_array = Settings_Page::extract_roles_from_input( $enforced_roles );

					if ( ! WP_Helper::is_multisite() ) {
						// Compare our roles with the users and see if we get a match.
						$result = array_intersect( $enforced_roles_array, $user_roles );

						// The user is one of the chosen roles we are forcing 2FA onto, so lets show the nag.
						if ( ! empty( $result ) ) {
							return true;
						}
					} else {
						$users_caps = array();
						$subsites   = get_sites();
						// Check each site and add to our array so we know each users actual roles.
						foreach ( $subsites as $subsite ) {
							$subsite_id = get_object_vars( $subsite )['blog_id'];

							global $wpdb;

							if ( 1 === (int) $subsite_id ) {
								$users_caps[] = get_user_meta( $user_id, $wpdb->prefix . 'capabilities', true );
							} else {
								$users_caps[] = get_user_meta( $user_id, $wpdb->prefix . $subsite_id . '_capabilities', true );
							}
						}

						foreach ( $users_caps as $key => $value ) {
							if ( ! empty( $value ) ) {
								foreach ( $value as $key => $value ) {
									$result = in_array( $key, $enforced_roles_array, true );
								}
							}
						}
						if ( ! empty( $result ) ) {
							return true;
						}
					}
				}

				if ( Settings_Utils::string_to_bool( WP2FA::get_wp2fa_setting( 'superadmins-role-add' ) ) ) {
					return is_super_admin( $user_id );
				}
			} elseif ( 'enforce-on-multisite' === $current_policy ) {
				$included_sites = self::get_included_sites();

				foreach ( $included_sites as $site_id ) {
					if ( is_user_member_of_blog( $user_id, $site_id ) ) {
						return true;
					}
				}
			} elseif ( 'all-users' === $current_policy ) {
				return true;
			}

			return false;
		}

		/**
		 * Returns the nominated email for user.
		 *
		 * @param int|\WP_User|null $user - The WP user we should extract the meta data for.
		 *
		 * @return mixed
		 *
		 * @since 2.6.0
		 */
		public static function get_nominated_email_for_user( $user = null ) {
			self::set_proper_user( $user );

			$email = self::get_meta( self::USER_NOMINATED_EMAIL );

			if ( empty( $email ) || ! isset( $email ) ) {
				$email = self::get_user()->user_email;
			}

			return $email;
		}

		/**
		 * Sets the nominated email for the user. If the email is the same as the current user email from the WP - the meta is not populated.
		 *
		 * @param string            $email - The token to set for the user.
		 * @param int|\WP_User|null $user  - The WP user we should extract the meta data for.
		 *
		 * @return mixed
		 *
		 * @since 2.6.0
		 */
		public static function set_nominated_email_for_user( string $email, $user = null ) {
			self::set_proper_user( $user );

			$email = \sanitize_email( \wp_unslash( $email ) );

			if ( ! empty( $email ) ) {
				if ( self::get_user()->user_email !== $email ) {
					return self::set_meta( self::USER_NOMINATED_EMAIL, $email );
				} else {
					self::remove_nominated_email_for_user( $user );
				}
			}

			return false;
		}

		/**
		 * Removes the nominated email for the user.
		 *
		 * @param int|\WP_User|null $user - The WP user we should extract the meta data for.
		 *
		 * @return void
		 *
		 * @since 2.6.0
		 */
		public static function remove_nominated_email_for_user( $user = null ) {
			self::set_proper_user( $user );

			self::remove_meta( self::USER_NOMINATED_EMAIL, self::$user );
		}


		/**
		 * Returns the backup email for user. If the data stored in meta is = 'wp_mail' that means that the user email should be extracted from the WP BE.
		 *
		 * @param int|\WP_User|null $user - The WP user we should extract the meta data for.
		 *
		 * @return mixed
		 *
		 * @since 2.6.0
		 */
		public static function get_backup_email_for_user( $user = null ) {
			self::set_proper_user( $user );

			$email = self::get_meta( self::USER_BACKUP_EMAIL );

			if ( empty( $email ) || ! isset( $email ) ) {
				$email = self::get_user()->user_email;
			}

			if ( isset( $email ) && 'wp_mail' === $email ) {
				$email = self::get_user()->user_email;
			}

			return $email;
		}

		/**
		 * Sets the backup email for the user. If the email is the same as the current user email from the WP - the meta is not populated.
		 *
		 * @param string            $email - The token to set for the user.
		 * @param int|\WP_User|null $user  - The WP user we should extract the meta data for.
		 *
		 * @return mixed
		 *
		 * @since 2.6.0
		 */
		public static function set_backup_email_for_user( string $email, $user = null ) {
			self::set_proper_user( $user );

			$email = \sanitize_email( \wp_unslash( $email ) );

			if ( ! empty( $email ) ) {
				if ( self::get_user()->user_email !== $email ) {
					return self::set_meta( self::USER_BACKUP_EMAIL, $email );
				} elseif ( self::get_user()->user_email === $email ) {
					return self::set_meta( self::USER_BACKUP_EMAIL, 'wp_mail' );
				} else {
					self::remove_backup_email_for_user( $user );
				}
			}

			return false;
		}

		/**
		 * Removes the backup email for the user.
		 *
		 * @param int|\WP_User|null $user - The WP user we should extract the meta data for.
		 *
		 * @return void
		 *
		 * @since 2.6.0
		 */
		public static function remove_backup_email_for_user( $user = null ) {
			self::set_proper_user( $user );

			self::remove_meta( self::USER_BACKUP_EMAIL, self::$user );
		}

		/**
		 * Updates teh user metadata. Checks for changes in the global settings, and if it finds some, checks these against the given user metadata settings hash and updates the user metadata if necessary.
		 *
		 * @return void
		 *
		 * @since 2.4.1
		 */
		private static function update_meta_if_necessary() {
			$global_settings_hash = Settings_Utils::get_option( WP_2FA_PREFIX . 'settings_hash' );
			if ( ! empty( $global_settings_hash ) ) {
				$stored_hash = self::get_global_settings_hash_for_user( self::get_user() );
				if ( $global_settings_hash !== $stored_hash ) {
					self::set_global_settings_hash_for_user( $global_settings_hash, self::get_user() );
					// update necessary user attributes (user meta) based on changed settings; the enforcement check
					// needs to run first as function "set_user_policies_and_grace" relies on having the correct values.
					self::check_methods_and_set_user();
					self::update_user_state( self::get_user() );
					self::set_user_policies_and_grace();
					self::remove_backup_methods( self::get_user() );
				}
				self::lock_user_account_if_needed();
			}
		}

		/**
		 * Sets the proper user policies and grace.
		 *
		 * @return void
		 *
		 * @since 2.4.1
		 */
		private static function set_user_policies_and_grace() {
			$enabled_methods_for_the_user = self::get_enabled_method_for_user( self::get_user() );
			if ( ! empty( $enabled_methods_for_the_user ) ) {
				self::remove_user_enforced_instantly( self::get_user() );
				self::remove_user_expiry_date( self::get_user() );
				self::remove_user_needs_to_reconfigure_2fa( self::get_user() );
				self::set_user_status( self::get_user() );

				return;
			}

			if ( self::is_enforced( self::get_user()->ID ) ) {
				$grace_policy = Settings::get_role_or_default_setting( 'grace-policy', self::get_user() );

				// Check if want to apply the custom period, or instant expiry.
				if ( 'use-grace-period' === $grace_policy ) {
					$custom_grace_period_duration =
					Settings::get_role_or_default_setting( 'grace-period', self::get_user() ) . ' ' . Settings::get_role_or_default_setting( 'grace-period-denominator', self::get_user() );
					$grace_expiry                 = strtotime( $custom_grace_period_duration );
					self::remove_user_enforced_instantly( self::get_user() );
				} else {
					$grace_expiry = time();
				}

				self::set_user_expiry_date( (string) $grace_expiry, self::get_user() );
				if ( 'no-grace-period' === $grace_policy ) {
					self::set_user_enforced_instantly( true, self::get_user() );
				}
			} else {
				self::remove_user_enforced_instantly( self::get_user() );
				self::remove_user_expiry_date( self::get_user() );
				self::remove_user_needs_to_reconfigure_2fa( self::get_user() );
			}

			// update the 2FA status meta field.
			self::set_user_status( self::get_user() );
		}

		/**
		 * Checks the user methods and sets the user status.
		 *
		 * @return void
		 *
		 * @since 2.4.1
		 */
		private static function check_methods_and_set_user() {
			if ( ! self::get_user_needs_to_reconfigure_2fa( self::get_user() ) ) {
				$enabled_methods_for_the_user = self::get_enabled_method_for_user( self::get_user() );

				if ( empty( $enabled_methods_for_the_user ) ) {
					return;
				}

				$global_methods = Methods::get_available_2fa_methods();
				if ( empty( \array_intersect( array( $enabled_methods_for_the_user ), $global_methods ) ) ) {
					self::remove_enabled_method_for_user( self::get_user() );
					if ( self::is_enforced( self::get_user()->ID ) ) {
						self::set_user_needs_to_reconfigure_2fa( true, self::get_user() );
					}
				}
			}
		}

		/**
		 * Calls all the backup methods and gives them and option to remove their stored values.
		 *
		 * @param int|\WP_User|null $user - The WP user we should extract the meta data for.
		 *
		 * @return void
		 *
		 * @since 2.5.0
		 */
		private static function remove_backup_methods( $user = null ) {
			self::set_proper_user( $user );
			\do_action( WP_2FA_PREFIX . 'remove_backup_methods_for_user', self::get_user() );
		}

		/**
		 * Sets the local variable class based on the given parameter.
		 *
		 * @param int|\WP_User|null $user - The WP user we should extract the meta data for.
		 *
		 * @return void
		 *
		 * @since 2.2.0
		 */
		private static function set_proper_user( $user = null ) {
			if ( null !== $user ) {
				self::set_user( $user );
			} else {
				self::get_user();
			}

			if ( false !== self::$user && 0 !== self::$user->ID && false === self::$update_started ) {
				self::$update_started = true;

				self::update_meta_if_necessary();
			}
		}
	}
}
includes/classes/Admin/class-user-notices.php000064400000023710150755130600015300 0ustar00<?php
/**
 * Responsible for WP2FA user's notifying.
 *
 * @package    wp2fa
 * @subpackage user-utils
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 * @link       https://wordpress.org/plugins/wp-2fa/
 */

namespace WP2FA\Admin;

use WP2FA\WP2FA;
use WP2FA\Extensions_Loader;
use WP2FA\Admin\Helpers\WP_Helper;
use WP2FA\Freemius\User_Licensing;
use WP2FA\Admin\Controllers\Methods;
use WP2FA\Admin\Helpers\User_Helper;
use WP2FA\Admin\Controllers\Settings;
use WP2FA\Admin\Views\Grace_Period_Notifications;
use WP2FA\Extensions\WhiteLabeling\White_Labeling_Render;

/**
 * User_Notices class with user notification filters
 *
 * @since 2.4.0
 */
if ( ! class_exists( '\WP2FA\Admin\User_Notices' ) ) {
	/**
	 * User_Notices - Class for displaying notices to our users.
	 */
	class User_Notices {

		/**
		 * Lets set things up
		 */
		public static function init() {
			$enforcement_policy = WP2FA::get_wp2fa_setting( 'enforcement-policy' );
			if ( ! empty( $enforcement_policy ) ) {
				// Check we are supposed to, before adding action to show nag.
				if ( in_array( $enforcement_policy, array( 'all-users', 'certain-roles-only', 'certain-users-only', 'superadmins-only', 'superadmins-siteadmins-only', 'enforce-on-multisite', true ), true ) ) {
					$global_methods = Methods::get_available_2fa_methods();
					$user           = User_Helper::get_user_object();
					$users_method   = User_Helper::get_enabled_method_for_user( User_Helper::get_user_object() );

					if ( Grace_Period_Notifications::notify_using_dashboard( User_Helper::get_user_object() ) ) {
						add_action( 'admin_notices', array( __CLASS__, 'user_setup_2fa_nag' ) );
						add_action( 'network_admin_notices', array( __CLASS__, 'user_setup_2fa_nag' ) );
					}

					// If enaabled method is no longer available, show nag so users reconfigures using an available remaining method.
					if ( User_Helper::is_enforced( $user ) && ! empty( $users_method ) && empty( \array_intersect( array( $users_method ), $global_methods ) ) ) {
					}
				} elseif ( 'do-not-enforce' === WP2FA::get_wp2fa_setting( 'enforcement-policy' ) ) {
					add_action( 'admin_notices', array( __CLASS__, 'user_reconfigure_2fa_nag' ) );
					add_action( 'network_admin_notices', array( __CLASS__, 'user_setup_2fa_nag' ) );
				}
			}
		}

		/**
		 * The nag content
		 *
		 * @param string $is_shortcode - Is that a call from shortcode.
		 * @param string $configure_2fa_url - The configuration url.
		 *
		 * @return void
		 */
		public static function user_setup_2fa_nag( $is_shortcode = '', $configure_2fa_url = '' ) {

			if ( isset( $_GET['user_id'] ) ) { // phpcs:ignore
				$current_profile_user_id = (int) $_GET['user_id']; // phpcs:ignore
			} elseif ( ! is_null( User_Helper::get_user_object() ) ) {
				$current_profile_user_id = User_Helper::get_user_object()->ID;
			} else {
				$current_profile_user_id = false;
			}

			if ( ! $current_profile_user_id ||
			isset( $_GET['user_id'] ) && // phpcs:ignore
			$_GET['user_id'] !== User_Helper::get_user_object()->ID || // phpcs:ignore
			User_Helper::get_user_enforced_instantly( User_Helper::get_user_object() ) ) {
				return;
			}

			$grace_expiry = (int) User_Helper::get_user_expiry_date( User_Helper::get_user_object() );

			$class = 'notice notice-info wp-2fa-nag';

			if ( User_Helper::get_user_needs_to_reconfigure_2fa( User_Helper::get_user_object() ) ) {
				$message = WP2FA::get_wp2fa_white_label_setting( 'default-2fa-resetup-required-notice', true );
			} else {
				$message = WP2FA::get_wp2fa_white_label_setting( 'default-2fa-required-notice', true );
			}

			$is_nag_dismissed = User_Helper::get_nag_status();
			$is_nag_needed    = User_Helper::is_enforced( User_Helper::get_user_object()->ID );
			$is_user_excluded = User_Helper::is_excluded( User_Helper::get_user_object()->ID );
			$enabled_methods  = User_Helper::get_enabled_method_for_user( User_Helper::get_user_object() );
			$new_page_id      = WP2FA::get_wp2fa_setting( 'custom-user-page-id' );

			if ( empty( $new_page_id ) ) {
				$new_page_id = Settings::get_custom_settings_page_id( '', User_Helper::get_user_object() );
			}

			$new_page_permalink = get_permalink( $new_page_id );

			$setup_url = Settings::get_setup_page_link();

			// Allow setup URL to be customized if outputting via shortcode.
			if ( isset( $is_shortcode ) && 'output_shortcode' === $is_shortcode && ! empty( $configure_2fa_url ) ) {
				$setup_url = $configure_2fa_url;
			}

			// Stop the page from being a link to a page this user cant access if needed.
			if ( WP_Helper::is_multisite() && ! is_user_member_of_blog( User_Helper::get_user_object()->ID ) ) {
				$new_page_id = false;
			}

			// If we have a custom page generated, lets use it.
			if ( ! empty( $new_page_id ) && $new_page_permalink ) {
				$setup_url = $new_page_permalink;
			}

			// If the nag has not already been dismissed, and of course if the user is eligible, lets show them something.
			if ( ! $is_nag_dismissed && $is_nag_needed && empty( $enabled_methods ) && ! $is_user_excluded && ! empty( $grace_expiry ) ) {

				$show = true;

				if ( class_exists( '\WP2FA\Freemius\User_Licensing' ) ) {
					if ( Extensions_Loader::use_proxytron() ) {
						$show = User_Licensing::enable_2fa_user_setting( true );
					}
				}

				if ( $show ) {
					echo '<div class="' . \esc_attr( $class ) . '">';
					echo wpautop( \wp_kses_post( WP2FA::replace_remaining_grace_period( $message, (int) $grace_expiry ) ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
					echo ' <a href="' . \esc_url( $setup_url ) . '" class="button button-primary">' . \esc_html__( 'Configure 2FA now', 'wp-2fa' ) . '</a>';
					echo ' <a href="#" class="button button-secondary dismiss-user-configure-nag">' . \esc_html__( 'Remind me on next login', 'wp-2fa' ) . '</a></p>';
					echo '</div>';
				}
			} else {
				self::user_reconfigure_2fa_nag();
			}
		}

		/**
		 * The nag content
		 */
		public static function user_reconfigure_2fa_nag() {

			// If the nag has not already been dismissed, and of course if the user is eligible, lets show them something.
			if ( User_Helper::needs_to_reconfigure_method() ) {
				$class = 'notice notice-info wp-2fa-nag';

				$message = \esc_html__( 'The 2FA method you were using is no longer allowed on this website. Please reconfigure 2FA using one of the supported methods.', 'wp-2fa' );

				echo '<div class="' . \esc_attr( $class ) . '"><p>' . \esc_html( $message );
				echo ' <a href="' . \esc_url( Settings::get_setup_page_link() ) . '" class="button button-primary">' . \esc_html__( 'Configure 2FA now', 'wp-2fa' ) . '</a>';
				echo '  <a href="#" class="button button-secondary wp-2fa-button-secondary dismiss-user-reconfigure-nag">' . \esc_html__( 'I\'ll do it later', 'wp-2fa' ) . '</a></p>';
				echo '</div>';
			}
		}


		/**
		 * Dismiss notice and setup a user meta value so we know its been dismissed
		 */
		public static function dismiss_nag() {
			User_Helper::set_nag_status( true );
		}

		/**
		 * Reset the nag when the user logs out, so they get it again next time.
		 *
		 * @param [type] $user_id - The ID of the user.
		 *
		 * @return void
		 */
		public static function reset_nag( $user_id ) {
			User_Helper::remove_nag_status( $user_id );
		}

		/**
		 * Adds setting option in the white label settings page.
		 *
		 * @return void
		 *
		 * @since 2.5.0
		 */
		public static function white_label_settings_text() {
			?>
			<tr>
				<th><label for="email-backup-method"><?php \esc_html_e( '2FA mandatory notice', 'wp-2fa' ); ?></label></th>
				<td>
					<?php
						echo White_Labeling_Render::get_method_text_editor( 'default-2fa-required-notice' ); // phpcs:ignore
					?>
					<div style="margin-top: 5px;"><span><strong><i><?php \esc_html_e( 'Note:', 'wp-2fa' ); ?></i></strong> <?php \esc_html_e( 'Only plain text is allowed.', 'wp-2fa' ); ?></span></div>
				</td>
			</tr>
			<tr>
				<th><label for="email-backup-method"><?php \esc_html_e( '2FA reconfiguration mandatory notice', 'wp-2fa' ); ?></label></th>
				<td>
					<?php
						echo White_Labeling_Render::get_method_text_editor( 'default-2fa-resetup-required-notice' ); // phpcs:ignore
					?>
					<div style="margin-top: 5px;"><span><strong><i><?php \esc_html_e( 'Note:', 'wp-2fa' ); ?></i></strong> <?php \esc_html_e( 'Only plain text is allowed.', 'wp-2fa' ); ?></span></div>
				</td>
			</tr>
			<tr>
				<th><label><?php \esc_html_e( 'User profile 2FA configuration area title', 'wp-2fa' ); ?></label></th>
				<td>
					<?php
						echo White_Labeling_Render::get_method_text_editor( 'user-profile-form-preamble-title' ); // phpcs:ignore
					?>
					<div style="margin-top: 5px;"><span><strong><i><?php \esc_html_e( 'Note:', 'wp-2fa' ); ?></i></strong> <?php \esc_html_e( 'Only plain text is allowed.', 'wp-2fa' ); ?></span></div>
				</td>
			</tr>
			<tr>
			<th><label><?php \esc_html_e( 'User profile 2FA configuration area description', 'wp-2fa' ); ?></label></th>
				<td>
					<?php
						echo White_Labeling_Render::get_method_text_editor( 'user-profile-form-preamble-desc' ); // phpcs:ignore
					?>
					<div style="margin-top: 5px;"><span><strong><i><?php \esc_html_e( 'Note:', 'wp-2fa' ); ?></i></strong> <?php \esc_html_e( 'Only plain text is allowed.', 'wp-2fa' ); ?></span></div>
				</td>
			</tr>
			<?php
			// phpcs:disable
			// phpcs:enable
			?>
			<?php
		}

		/**
		 * Adds and filters extension values in the settings store array ($output).
		 *
		 * @param array $output - Array with the currently stored settings.
		 * @param array $input  - Array with the input ($_POST) values.
		 *
		 * @return array
		 *
		 * @since 2.5.0
		 */
		public static function settings_store( array $output, array $input ) {
			if ( isset( $input['default-2fa-required-notice'] ) ) {
				$output['default-2fa-required-notice'] = \wp_kses_post( $input['default-2fa-required-notice'] );
			}

			if ( isset( $input['default-2fa-resetup-required-notice'] ) ) {
				$output['default-2fa-resetup-required-notice'] = \wp_kses_post( $input['default-2fa-resetup-required-notice'] );
			}

			return $output;
		}
	}
}
includes/classes/Admin/SettingsPages/class-settings-page-render.php000064400000014633150755130600021473 0ustar00<?php
/**
 * Settings page render class.
 *
 * @package    wp2fa
 * @subpackage views
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 * @link       https://wordpress.org/plugins/wp-2fa/
 */

namespace WP2FA\Admin\SettingsPages;

use WP2FA\WP2FA;
use WP2FA\Admin\Helpers\WP_Helper;

if ( ! class_exists( '\WP2FA\Admin\SettingsPages\Settings_Page_Render' ) ) {
	/**
	 * Settings_Page_Render - Class for rendering the plugin settings settings
	 *
	 * @since 2.0.0
	 */
	class Settings_Page_Render {

		/**
		 * Render the settings
		 */
		public static function render() {
			if ( ! current_user_can( 'manage_options' ) ) {
				return;
			}

			$main_user       = get_current_user_id();
			$current_user_id = $main_user;

			if ( ! empty( WP2FA::get_wp2fa_setting( '2fa_settings_last_updated_by' ) ) ) {
				$main_user = (int) WP2FA::get_wp2fa_setting( '2fa_settings_last_updated_by' );
			}
			?>

		<div class="wrap wp-2fa-settings-wrapper wp2fa-form-styles">
			<h2><?php \esc_html_e( 'WP 2FA Settings', 'wp-2fa' ); ?></h2>
			<hr>
			<br>			
			<?php if ( ! empty( WP2FA::get_wp2fa_general_setting( 'limit_access' ) ) && $main_user !== $current_user_id ) { ?>
				<?php
				echo \esc_html__( 'These settings have been disabled by your site administrator, please contact them for further assistance.', 'wp-2fa' );
				?>
			<?php } else { ?>
				<?php
					/**
					 * Fires before the plugin settings rendering.
					 *
					 * @since 2.0.0
					 */
					do_action( WP_2FA_PREFIX . 'before_plugin_settings' );
				?>
				<div class="nav-tab-wrapper">
					<?php
					$settings = self::settings_array();

					/**
					 * Stores the default settings key, so there is no need to walk the entire array again to extract that value
					 */
					$default_settings_key = 'generic-settings';

					foreach ( $settings as $setting_tab => $setting_values ) {
						$active_class = '';
						if ( ! isset( $_REQUEST['tab'] ) && $setting_values['default'] ) { // phpcs:ignore
							$active_class         = 'nav-tab-active';
							$default_settings_key = $setting_tab;
						} elseif ( isset( $_REQUEST['tab'] ) && $setting_tab === $_REQUEST['tab'] ) { // phpcs:ignore
							$active_class = 'nav-tab-active';
						}
						echo '<a href="' . $setting_values['url'] . '" class="nav-tab ' . $active_class . '">' . $setting_values['name'] . '</a>'; // phpcs:ignore
					}
					?>
				</div>
					<?php
					$show_tab = $default_settings_key;

					if ( isset( $_REQUEST['tab'] ) && array_key_exists( $_REQUEST['tab'], $settings ) ) { // phpcs:ignore
						$show_tab = \sanitize_text_field( \wp_unslash( $_REQUEST['tab'] ) ); // phpcs:ignore
					}

					if ( WP_Helper::is_multisite() ) {
						$action = 'edit.php?action=' . $settings[ $show_tab ]['network_action'];
					} else {
						$action = 'options.php';
					}
					?>
					<br/>
					<?php
						$settings[ $show_tab ]['description'];
					?>
					<br/>
					<form id="wp-2fa-admin-settings" action='<?php echo \esc_attr( $action ); ?>' method='post' autocomplete="off" >
						<?php
						\call_user_func( array( $settings[ $show_tab ]['class'], $settings[ $show_tab ]['method'] ) );
						?>
					</form>
			<?php } ?>
		</div>
			<?php
		}

		/**
		 * Holds the array with all the settings of the plugin. Fires filter, so third parties could change these settings.
		 *
		 * @return array
		 *
		 * @since 2.2.0
		 */
		private static function settings_array(): array {			
			$email_settings_name = \esc_html__( 'Emails & templates', 'wp-2fa' );
			$settings_tabs = array(
				'generic-settings'     => array(
					'url'            => \esc_url(
						add_query_arg(
							array(
								'page' => 'wp-2fa-settings',
								'tab'  => 'generic-settings',
							),
							network_admin_url( 'admin.php' )
						)
					),
					'name'           => \esc_html__( 'General settings', 'wp-2fa' ),
					'default'        => true,
					'description'    => sprintf(
						'<p class="description">%1$s <a href="mailto:support@melapress.com">%2$s</a></p>',
						\esc_html__( 'Use the settings below to configure the properties of the two-factor authentication on your website and how users use it. If you have any questions send us an email at', 'wp-2fa' ),
						\esc_html__( 'support@melapress.com', 'wp-2fa' )
					),
					'class'          => 'WP2FA\Admin\SettingsPages\Settings_Page_General',
					'method'         => 'render',
					'network_action' => 'update_wp2fa_network_options',
				),
				'email-settings'       => array(
					'url'            => \esc_url(
						add_query_arg(
							array(
								'page' => 'wp-2fa-settings',
								'tab'  => 'email-settings',
							),
							network_admin_url( 'admin.php' )
						)
					),
					'name'           => $email_settings_name,
					'default'        => false,
					'description'    => sprintf(
						'<p class="description">%1$s <a href="mailto:support@melapress.com">%2$s</a></p>',
						\esc_html__( 'Use the settings below to configure the properties of the two-factor authentication on your website and how users use it. If you have any questions send us an email at', 'wp-2fa' ),
						\esc_html__( 'support@melapress.com', 'wp-2fa' )
					),
					'class'          => 'WP2FA\Admin\SettingsPages\Settings_Page_Email',
					'method'         => 'render',
					'network_action' => 'update_wp2fa_network_email_options',
				),
				'white-label-settings' => array(
					'url'            => \esc_url(
						add_query_arg(
							array(
								'page' => 'wp-2fa-settings',
								'tab'  => 'white-label-settings',
							),
							network_admin_url( 'admin.php' )
						)
					),
					'name'           => \esc_html__( 'White labeling', 'wp-2fa' ),
					'default'        => false,
					'description'    => sprintf(
						'<p class="description">%1$s <a href="mailto:support@melapress.com">%2$s</a></p>',
						\esc_html__( 'Use the settings below to configure the emails which are sent to users as part of the 2FA plugin. If you have any questions send us an email at', 'wp-2fa' ),
						\esc_html__( 'support@melapress.com', 'wp-2fa' )
					),
					'class'          => 'WP2FA\Admin\SettingsPages\Settings_Page_White_Label',
					'method'         => 'render',
					'network_action' => 'update_wp2fa_network_options',
				),
			);

			/**
			* Filter: `Settings tabs`
			*
			* Gives an option for third parties to alter the plugin settings page
			*
			* @param array $settings_tabs – Settings tabs.
			*/
			return \apply_filters( WP_2FA_PREFIX . 'settings_tabs', $settings_tabs );
		}
	}
}
includes/classes/Admin/SettingsPages/index.php000064400000000046150755130600015441 0ustar00<?php
/**
 * Nothing to see here.
 */
includes/classes/Admin/SettingsPages/class-settings-page-general.php000064400000026225150755130600021631 0ustar00<?php
/**
 * Generals settings class.
 *
 * @package    wp2fa
 * @subpackage settings-pages
 *
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 *
 * @see       https://wordpress.org/plugins/wp-2fa/
 */

namespace WP2FA\Admin\SettingsPages;

use WP2FA\Utils\Debugging;
use WP2FA\Utils\Settings_Utils;
use WP2FA\WP2FA;

/*
 * General settings tab
 */
if ( ! class_exists( '\WP2FA\Admin\SettingsPages\Settings_Page_General' ) ) {
	/**
	 * Settings_Page_General - Class for handling general settings.
	 *
	 * @since 2.0.0
	 */
	class Settings_Page_General {
		/**
		 * Renders the settings.
		 *
		 * @return void
		 *
		 * @since 2.0.0
		 */
		public static function render() {
			settings_fields( WP_2FA_SETTINGS_NAME );
			self::no_method_exists();
			self::disable_brute_force_settings();
			self::limit_settings_access();
			self::remove_data_upon_uninstall();
			submit_button( null, 'primary', WP_2FA_SETTINGS_NAME . '[submit]' );
		}

		/**
		 * Validate options before saving.
		 *
		 * @param array $input The settings array.
		 *
		 * @return array|void
		 */
		public static function validate_and_sanitize( $input ) {
			// Bail if user doesn't have permissions to be here.
			if ( ! current_user_can( 'manage_options' ) || ! isset( $_POST['action'] ) && ! check_admin_referer( 'wp2fa-step-choose-method' ) ) {
				return;
			}

			Debugging::log( 'The following settings will be processed (General): ' . "\n" . wp_json_encode( $input ) );

			$simple_settings_we_can_loop = array(
				'enable_destroy_session',
				'limit_access',
				'brute_force_disable',
				'delete_data_upon_uninstall',
				'method_invalid_setting',
			);

			/**
			 * Gives the ability to change the default general settings.
			 *
			 * @param array $general_settings - The array with the default settings.
			 *
			 * @since 2.0.0
			 */
			$simple_settings_we_can_loop = \apply_filters( WP_2FA_PREFIX . 'loop_general_settings', $simple_settings_we_can_loop );

			$settings_to_turn_into_bools = array(
				'enable_destroy_session',
				'limit_access',
				'brute_force_disable',
				'delete_data_upon_uninstall',
			);

			foreach ( $simple_settings_we_can_loop as $simple_setting ) {
				if ( ! in_array( $simple_setting, $settings_to_turn_into_bools, true ) ) {
					// Is item is not one of our possible settings we want to turn into a bool, process.
					$output[ $simple_setting ] = ( isset( $input[ $simple_setting ] ) && ! empty( $input[ $simple_setting ] ) ) ? trim( (string) sanitize_text_field( $input[ $simple_setting ] ) ) : false;
				} else {
					// This item is one we treat as a bool, so process correctly.
					$output[ $simple_setting ] = ( isset( $input[ $simple_setting ] ) && ! empty( $input[ $simple_setting ] ) ) ? true : false;
				}
			}

			if ( isset( $input['2fa_settings_last_updated_by'] ) && ! empty( $input['2fa_settings_last_updated_by'] ) ) {
				$policies = WP2FA::get_wp2fa_setting();
				if ( false === $policies ) {
					$policies = WP2FA::get_default_settings();
				}
				$policies['2fa_settings_last_updated_by'] = (int) get_current_user_id();

				WP2FA::update_plugin_settings( $policies );
			}

			// Remove duplicates from settings errors. We do this as this sanitization callback is actually fired twice, so we end up with duplicates when saving the settings for the FIRST TIME only. The issue is not present once the settings are in the DB as the sanitization wont fire again. For details on this core issue - https://core.trac.wordpress.org/ticket/21989.
			global $wp_settings_errors;
			if ( isset( $wp_settings_errors ) ) {
				$errors             = array_map( 'unserialize', array_unique( array_map( 'serialize', $wp_settings_errors ) ) );
				$wp_settings_errors = $errors; // phpcs:ignore
			}

			/**
			 * Filter the values we are about to store in the plugin settings.
			 *
			 * @param array $output - The output array with all the data we will store in the settings.
			 * @param array $input  - The input array with all the data we received from the user.
			 *
			 * @since 2.0.0
			 */
			$output = \apply_filters( WP_2FA_PREFIX . 'filter_output_content_general_settings', $output, $input );

			// We have overridden any defaults by now so can clear this.
			Settings_Utils::delete_option( WP_2FA_PREFIX . 'default_settings_applied' );

			Debugging::log( 'The following settings are being saved (General): ' . "\n" . wp_json_encode( $output ) );

			return $output;
		}

		/**
		 * Updates global settings network options.
		 *
		 * @return void
		 *
		 * @SuppressWarnings(PHPMD.ExitExpressions)
		 */
		public static function update_wp2fa_network_options() {
			if ( isset( $_POST[ WP_2FA_SETTINGS_NAME ] ) ) {
				check_admin_referer( 'wp_2fa_settings-options' );
				$options = self::validate_and_sanitize(wp_unslash($_POST[WP_2FA_SETTINGS_NAME])); // phpcs:ignore
				$settings_errors = get_settings_errors( WP_2FA_SETTINGS_NAME );
				if ( ! empty( $settings_errors ) ) {
					// redirect back to our options page.
					wp_safe_redirect(
						add_query_arg(
							array(
								'page' => 'wp-2fa-settings',
								'wp_2fa_network_settings_error' => urlencode_deep( $settings_errors[0]['message'] ),
							),
							network_admin_url( 'settings.php' )
						)
					);
					exit;
				}
				WP2FA::update_plugin_settings( $options, false, WP_2FA_SETTINGS_NAME );

				// redirect back to our options page.
				wp_safe_redirect(
					add_query_arg(
						array(
							'page' => 'wp-2fa-settings',
							'tab'  => 'generic-settings',
							'wp_2fa_network_settings_updated' => 'true',
						),
						network_admin_url( 'admin.php' )
					)
				);
				exit;
			}
		}

		/**
		 * Limit settings setting.
		 *
		 * @return void
		 *
		 * @since 2.0.0
		 */
		private static function remove_data_upon_uninstall() {
			?>
		<div class="danger-zone-wrapper">
			<h3><?php \esc_html_e( 'Do you want to delete the plugin data from the database upon uninstall', 'wp-2fa' ); ?></h3>
			<p class="description">
				<?php \esc_html_e( 'The plugin saves its settings in the WordPress database. By default the plugin settings are kept in the database so if it is installed again, you do not have to reconfigure the plugin. Enable this setting to delete the plugin settings from the database upon uninstall.', 'wp-2fa' ); ?>
			</p>
			<table class="form-table">
				<tbody>
					<tr>
						<th><label for="delete_data"><?php \esc_html_e( 'Delete data', 'wp-2fa' ); ?></label></th>
						<td>
							<fieldset>
								<input type="checkbox" id="delete_data" name="wp_2fa_settings[delete_data_upon_uninstall]" value="delete_data_upon_uninstall"
								<?php checked( 1, WP2FA::get_wp2fa_general_setting( 'delete_data_upon_uninstall' ), true ); ?>
								>
								<?php \esc_html_e( 'Delete data upon uninstall', 'wp-2fa' ); ?>
							</fieldset>
						</td>
					</tr>
				</tbody>
			</table>
		</div>
			<?php
			$last_user_to_update_settings = get_current_user_id();

			?>
		<input type="hidden" id="2fa_main_user" name="wp_2fa_settings[2fa_settings_last_updated_by]" value="<?php echo \esc_attr( $last_user_to_update_settings ); ?>">
			<?php
		}

		/**
		 * Limit settings setting.
		 *
		 * @return void
		 *
		 * @since 2.0.0
		 */
		private static function limit_settings_access() {
			?>
		<br>
		<h3><?php \esc_html_e( 'Limit 2FA settings access', 'wp-2fa' ); ?></h3>
		<p class="description">
			<?php \esc_html_e( 'Use this setting to hide this plugin configuration area from all other admins.', 'wp-2fa' ); ?>
		</p>
		<table class="form-table">
			<tbody>
				<tr>
					<th><label for="limit_access"><?php \esc_html_e( 'Limit access to 2FA settings', 'wp-2fa' ); ?></label></th>
					<td>
						<fieldset>
							<input type="checkbox" id="limit_access" name="wp_2fa_settings[limit_access]" value="limit_access"
							<?php checked( 1, WP2FA::get_wp2fa_general_setting( 'limit_access' ), true ); ?>
							>
							<?php \esc_html_e( 'Hide settings from other administrators', 'wp-2fa' ); ?>
						</fieldset>
					</td>
				</tr>
			</tbody>
		</table>
			<?php
		}

		/**
		 * Disable brute force setting.
		 *
		 * @return void
		 *
		 * @since 2.5.0
		 */
		private static function disable_brute_force_settings() {
			?>
		<br>
		<h3><?php \esc_html_e( 'Disable 2FA code brute force protection', 'wp-2fa' ); ?></h3>
		<p class="description">
			<?php \esc_html_e( 'When using email and SMS 2FA, the plugin sends the users a new one-time code whenever they enter the wrong code when logging in. This is a security enhancement, a sort of brute force protection. You can disable this feature from the below setting, however, it is not recommended.', 'wp-2fa' ); ?>
		</p>
		<table class="form-table">
			<tbody>
				<tr>
					<th><label for="brute_force_disable"><?php \esc_html_e( 'Disable one-time code brute force protection', 'wp-2fa' ); ?></label></th>
					<td>
						<fieldset>
							<input type="checkbox" id="brute_force_disable" name="wp_2fa_settings[brute_force_disable]" value="brute_force_disable"
							<?php checked( 1, WP2FA::get_wp2fa_general_setting( 'brute_force_disable' ), true ); ?>
							>
						</fieldset>
					</td>
				</tr>
			</tbody>
		</table>
			<?php
		}

		/**
		 * Rendering settings when there are no methods.
		 *
		 * @return void
		 *
		 * @since 2.2.0
		 */
		private static function no_method_exists() {
			?>
		<p class="description">
			<?php
				printf(
					// translators: support email.
					\esc_html__( 'Use this setting below to configure the properties of the two-factor authentication on your website and how users use it. If you have any questions send us an email at %1$s.', 'wp-2fa' ),
					'<a href="mailto:support@melapress.com">support@melapress.com</a>'
				);
			?>
		</p>
		<h3><?php \esc_html_e( 'What should the plugin do if the 2FA method used during a user login is unavailable', 'wp-2fa' ); ?></h3>
		<p class="description">
			<?php \esc_html_e( 'There may be cases in which the 2FA service is unavailable when a user is trying to log in. For example, the service is unreachable or there are no credits to complete the action. In this case you can configure the plugin to either block the login process, or allow the user to log in without 2FA authentication.', 'wp-2fa' ); ?>
		</p>
		<table class="form-table">
			<tbody>
				<tr>
					<th><label for="no-methods"><?php \esc_html_e( 'Select action', 'wp-2fa' ); ?></label></th>
					<td>
						<fieldset class="contains-hidden-inputs" id="no-methods">
							<label for="login_block">
								<input type="radio" name="wp_2fa_settings[method_invalid_setting]" id="login_block" value="login_block"
								<?php checked( WP2FA::get_wp2fa_general_setting( 'method_invalid_setting' ), 'login_block' ); ?>
								>
							<span><?php \esc_html_e( 'Block the login.', 'wp-2fa' ); ?></span>
							</label>

							<br/>
							<label for="allow_login_without_method">
								<input type="radio" name="wp_2fa_settings[method_invalid_setting]" id="allow_login_without_method" value="allow_login_without_method"
								<?php checked( WP2FA::get_wp2fa_general_setting( 'method_invalid_setting' ), 'allow_login_without_method' ); ?>
								data-unhide-when-checked=".custom-from-inputs">
								<span><?php \esc_html_e( 'Allow the login without 2FA', 'wp-2fa' ); ?></span>
							</label>
						</fieldset>
					</td>
				</tr>
			</tbody>
		</table>
			<?php
		}
	}
}
includes/classes/Admin/SettingsPages/class-settings-page-policies.php000064400000106164150755130600022024 0ustar00<?php
/**
 * Policy settings class.
 *
 * @package    wp2fa
 * @subpackage settings-pages
 *
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 *
 * @see       https://wordpress.org/plugins/wp-2fa/
 */

namespace WP2FA\Admin\SettingsPages;

use WP2FA\WP2FA;
use WP2FA\Methods\TOTP;
use WP2FA\Methods\Email;
use WP2FA\Utils\Debugging;
use WP2FA\Admin\Settings_Page;
use WP2FA\Utils\Generate_Modal;
use WP2FA\Utils\Settings_Utils;
use WP2FA\Admin\Helpers\WP_Helper;
use WP2FA\Admin\Helpers\User_Helper;
use WP2FA\Admin\Controllers\Settings;
use WP2FA\Admin\Views\First_Time_Wizard_Steps;

/*
 * Policies settings tab
 */
if ( ! class_exists( '\WP2FA\Admin\SettingsPages\Settings_Page_Policies' ) ) {
	/**
	 * Settings_Page_Policies - Class for handling settings.
	 *
	 * @since 2.0.0
	 */
	class Settings_Page_Policies {
		/**
		 * Renders the settings.
		 *
		 * @return void
		 *
		 * @since 2.0.0
		 */
		public static function render() {
			if ( ! current_user_can( 'manage_options' ) ) {
				return;
			}

			$user = wp_get_current_user();
			if ( ! empty( WP2FA::get_wp2fa_setting( '2fa_settings_last_updated_by' ) ) ) {
				$main_user = (int) WP2FA::get_wp2fa_setting( '2fa_settings_last_updated_by' );
			} else {
				$main_user = get_current_user_id();
			}

			/**
			 * Used from user settings controller.
			 *
			 * @param bool - Default at this point is false - no user settings.
			 *
			 * @since 2.4.0
			 */
			$roles_controller = \apply_filters( WP_2FA_PREFIX . 'roles_controller_exists', false );
			if ( $roles_controller ) {
				$roles = WP_Helper::get_roles();

				foreach ( $roles as $role ) {
					self::new_page_created( $role );
				}
			} else {
				self::new_page_created();
			}

			$enabled_methods = User_Helper::get_enabled_method_for_user( $user );

			if ( empty( $enabled_methods ) ) {
				$new_page_modal_content  = '<h3>' . \esc_html__( 'Exclude yourself?', 'wp-2fa' ) . '</h3>';
				$new_page_modal_content .= '</p>' . \esc_html__( 'You are about to enforce 2FA instantly on all users, including yourself, however you have not yet configured your own 2FA method. What would you like to do?', 'wp-2fa' ) . '</p>';

				echo Generate_Modal::generate_modal( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
					'exclude-self-from-instant-2fa',
					false,
					$new_page_modal_content, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
					array(
						'<a href="#" class="wp-2fa-button-secondary button-secondary" data-close-2fa-modal>' . __( 'Continue anyway', 'wp-2fa' ) . '</a>', // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
						'<a href="#" class="wp-2fa-button-primary button-primary" data-close-2fa-modal data-user-login-name="' . \esc_attr( $user->user_login ) . '">' . __( 'Exclude myself from 2FA policies', 'wp-2fa' ) . '</a>', // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
					),
					false,
					'560px'
				);
			}
			?>

			<div class="wrap wp-2fa-settings-wrapper wp2fa-form-styles">
				<h2><?php \esc_html_e( 'WP 2FA Settings', 'wp-2fa' ); ?></h2>
				<hr>
				<?php if ( ! empty( WP2FA::get_wp2fa_general_setting( 'limit_access' ) ) && $main_user !== $user->ID ) { ?>
					<?php
					echo \esc_html__( 'These settings have been disabled by your site administrator, please contact them for further assistance.', 'wp-2fa' );
					?>
				<?php } else { ?>
					<?php
						/**
						 * Fires before the plugin settings rendering.
						 *
						 * @since 2.0.0
						 */
						do_action( WP_2FA_PREFIX . 'before_plugin_settings' );
					?>
						<?php
						if ( WP_Helper::is_multisite() ) {
							$action = 'edit.php?action=update_wp2fa_network_options';
						} else {
							$action = 'options.php';
						}
						if (! isset($_REQUEST['tab']) || isset($_REQUEST['tab']) && '2fa-settings' === $_REQUEST['tab']) { // phpcs:ignore
							?>
						<br/>
							<?php
							printf(
								'<p class="description">%1$s <a href="mailto:support@melapress.com">%2$s</a></p>',
								\esc_html__( 'Use the settings below to configure the properties of the two-factor authentication on your website and how users use it. If you have any questions send us an email at', 'wp-2fa' ),
								\esc_html__( 'support@melapress.com', 'wp-2fa' )
							);
							?>
						<br/>
							<?php $total_users = count_users(); ?>
						<form id="wp-2fa-admin-settings" action='<?php echo \esc_attr( $action ); ?>' method='post' autocomplete="off" data-2fa-total-users="<?php echo \esc_attr( $total_users['total_users'] ); ?>">
							<?php
								settings_fields( WP_2FA_POLICY_SETTINGS_NAME );
							self::select_method_setting();
							self::select_enforcement_policy_setting();
							self::excluded_roles_or_users_setting();
							if ( WP_Helper::is_multisite() ) {
								self::excluded_network_sites();
							}

							/**
							 * Fires before grace period HTML rendering settings.
							 *
							 * @since 2.0.0
							 */
							do_action( WP_2FA_PREFIX . 'before_grace_period_settings' );

							self::grace_period_setting();
							self::user_redirect_after_wizard();

							/**
							 * Fires before user profile period HTML rendering settings.
							 *
							 * @since 2.0.0
							 */
							do_action( WP_2FA_PREFIX . 'before_user_profile_settings' );
							self::user_profile_settings();
							self::disable_2fa_removal_setting();
							submit_button();
							?>
						</form>
					<?php } ?>
				<?php } ?>
			</div>
			<?php
		}

		/**
		 * Creates new page for settings (FE only).
		 *
		 * @param string $role - The name of the role, empty for global.
		 *
		 * @return void
		 *
		 * @since 2.0.0
		 */
		private static function new_page_created( $role = '' ) {
			$role = ( is_null( $role ) || empty( $role ) || 'global' === $role ) ? '' : $role;
			// Check if new user page has been published.
			if ( ! empty( get_transient( WP_2FA_PREFIX . 'new_custom_page_created' . $role ) ) ) {
				\delete_transient( WP_2FA_PREFIX . 'new_custom_page_created' . $role );
				$new_page_id = Settings::get_role_or_default_setting( 'custom-user-page-id', '', $role );
				if ( empty( $new_page_id ) ) {
					$new_page_id = Settings::get_custom_settings_page_id( $role );
				}

				if ( $new_page_id > 0 ) {
					$new_page_permalink = get_permalink( $new_page_id );

					$new_page_modal_content  = '<h3>' . \esc_html__( 'The plugin created the 2FA settings page with the URL:', 'wp-2fa' ) . '</h3>';
					$new_page_modal_content .= '<h4><a target="_blank" href="' . \esc_url( $new_page_permalink ) . '">' . \esc_url( $new_page_permalink ) . '</a></h4>';
					$new_page_modal_content .= '<p>' . \esc_html__( 'You can edit this page using the page editor, like you do with all other pages.', 'wp-2fa' );
					$new_page_modal_content .= '</p>';
					$new_page_modal_content .= sprintf(
					/* translators: %s: tag name. */
						\esc_html__( 'Use the %s html tag in the email templates to include the URL of the 2FA configuration page when notifying the users to configure two-factor authentication.', 'wp-2fa' ),
						'<strong>{2fa_settings_page_url}</strong>'
					);
					$new_page_modal_content .= '</p>';

					echo Generate_Modal::generate_modal( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
						'new-page-created' . $role, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
						false,
						$new_page_modal_content, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
						array(
							'<a href="#" class="wp-2fa-button-primary button-primary" data-close-2fa-modal>' . __( 'OK', 'wp-2fa' ) . '</a>', // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
						),
						true,
						'560px'
					);
				}
			}
		}

		/**
		 * Validate options before saving.
		 *
		 * @param array $input The settings array.
		 *
		 * @return array|void
		 *
		 * @since 2.0.0
		 */
		public static function validate_and_sanitize( $input ) {
			Debugging::log( 'The following settings will be processed (Policy): ' . "\n" . wp_json_encode( $input ) );

			/*
			 * Adds the ability to check the referer and act accordingly.
			*
			* @since 2.0.0
			*/
			\do_action( WP_2FA_PREFIX . 'change_referer' );

			// Bail if user doesn't have permissions to be here.
			if ( ! current_user_can( 'manage_options' ) || ! isset( $_POST['action'] ) && ! check_admin_referer( 'wp2fa-step-choose-method' ) ) {
				return;
			}

			$no_method_enabled = false;
			if ( ! isset( $input[ TOTP::POLICY_SETTINGS_NAME ] ) && ! isset( $input[ Email::POLICY_SETTINGS_NAME ] ) && ! isset( $_POST['save_step'] ) ) {
				/**
				 * At this point, none of the default providers is set / activated. This filter allows additional providers to change the behavior. Checking the input array for specific values (methods), and based on that we can raise error that none of the allowed methods has bees selected by the user, or dismiss the error otherwise.
				 *
				 * @param bool - Default at this point is true - no method is selected.
				 * @param array $input - The input array with all the data.
				 *
				 * @since 2.0.0
				 */
				$no_methods_set = \apply_filters( WP_2FA_PREFIX . 'save_additional_enabled_methods', true, $input );

				if ( $no_methods_set ) {
					add_settings_error(
						WP_2FA_POLICY_SETTINGS_NAME,
						\esc_attr( 'enable_email_settings_error' ),
						\esc_html__( 'No global 2FA methods enabled.', 'wp-2fa' ),
						'error'
					);
					$no_method_enabled = true;
				}
			}

			$simple_settings_we_can_loop = array(
				'grace-policy',
				'enable_destroy_session',
				'2fa_settings_last_updated_by',
				'limit_access',
				'hide_remove_button',
				'redirect-user-custom-page',
				'redirect-user-custom-page-global',
				'superadmins-role-add',
				'superadmins-role-exclude',
				'separate-multisite-page-url',
			);

			/**
			 * Gives the ability to filter the settings array of the plugin.
			 *
			 * @param array $settings - The array with all the default settings.
			 *
			 * @since 2.0.0
			 */
			$simple_settings_we_can_loop = \apply_filters( WP_2FA_PREFIX . 'loop_settings', $simple_settings_we_can_loop );

			$settings_to_turn_into_bools = array(
				'enable_destroy_session',
				'limit_access',
				'hide_remove_button',
			);

			$settings_to_turn_into_array = array(
				'enforced_roles',
				'enforced_users',
				'excluded_users',
				'excluded_roles',
				'excluded_sites',
			);

			foreach ( $simple_settings_we_can_loop as $simple_setting ) {
				if ( ! in_array( $simple_setting, $settings_to_turn_into_bools, true ) ) {
					// Is item is not one of our possible settings we want to turn into a bool, process.
					$output[ $simple_setting ] = ( isset( $input[ $simple_setting ] ) && ! empty( $input[ $simple_setting ] ) ) ? trim( (string) sanitize_text_field( $input[ $simple_setting ] ) ) : false;
				} else {
					// This item is one we treat as a bool, so process correctly.
					$output[ $simple_setting ] = ( isset( $input[ $simple_setting ] ) && ! empty( $input[ $simple_setting ] ) ) ? true : false;
				}
			}

			if ( $no_method_enabled ) {
				/**
				 * No methods are enabled - return the previous selection. Gives the ability for external providers to set the default values.
				 *
				 * @param array $output - The output array with all the data we will store in the settings.
				 *
				 * @since 2.0.0
				 */
				$output = \apply_filters( WP_2FA_PREFIX . 'no_method_enabled', $output );
			}

			$output['included_sites'] = array();
			if ( WP_Helper::is_multisite() ) {
				if ( isset( $input['included_sites'] ) && is_array( $input['included_sites'] ) && ! empty( $input['included_sites'] ) ) {
					foreach ( $input['included_sites'] as &$site ) {
						if ( ! filter_var( $site, FILTER_VALIDATE_INT ) ) {
							unset( $site );

							continue;
						}

						$output['included_sites'][] = $site;
					}
					unset( $site );
				} elseif ( isset( $input['enforcement-policy'] ) && 'enforce-on-multisite' === $input['enforcement-policy'] && empty( $input['included_sites'] ) ) {

					add_settings_error(
						WP_2FA_POLICY_SETTINGS_NAME,
						\esc_attr( 'included_sites_settings_error' ),
						\esc_html__( 'You must specify at least one sub-site', 'wp-2fa' ),
						'error'
					);

				}
			}

			foreach ( $settings_to_turn_into_array as $setting ) {
				if ( isset( $input[ $setting ] ) ) {
					$output[ $setting ] = $input[ $setting ];
				} else {
					$output[ $setting ] = array();
				}
			}

			if ( isset( $input['grace-period'] ) ) {
				if ( 0 === (int) $input['grace-period'] ) {
					add_settings_error(
						WP_2FA_POLICY_SETTINGS_NAME,
						\esc_attr( 'grace_settings_error' ),
						\esc_html__( 'Grace period must be at least 1 day/hour', 'wp-2fa' ),
						'error'
					);
					$output['grace-period'] = 1;
				} else {
					$output['grace-period'] = (int) $input['grace-period'];
				}
			}


			if ( isset( $input['grace-period-denominator'] ) && 'days' === $input['grace-period-denominator'] || isset( $input['grace-period-denominator'] ) && 'hours' === $input['grace-period-denominator'] || isset( $input['grace-period-denominator'] ) && 'seconds' === $input['grace-period-denominator'] ) {
				$output['grace-period-denominator'] = sanitize_text_field( $input['grace-period-denominator'] );
			}

			if ( ( isset( $input['create-custom-user-page'] ) && 'yes' === $input['create-custom-user-page'] ) || ( isset( $input['create-custom-user-page'] ) && 'no' === $input['create-custom-user-page'] ) ) {
				$output['create-custom-user-page'] = sanitize_text_field( $input['create-custom-user-page'] );
			}

			if ( ( isset( $input['create-custom-user-page'] ) && 'yes' === $input['create-custom-user-page'] ) && isset( $input['custom-user-page-url'] ) && ! empty( $input['custom-user-page-url'] ) ) {
				if ( WP2FA::get_wp2fa_setting( 'custom-user-page-url' ) !== $input['custom-user-page-url'] ) {

					if ( 'yes' === $input['create-custom-user-page'] && ! empty( $input['custom-user-page-url'] ) ) {
						$output['custom-user-page-url'] = sanitize_title_with_dashes( $input['custom-user-page-url'] );
						if ( WP_Helper::is_multisite() && isset( $input['separate-multisite-page-url'] ) ) {
							$sites = WP_Helper::get_multi_sites();

							foreach ( $sites as $site ) {
								$blog_id = $site->id;

								\switch_to_blog( $blog_id );

								self::generate_custom_user_profile_page( $output['custom-user-page-url'] );

								\restore_current_blog();
							}
						} else {
							self::generate_custom_user_profile_page( $output['custom-user-page-url'] );
						}
					}
				} else {
					$output['custom-user-page-url'] = sanitize_title_with_dashes( $input['custom-user-page-url'] );
					$output['custom-user-page-id']  = WP2FA::get_wp2fa_setting( 'custom-user-page-id' );
					if ( is_null( get_post( $output['custom-user-page-id'] ) ) ) {
						$create_page                   = self::generate_custom_user_profile_page( $output['custom-user-page-url'] );
						$output['custom-user-page-id'] = (int) $create_page;
					}
				}
			}

			if ( isset( $_REQUEST['page'] ) && 'wp-2fa-setup' !== $_REQUEST['page'] || isset( $_REQUEST[ WP_2FA_POLICY_SETTINGS_NAME ]['create-custom-user-page'] ) ) {
				if ( isset( $input['create-custom-user-page'] ) && 'no' === $input['create-custom-user-page'] ) {
					$output['custom-user-page-url']        = '';
					$output['custom-user-page-id']         = '';
					$output['separate-multisite-page-url'] = '';
					\wp_delete_post( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ), true );
				}
			}

			if ( isset( $input['create-custom-user-page'] ) && 'yes' === $input['create-custom-user-page'] && empty( $input['custom-user-page-url'] ) ) {
				add_settings_error(
					WP_2FA_POLICY_SETTINGS_NAME,
					\esc_attr( 'no_page_slug_provided' ),
					\esc_html__( 'You must provide a new page slug.', 'wp-2fa' ),
					'error'
				);
			}

			if ( isset( $input['grace-period'] ) && isset( $input['grace-period-denominator'] ) ) {
				// Turn inputs into a useable string.
				$create_a_string = $output['grace-period'] . ' ' . $output['grace-period-denominator'];
				// Turn that string into a time.
				$grace_expiry                       = strtotime( $create_a_string );
				$output['grace-period-expiry-time'] = sanitize_text_field( $grace_expiry );
			}

			// Process main policy.
			if ( isset( $input['enforcement-policy'] ) && in_array( $input['enforcement-policy'], array( 'all-users', 'certain-users-only', 'certain-roles-only', 'do-not-enforce', 'superadmins-only', 'superadmins-siteadmins-only', 'enforce-on-multisite' ), true ) ) {
				// Clear enforced roles/users if setting has changed.
				if ( 'all-users' === $input['enforcement-policy'] || 'do-not-enforce' === $input['enforcement-policy'] ) {
					$input['enforced_users']        = array();
					$input['enforced_roles']        = array();
					$output['enforced_users']       = array();
					$output['enforced_roles']       = array();
					$output['superadmins-role-add'] = 'no';
				}

				$output['enforcement-policy'] = sanitize_text_field( $input['enforcement-policy'] );

				if ( 'certain-roles-only' === $input['enforcement-policy'] && empty( $input['enforced_roles'] ) && empty( $input['enforced_users'] ) ) {
					add_settings_error(
						WP_2FA_POLICY_SETTINGS_NAME,
						\esc_attr( 'enforced_roles_settings_error' ),
						\esc_html__( 'You must specify at least one role or user', 'wp-2fa' ),
						'error'
					);

					$output['enforcement-policy'] = 'do-not-enforce';
				}

				// If any users are being excluded, delete any wp 2fa data.
				if ( isset( $output['excluded_users'] ) &&
				! empty( array_diff( (array) WP2FA::get_wp2fa_setting( 'excluded_users' ), (array) $output['excluded_users'] ) ) ) {
					// Wipe user 2fa data.
					$user_array = $output['excluded_users'];
					foreach ( $user_array as $user ) {
						if ( ! empty( $user ) ) {
							$user_to_wipe = get_user_by( 'login', $user );
							global $wpdb;
							// @codingStandardsIgnoreStart
							$wpdb->query(
								$wpdb->prepare(
									"
								DELETE FROM $wpdb->usermeta
								WHERE user_id = %d
								AND meta_key LIKE %s
								",
									array(
										$user_to_wipe->ID,
										'wp_2fa_%',
									)
								)
							);
							// @codingStandardsIgnoreEnd
						}
					}
				}
			}

			/**
			 * Allow extensions and 3rd party developers to run extra validation of the output array.
			 *
			 * @param array - Array with all the collected data.
			 */
			do_action( WP_2FA_PREFIX . 'run_extra_settings_validation', $output );

			/**
			 * Filter the values we are about to store in the plugin settings.
			 *
			 * @param array $output - The output array with all the data we will store in the settings.
			 * @param array $input  - The input array with all the data we received from the user.
			 *
			 * @since 2.0.0
			 */
			$output = \apply_filters( WP_2FA_PREFIX . 'filter_output_content', $output, $input );

			// Remove duplicates from settings errors. We do this as this sanitization callback is actually fired twice, so we end up with duplicates when saving the settings for the FIRST TIME only. The issue is not present once the settings are in the DB as the sanitization wont fire again. For details on this core issue - https://core.trac.wordpress.org/ticket/21989.
			global $wp_settings_errors;
			if ( isset( $wp_settings_errors ) ) {
				$errors             = array_map( 'unserialize', array_unique( array_map( 'serialize', $wp_settings_errors ) ) );
				$wp_settings_errors = $errors; // phpcs:ignore
			}

			/**
			 * Allow extensions and 3rd party developers to change or check the settings array.
			 *
			 * @param array - Array with all the collected and validated data.
			 *
			 * @since 2.6.0
			 */
			do_action( WP_2FA_PREFIX . 'before_settings_save', $output );

			// WordPress saves the option to the database, but we still need to do some work when the settings are saved.
			WP2FA::update_plugin_settings( $output, true );

			Debugging::log( 'The following settings are being saved (Policy): ' . "\n" . wp_json_encode( $output ) );

			// We have overridden any defaults by now so can clear this.
			Settings_Utils::delete_option( WP_2FA_PREFIX . 'default_settings_applied' );
			Settings_Utils::delete_option( 'wizard_not_finished' );

			/**
			 * Notify the extensions and 3rd party developers that the settings array is saved.
			 *
			 * @param array - Array with all the stored settings.
			 *
			 * @since 2.6.0
			 */
			do_action( WP_2FA_PREFIX . 'after_settings_save', Settings_Utils::get_option( WP_2FA_POLICY_SETTINGS_NAME, array() ) );

			return $output;
		}

		/**
		 * Updates global policy network options.
		 *
		 * @return void
		 *
		 * @since 2.0.0
		 *
		 * @SuppressWarnings(PHPMD.ExitExpressions)
		 */
		public static function update_wp2fa_network_options() {
			if ( isset( $_POST[ WP_2FA_POLICY_SETTINGS_NAME ] ) ) {
				check_admin_referer( 'wp_2fa_policy-options' );
				$options = self::validate_and_sanitize(wp_unslash($_POST[WP_2FA_POLICY_SETTINGS_NAME])); // phpcs:ignore
				$settings_errors = get_settings_errors( WP_2FA_POLICY_SETTINGS_NAME );
				if ( ! empty( $settings_errors ) ) {
					// redirect back to our options page.
					wp_safe_redirect(
						add_query_arg(
							array(
								'page' => Settings_Page::TOP_MENU_SLUG,
								'wp_2fa_network_settings_error' => urlencode_deep( $settings_errors[0]['message'] ),
							),
							network_admin_url( 'admin.php' )
						)
					);
					exit;
				}
				WP2FA::update_plugin_settings( $options );

				// redirect back to our options page.
				wp_safe_redirect(
					add_query_arg(
						array(
							'page' => Settings_Page::TOP_MENU_SLUG,
							'wp_2fa_network_settings_updated' => 'true',
						),
						network_admin_url( 'admin.php' )
					)
				);
				exit;
			}
		}

		/**
		 * Creates a new page with our shortcode present.
		 *
		 * @param string $page_slug - The page slug.
		 * @param string $role      - The name of the role for which the page has been created.
		 *
		 * @return mixed
		 *
		 * @since 2.0.0
		 */
		public static function generate_custom_user_profile_page( $page_slug, string $role = '' ) {
			// Check if a page with slug exists.
			$page_exists = self::get_post_by_post_name( $page_slug, 'page' );
			if ( $page_exists ) {
				// Seeing as the page exists, return its ID.
				return $page_exists->ID;
			}

			$generated_by_message  = '<p>' . \esc_html__( 'Page generated by', 'wp-2fa' );
			$generated_by_message .= ' <a href="https://melapress.com/wordpress-2fa/?&utm_source=plugin&utm_medium=link&utm_campaign=wp2fa" target="_blank">' . \esc_html__( 'WP 2FA Plugin', 'wp-2fa' ) . '</a>';
			$generated_by_message .= '</p>';

			$user      = wp_get_current_user();
			$post_data = array(
				'post_title'   => 'WP 2FA User Profile',
				'post_name'    => $page_slug,
				'post_content' => '[wp-2fa-setup-form] ' . $generated_by_message,
				'post_status'  => 'publish',
				'post_author'  => $user->ID,
				'post_type'    => 'page',
			);

			// Lets insert the post now.
			$result = wp_insert_post( $post_data );

			if ( $result && ! is_wp_error( $result ) ) {
				$post_id = $result;
				set_transient( WP_2FA_PREFIX . 'new_custom_page_created' . $role, true, 60 );
				set_site_transient( WP_2FA_PREFIX . 'new_custom_page_created' . $role, true, 60 );

				return $post_id;
			}

			return $result;
		}

		/**
		 * Check if page with slug exists.
		 *
		 * @param string $slug      - The post slug.
		 * @param string $post_type - Post type.
		 *
		 * @return \WP_Post|bool
		 *
		 * @since 2.0.0
		 */
		public static function get_post_by_post_name( $slug = '', $post_type = '' ) {
			if ( ! $slug || ! $post_type ) {
				return false;
			}

			$post_object = get_page_by_path( $slug, OBJECT, $post_type );

			if ( ! $post_object ) {
				return false;
			}

			return $post_object;
		}

		/**
		 * General settings.
		 *
		 * @return void
		 *
		 * @since 2.0.0
		 */
		private static function select_method_setting() {
			First_Time_Wizard_Steps::select_method( false );
		}

		/**
		 * Policy settings.
		 *
		 * @return void
		 *
		 * @since 2.0.0
		 */
		private static function select_enforcement_policy_setting() {
			First_Time_Wizard_Steps::enforcement_policy( false );
		}

		/**
		 * User profile settings.
		 *
		 * @return void
		 *
		 * @since 2.0.0
		 */
		private static function user_profile_settings() {
			ob_start();
			$create_page = WP2FA::get_wp2fa_setting( 'create-custom-user-page' );
			?>
		<h3><?php \esc_html_e( 'Can users access the WordPress dashboard or you have custom profile pages? ', 'wp-2fa' ); ?></h3>
		<p class="description">
			<?php \esc_html_e( 'If your users do not have access to the WordPress dashboard (because you use custom user profile pages) enable this option. Once enabled, the plugin creates a page which ONLY authenticated users can access to configure their user 2FA settings. A link to this page is sent in the 2FA welcome email.', 'wp-2fa' ); ?></a>
		</p>
		<table class="form-table">
			<tbody>
				<tr>
					<th><label for="use_custom_page"><?php \esc_html_e( 'Frontend 2FA settings page', 'wp-2fa' ); ?></label></th>
					<td>
						<fieldset>
							<label class="radio-inline">
								<input id="use_custom_page" type="radio" name="wp_2fa_policy[create-custom-user-page]" value="yes"
								<?php checked( $create_page, 'yes' ); ?>
								>
								<?php \esc_html_e( 'Yes', 'wp-2fa' ); ?>
							</label>
							<label class="radio-inline">
								<input id="dont_use_custom_page" type="radio" name="wp_2fa_policy[create-custom-user-page]" value="no"
								<?php checked( $create_page, 'no' ); ?>
								<?php checked( $create_page, '' ); ?>
								>
								<?php \esc_html_e( 'No', 'wp-2fa' ); ?>
							</label>
						</fieldset>
					</td>
				</tr>
				<tr class="custom-user-page-setting<?php echo ( 'yes' !== $create_page ) ? ' disabled' : ''; ?>">
					<th><label for="custom-user-page-url"><?php \esc_html_e( 'Frontend 2FA settings page URL', 'wp-2fa' ); ?></label></th>
					<td>
						<fieldset>
							<?php
							if ( ! empty( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) ) ) {
								$custom_slug = \get_post_field( 'post_name', \get_post( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) ) );
							} else {
								$custom_slug = WP2FA::get_wp2fa_setting( 'custom-user-page-url' );
							}

							$has_error       = false;
							$settings_errors = \get_settings_errors( WP_2FA_SETTINGS_NAME );
							if ( ! empty( $settings_errors ) ) {
								foreach ( $settings_errors as $error ) {
									if ( 'no_page_slug_provided' === $error['code'] ) {
										$has_error = true;

										break;
									}
								}
							}

							?>
							<?php echo \esc_html( trailingslashit( get_site_url() ) ); ?>
							<input type="text" id="custom-user-page-url" name="wp_2fa_policy[custom-user-page-url]" value="<?php echo \esc_attr( sanitize_text_field( $custom_slug ) ); ?>"
							<?php echo ( $has_error ) ? ' class="error"' : ''; ?>>
						</fieldset>
							<?php
							if ( ! empty( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) && ! WP_Helper::is_multisite() ) ) {
								$edit_post_link = \get_edit_post_link( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) );
								$view_post_link = \get_permalink( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) );
								?>
							<br>
							<a href="<?php echo \esc_url( $edit_post_link ); ?>" target="_blank" class="button button-secondary" style="margin-right: 5px;"><?php \esc_html_e( 'Edit Page', 'wp-2fa' ); ?></a> <a href="<?php echo \esc_url( $view_post_link ); ?>" target="_blank" class="button button-primary"><?php \esc_html_e( 'View Page', 'wp-2fa' ); ?></a>
								<?php
							}
							?>
					</td>
				</tr>
				<?php if ( WP_Helper::is_multisite() ) { ?>
					<tr class="custom-user-page-setting<?php echo ( 'yes' !== $create_page ) ? ' disabled' : ''; ?>">
						<th><label for="separate-multisite-page-url"><?php \esc_html_e( 'Create separate pages on multisite network', 'wp-2fa' ); ?></label></th>
						<td>
							<?php
								$separate_multisite_page = WP2FA::get_wp2fa_setting( 'separate-multisite-page-url' );
							?>
							<fieldset>
								<input type="checkbox" name="wp_2fa_policy[<?php echo \esc_attr( 'separate-multisite-page-url' ); ?>]" 
								id="separate-multisite-page-url" 
								value="separate-multisite-page-url" <?php checked( $separate_multisite_page, 'separate-multisite-page-url' ); ?> class="js-nested">
								<label for="separate-multisite-page-url"><?php echo \esc_html__( 'Create User settings page separately for every site', 'wp-2fa' ); ?></label>
								<p class="description"><?php echo \esc_html__( 'When you enable this setting a page with the same slug is created on each site on the network, so the users of each sub site can use this page on their website to configure 2FA.', 'wp-2fa' ); ?></p>
							</fieldset>
						</td>
					</tr>
					<?php } ?>
				<tr class="custom-user-page-setting<?php echo ( 'yes' !== $create_page ) ? ' disabled' : ''; ?>">
					<th colspan="2"><p class="description"><?php \esc_html_e( 'Specify the page where you want to redirect your users to after they complete the 2FA setup. This will override the global redirect setting.', 'wp-2fa' ); ?></p></th>
				</tr>
				<tr class="custom-user-page-setting<?php echo ( 'yes' !== $create_page ) ? ' disabled' : ''; ?>">
					<th><label for="redirect-user-custom-page"><?php \esc_html_e( 'Redirect users after 2FA setup', 'wp-2fa' ); ?></label></th>
					<td>
						<fieldset>
							<?php
							$custom_slug = WP2FA::get_wp2fa_setting( 'redirect-user-custom-page' );
							?>
							<?php echo \esc_html( trailingslashit( get_site_url() ) ); ?>
							<input type="text" id="redirect-user-custom-page" name="wp_2fa_policy[redirect-user-custom-page]" value="<?php echo \esc_attr( sanitize_text_field( $custom_slug ) ); ?>">
						</fieldset>
					</td>
				</tr>
			</tbody>
		</table>
			<?php
			$output = ob_get_clean();

			/**
			 * Gives the ability to manipulate the output.
			 *
			 * @param string $output - Parsed HTML with the methods.
			 *
			 * @since 2.0.0
			 */
			$output = \apply_filters( WP_2FA_PREFIX . 'user_profile_settings', $output );

			echo $output; // phpcs:ignore
		}

		/**
		 * User profile settings.
		 *
		 * @return void
		 *
		 * @since 2.0.0
		 */
		private static function user_redirect_after_wizard() {
			ob_start();
			?>
		<h3><?php \esc_html_e( 'Do you want to redirect the user to a specific page after completing the 2FA setup wizard', 'wp-2fa' ); ?></h3>
		<p class="description">
			<?php \esc_html_e( 'Specify a URL of a page where you want to redirect the users once they complete the 2FA setup wizard. Leave empty for default behaviour, in which users are redirected back to the page from where they launched the wizard.', 'wp-2fa' ); ?></a>
		</p>
		<table class="form-table">
			<tbody>
				<tr>
					<th><label for="redirect-user-custom-page-global"><?php \esc_html_e( 'Redirect users after 2FA setup to', 'wp-2fa' ); ?></label></th>
					<td>
						<fieldset>
							<?php echo \esc_html( trailingslashit( get_site_url() ) ); ?>
							<input type="text" id="redirect-user-custom-page-global" name="wp_2fa_policy[redirect-user-custom-page-global]" value="<?php echo \esc_attr( sanitize_text_field( WP2FA::get_wp2fa_setting( 'redirect-user-custom-page-global' ) ) ); ?>">
						</fieldset>
					</td>
				</tr>
			</tbody>
		</table>
			<?php
			$output = ob_get_clean();

			/**
			 * Gives the ability to manipulate the output.
			 *
			 * @param string $output - Parsed HTML with the methods.
			 *
			 * @since 2.0.0
			 */
			$output = \apply_filters( WP_2FA_PREFIX . 'redirect_after', $output );

			echo $output; // phpcs:ignore
		}

		/**
		 * Role and users exclusion settings.
		 *
		 * @return void
		 *
		 * @since 2.0.0
		 */
		private static function excluded_roles_or_users_setting() {
			?>
		<div id="exclusion_settings_wrapper">
			<?php First_Time_Wizard_Steps::exclude_users(); ?>
		</div>
			<?php
		}

		/**
		 * Role and users exclusion settings.
		 *
		 * @return void
		 *
		 * @since 2.0.0
		 */
		private static function excluded_network_sites() {
			First_Time_Wizard_Steps::excluded_network_sites();
		}

		/**
		 * Grace period settings.
		 *
		 * @return void
		 *
		 * @since 2.0.0
		 */
		private static function grace_period_setting() {
			ob_start();

			/**
			 * Fires after the grace period. Gives the ability to change the parsed code.
			 *
			 * @param string $content - HTML content.
			 * @param string $role - The name of the role.
			 * @param string $name_prefix - Name prefix for the input name, includes the role name if provided.
			 * @param string $data_role - Data attribute - used by the JS.
			 * @param string $role_id - The role name, used to identify the inputs.
			 *
			 * @since 2.0.0
			 */
			echo \apply_filters( WP_2FA_PREFIX . 'before_grace_period_settings', '', '', 'wp_2fa_policy' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped

			?>
		<br>
		<h3><?php \esc_html_e( 'Should users be asked to setup 2FA instantly or should they have a grace period?', 'wp-2fa' ); ?></h3>
		<p class="description">
			<?php \esc_html_e( 'When you enforce 2FA on users they have a grace period to configure 2FA. If they fail to configure it within the configured stipulated time, their account will be locked and have to be unlocked manually. Note that user accounts cannot be unlocked automatically, even if you change the settings. As a security precaution they always have to be unlocked them manually. Maximum grace period is 10 days.', 'wp-2fa' ); ?> <a href="https://melapress.com/support/kb/configure-grace-period-2fa/?utm_source=plugin&utm_medium=link&utm_campaign=wp2fa" target="_blank"><?php \esc_html_e( 'Learn more.', 'wp-2fa' ); ?></a>
		</p>

		<table class="form-table">
			<tbody>
				<tr>
					<th><label for="grace-policy"><?php \esc_html_e( 'Grace period', 'wp-2fa' ); ?></label></th>
					<td>
					<?php First_Time_Wizard_Steps::grace_period( true ); ?>
					</td>
				</tr>
			</tbody>
		</table>
			<?php
			$output = ob_get_clean();

			/**
			 * Gives the ability to manipulate the output.
			 *
			 * @param string $output - Parsed HTML with the methods.
			 *
			 * @since 2.0.0
			 */
			$output = \apply_filters( WP_2FA_PREFIX . 'grace_period', $output );

			echo $output; // phpcs:ignore
		}

		/**
		 * Disable removal of 2FA settings.
		 *
		 * @return void
		 *
		 * @since 2.0.0
		 */
		private static function disable_2fa_removal_setting() {
			ob_start();
			?>
		<br>
		<h3><?php \esc_html_e( 'Should users be allowed to disable 2FA from their user profile?', 'wp-2fa' ); ?></h3>
		<p class="description">
			<?php \esc_html_e( 'Users can configure and also disable 2FA on their profile by clicking the "Remove 2FA" button. Enable this setting to disable the Remove 2FA button so users cannot disable 2FA from their user profile.', 'wp-2fa' ); ?>
		</p>
		<table class="form-table">
			<tbody>
				<tr>
					<th><label for="hide-remove-2fa"><?php \esc_html_e( 'Hide the Remove 2FA button', 'wp-2fa' ); ?></label></th>
					<td>
						<fieldset>
							<input type="checkbox" id="hide-remove-2fa" name="wp_2fa_policy[hide_remove_button]" value="hide_remove_button"
							<?php checked( 1, WP2FA::get_wp2fa_setting( 'hide_remove_button' ), true ); ?>
							>
							<?php \esc_html_e( 'Hide the Remove 2FA button on user profile pages', 'wp-2fa' ); ?>
						</fieldset>
					</td>
				</tr>

			</tbody>
		</table>
			<?php
			$output = ob_get_clean();

			/**
			 * Gives the ability to manipulate the output.
			 *
			 * @param string $output - Parsed HTML with the methods.
			 *
			 * @since 2.0.0
			 */
			$output = \apply_filters( WP_2FA_PREFIX . 'disable_2fa', $output );

			echo $output; // phpcs:ignore
		}
	}
}
includes/classes/Admin/SettingsPages/class-settings-page-white-label.php000064400000041233150755130600022405 0ustar00<?php
/**
 * White label settings class.
 *
 * @package    wp2fa
 * @subpackage settings-pages
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 * @link       https://wordpress.org/plugins/wp-2fa/
 */

namespace WP2FA\Admin\SettingsPages;

use WP2FA\WP2FA;
use WP2FA\Utils\Debugging;
use WP2FA\Extensions\WhiteLabeling\White_Labeling_Render;

/**
 * White labeling settings tab
 */
if ( ! class_exists( '\WP2FA\Admin\SettingsPages\Settings_Page_White_Label' ) ) {
	/**
	 * Settings_Page_White_Label - Class for handling settings
	 *
	 * @since 2.0.0
	 */
	class Settings_Page_White_Label {

		/**
		 * Render the settings
		 *
		 * @return void
		 *
		 * @since 2.0.0
		 */
		public static function render() {
			settings_fields( WP_2FA_WHITE_LABEL_SETTINGS_NAME );
			self::white_labelling_tabs_wrapper();
			submit_button();
		}

		/**
		 * Validate options before saving
		 *
		 * @param array $input The settings array.
		 *
		 * @return array|void
		 *
		 * @since 2.0.0
		 */
		public static function validate_and_sanitize( $input ) {

			// Bail if user doesn't have permissions to be here.
			if ( ! current_user_can( 'manage_options' ) || ! isset( $_POST['action'] ) && ! check_admin_referer( 'wp2fa-step-choose-method' ) ) {
				return;
			}

			Debugging::log( 'The following settings will be processed (White Label): ' . "\n" . wp_json_encode( $input ) );

			$output['default-text-code-page'] = WP2FA::get_wp2fa_white_label_setting( 'default-text-code-page', false, false );

			if ( isset( $input['default-text-code-page'] ) && '' !== trim( (string) $input['default-text-code-page'] ) ) {
				$output['default-text-code-page'] = \wp_kses_post( $input['default-text-code-page'] );
			}

			$output['default-backup-code-page'] = WP2FA::get_wp2fa_white_label_setting( 'default-backup-code-page', false, false );

			if ( isset( $input['default-backup-code-page'] ) && '' !== trim( (string) $input['default-backup-code-page'] ) ) {
				$output['default-backup-code-page'] = \wp_strip_all_tags( $input['default-backup-code-page'] );
			}

			$output['login-to-view-area'] = WP2FA::get_wp2fa_white_label_setting( 'login-to-view-area', false, false );

			if ( isset( $input['login-to-view-area'] ) && '' !== trim( (string) $input['login-to-view-area'] ) ) {
				$output['login-to-view-area'] = \wp_strip_all_tags( $input['login-to-view-area'] );
			}

			$output['use_custom_2fa_message'] = WP2FA::get_wp2fa_white_label_setting( 'use_custom_2fa_message', false, false );

			if ( isset( $input['use_custom_2fa_message'] ) && '' !== trim( (string) $input['use_custom_2fa_message'] ) ) {
				$output['use_custom_2fa_message'] = \wp_strip_all_tags( $input['use_custom_2fa_message'] );
			}

			$output['custom-text-app-code-page']            = WP2FA::get_wp2fa_white_label_setting( 'custom-text-app-code-page', false, false );
			$output['custom-text-email-code-page']          = WP2FA::get_wp2fa_white_label_setting( 'custom-text-email-code-page', false, false );
			$output['custom-text-authy-code-page-intro']    = WP2FA::get_wp2fa_white_label_setting( 'custom-text-authy-code-page-intro', false, false );
			$output['custom-text-authy-code-page-awaiting'] = WP2FA::get_wp2fa_white_label_setting( 'custom-text-authy-code-page-awaiting', false, false );
			$output['custom-text-authy-code-page']          = WP2FA::get_wp2fa_white_label_setting( 'custom-text-authy-code-page', false, false );
			$output['custom-text-twilio-code-page']         = WP2FA::get_wp2fa_white_label_setting( 'custom-text-twilio-code-page', false, false );

			if ( isset( $input['custom-text-app-code-page'] ) && '' !== trim( (string) $input['custom-text-app-code-page'] ) ) {
				$output['custom-text-app-code-page'] = \wp_strip_all_tags( $input['custom-text-app-code-page'] );
			}

			if ( isset( $input['custom-text-email-code-page'] ) && '' !== trim( (string) $input['custom-text-email-code-page'] ) ) {
				$output['custom-text-email-code-page'] = \wp_strip_all_tags( $input['custom-text-email-code-page'] );
			}

			if ( isset( $input['custom-text-authy-code-page'] ) && '' !== trim( (string) $input['custom-text-authy-code-page'] ) ) {
				$output['custom-text-authy-code-page'] = \wp_strip_all_tags( $input['custom-text-authy-code-page'] );
			}

			if ( isset( $input['custom-text-authy-code-page-intro'] ) && '' !== trim( (string) $input['custom-text-authy-code-page-intro'] ) ) {
				$output['custom-text-authy-code-page-intro'] = \wp_strip_all_tags( $input['custom-text-authy-code-page-intro'] );
			}

			if ( isset( $input['custom-text-authy-code-page-awaiting'] ) && '' !== trim( (string) $input['custom-text-authy-code-page-awaiting'] ) ) {
				$output['custom-text-authy-code-page-awaiting'] = \wp_strip_all_tags( $input['custom-text-authy-code-page-awaiting'] );
			}

			if ( isset( $input['custom-text-twilio-code-page'] ) && '' !== trim( (string) $input['custom-text-twilio-code-page'] ) ) {
				$output['custom-text-twilio-code-page'] = \wp_strip_all_tags( $input['custom-text-twilio-code-page'] );
			}

			if ( isset( $_REQUEST['_wp_http_referer'] ) ) {
				$request_area      = wp_parse_url( \wp_unslash( $_REQUEST['_wp_http_referer'] ) ); // phpcs:ignore
				$request_area_path = strpos( $request_area['query'], 'white-label-section' );

				// If we have the input POSTed, we are on the right page so grab it.
				if ( isset( $input['enable_wizard_styling'] ) && '' !== trim( (string) $input['enable_wizard_styling'] ) ) {
					$output['enable_wizard_styling'] = \wp_strip_all_tags( $input['enable_wizard_styling'] );
				} else {
					// Are we on either the white labelling page (free and premium) or the custom CSS area (premium only)?
					if ( ! $request_area_path || $request_area_path && strpos( $request_area['query'], 'custom-css' ) ) {
						$output['enable_wizard_styling'] = '';
						$input['enable_wizard_styling']  = '';
					} else {
						$input['enable_wizard_styling']  = WP2FA::get_wp2fa_white_label_setting( 'enable_wizard_styling', false );
						$output['enable_wizard_styling'] = WP2FA::get_wp2fa_white_label_setting( 'enable_wizard_styling', false );
					}
				}

				if ( isset( $input['show_help_text'] ) && '' !== trim( (string) $input['show_help_text'] ) ) {
					$output['show_help_text'] = \wp_strip_all_tags( $input['show_help_text'] );
				} else {
					// Nothing was POSTed, check where we are in case that means we simple an empty/disabled checkbox.
					if ( $request_area_path && ! strpos( $request_area['query'], 'method_selection' ) ) {
						$input['show_help_text']  = WP2FA::get_wp2fa_white_label_setting( 'show_help_text', false );
						$output['show_help_text'] = WP2FA::get_wp2fa_white_label_setting( 'show_help_text', false );
					} else {
						$output['show_help_text'] = '';
						$input['show_help_text']  = '';
					}
				}

				// Same as above, but for the optional welcome.
				if ( isset( $input['enable_welcome'] ) && '' !== trim( (string) $input['enable_welcome'] ) ) {
					$output['enable_welcome'] = \wp_strip_all_tags( $input['enable_welcome'] );
				} elseif ( strpos( $request_area['query'], 'white-label-sub-section' ) && strpos( $request_area['query'], 'welcome' ) ) {
						$input['enable_welcome']  = '';
						$output['enable_welcome'] = '';
				} else {
					$input['enable_welcome']  = WP2FA::get_wp2fa_white_label_setting( 'enable_welcome', false );
					$output['enable_welcome'] = WP2FA::get_wp2fa_white_label_setting( 'enable_welcome', false );
				}

				if ( isset( $input['enable_wizard_logo'] ) && '' !== trim( (string) $input['enable_wizard_logo'] ) ) {
					$output['enable_wizard_logo'] = \wp_strip_all_tags( $input['enable_wizard_logo'] );
				} elseif ( strpos( $request_area['query'], 'white-label-sub-section' ) && strpos( $request_area['query'], 'welcome' ) ) {
						$input['enable_wizard_logo']  = '';
						$output['enable_wizard_logo'] = '';
				} else {
					$input['enable_wizard_logo']  = WP2FA::get_wp2fa_white_label_setting( 'enable_wizard_logo', false );
					$output['enable_wizard_logo'] = WP2FA::get_wp2fa_white_label_setting( 'enable_wizard_logo', false );
				}
			}


			if ( isset( $input['login_custom_css'] ) && ! empty( $input['login_custom_css'] ) ) {
				if ( preg_match( '#</?\w+#', $input['login_custom_css'] ) ) {
					add_settings_error(
						WP_2FA_SETTINGS_NAME,
						\esc_attr( 'markup_invalid_settings_error' ),
						\esc_html__( 'Markup is not allowed in Login area CSS.', 'wp-2fa' ),
						'error'
					);
					$output['login_custom_css'] = WP2FA::get_wp2fa_white_label_setting( 'login_custom_css', false );
					$input['login_custom_css']  = WP2FA::get_wp2fa_white_label_setting( 'login_custom_css', false );
				} else {
					$output['login_custom_css'] = \wp_strip_all_tags( $input['login_custom_css'] );
					$input['login_custom_css']  = \wp_strip_all_tags( $input['login_custom_css'] );
				}
			}

			if ( isset( $input['disable_login_css'] ) && '' !== trim( (string) $input['disable_login_css'] ) ) {
				$output['disable_login_css'] = \wp_strip_all_tags( $input['disable_login_css'] );
			} else {
				// Nothing was POSTed, check where we are in case that means we simple an empty/disabled checkbox.
				if ( $request_area_path && ! strpos( $request_area['query'], 'method_selection' ) ) {
					$input['disable_login_css']  = WP2FA::get_wp2fa_white_label_setting( 'disable_login_css', false );
					$output['disable_login_css'] = WP2FA::get_wp2fa_white_label_setting( 'disable_login_css', false );
				} else {
					$output['disable_login_css'] = '';
					$input['disable_login_css']  = '';
				}
			}

			// Remove duplicates from settings errors. We do this as this sanitization callback is actually fired twice, so we end up with duplicates when saving the settings for the FIRST TIME only. The issue is not present once the settings are in the DB as the sanitization wont fire again. For details on this core issue - https://core.trac.wordpress.org/ticket/21989.
			global $wp_settings_errors;
			if ( isset( $wp_settings_errors ) ) {
				$errors             = array_map( 'unserialize', array_unique( array_map( 'serialize', $wp_settings_errors ) ) );
				$wp_settings_errors = $errors; // phpcs:ignore
			}

			/**
			 * Filter the values we are about to store in the plugin settings.
			 *
			 * @param array $output - The output array with all the data we will store in the settings.
			 * @param array $input - The input array with all the data we received from the user.
			 *
			 * @since 2.0.0
			 */
			$output = \apply_filters( WP_2FA_PREFIX . 'filter_output_content', $output, $input );

			Debugging::log( 'The following settings are being saved (White Label): ' . "\n" . wp_json_encode( $output ) );

			return $output;
		}

		/**
		 * Updates global white label network options
		 *
		 * @return void
		 *
		 * @since 2.0.0
		 *
		 * @SuppressWarnings(PHPMD.ExitExpressions)
		 */
		public static function update_wp2fa_network_options() {

			if ( isset( $_POST[ WP_2FA_WHITE_LABEL_SETTINGS_NAME ] ) ) {
				check_admin_referer( 'wp_2fa_white_label-options' );
				$options         = self::validate_and_sanitize( wp_unslash( $_POST[ WP_2FA_WHITE_LABEL_SETTINGS_NAME ] ) ); // phpcs:ignore
				$settings_errors = get_settings_errors( WP_2FA_WHITE_LABEL_SETTINGS_NAME );
				if ( ! empty( $settings_errors ) ) {

					// redirect back to our options page.
					wp_safe_redirect(
						add_query_arg(
							array(
								'page' => 'wp-2fa-settings',
								'wp_2fa_network_settings_error' => urlencode_deep( $settings_errors[0]['message'] ),
							),
							network_admin_url( 'settings.php' )
						)
					);
					exit;

				}
				WP2FA::update_plugin_settings( $options, false, WP_2FA_WHITE_LABEL_SETTINGS_NAME );

				// redirect back to our options page.
				wp_safe_redirect(
					add_query_arg(
						array(
							'page' => 'wp-2fa-settings',
							'tab'  => 'white-label-settings',
							'wp_2fa_network_settings_updated' => 'true',
						),
						network_admin_url( 'admin.php' )
					)
				);
				exit;
			}
		}

		/**
		 * Wrapper which adds special tabbed navigation and content
		 *
		 * @return void
		 *
		 * @since 2.3.0
		 */
		private static function white_labelling_tabs_wrapper() {
			/**
			 * Fires right before the white label settings tab HTML, handles tabbed nav.
			 *
			 * @since 2.3.0
			 */
			do_action( WP_2FA_PREFIX . 'white_labeling_tabbed_navigation' );
				self::change_default_text_area();
		}

		/**
		 * Shows default settings input to the user
		 *
		 * @return void
		 *
		 * @since 2.0.0
		 */
		private static function change_default_text_area() {
			/**
			 * Fires right before the white label settings tab HTML rendering.
			 *
			 * @since 2.0.0
			 */
			do_action( WP_2FA_PREFIX . 'white_labeling_settings_page_before_default_text' );
			?>

		<h3><?php \esc_html_e( 'Change the default text used in the 2FA code page', 'wp-2fa' ); ?></h3>
		<p class="description">
			<?php \esc_html_e( 'This is the text shown to the users on the page when they are asked to enter the 2FA code. To change the default text, simply type it in the below placeholder.', 'wp-2fa' ); ?>
		</p>

		<table class="form-table">
			<tbody>
				<tr>
					<th><label for="2fa-method"><?php \esc_html_e( '2FA code page text', 'wp-2fa' ); ?></label></th>
					<td>
						<?php
						if ( class_exists( 'WP2FA\Extensions\WhiteLabeling\White_Labeling_Render' ) ) {
							echo White_Labeling_Render::get_method_text_editor( 'default-text-code-page' ); // phpcs:ignore
						} else {
							echo self::create_standard_editor( WP2FA::get_wp2fa_white_label_setting( 'default-text-code-page', true ), 'default-text-code-page' );
						}
						?>
						<div style="margin-top: 5px;"><span><strong><i><?php \esc_html_e( 'Note:', 'wp-2fa' ); ?></i></strong> <?php \esc_html_e( 'Only plain text is allowed.', 'wp-2fa' ); ?></span></div>
					</td>
				</tr>
				<tr>
					<th><label for="backup-method"><?php \esc_html_e( 'Backup code page text', 'wp-2fa' ); ?></label></th>
					<td>
						<?php
						if ( class_exists( 'WP2FA\Extensions\WhiteLabeling\White_Labeling_Render' ) ) {
							echo White_Labeling_Render::get_method_text_editor( 'default-backup-code-page' ); // phpcs:ignore
						} else {
							echo self::create_standard_editor( WP2FA::get_wp2fa_white_label_setting( 'default-backup-code-page', true ), 'default-backup-code-page' );
						}
						?>
						<div style="margin-top: 5px;"><span><strong><i><?php \esc_html_e( 'Note:', 'wp-2fa' ); ?></i></strong> <?php \esc_html_e( 'Only plain text is allowed.', 'wp-2fa' ); ?></span></div>
					</td>
				</tr>

				<tr>
					<th><label for="backup-method"><?php \esc_html_e( 'Text for logged out users trying to access the 2FA configuration page', 'wp-2fa' ); ?></label></th>
					<td>
						<?php
						if ( class_exists( 'WP2FA\Extensions\WhiteLabeling\White_Labeling_Render' ) ) {
							echo White_Labeling_Render::get_method_text_editor( 'login-to-view-area' ); // phpcs:ignore
						} else {
							echo self::create_standard_editor( WP2FA::get_wp2fa_white_label_setting( 'login-to-view-area', true ), 'login-to-view-area' );
						}
						?>
					</td>
				</tr>

				<?php
				/**
				 * Gives the ability for the 3rd party extensions to add additional white label settings
				 */
				do_action( WP_2FA_PREFIX . 'white_labeling_settings_page_after_code_page' );
				?>
			</tbody>
		</table>
			<h3><?php \esc_html_e( 'Change the styling of the user 2FA wizards', 'wp-2fa' ); ?></h3>
			<p class="description">
				<?php \esc_html_e( 'By default, the user 2FA wizards which the users see and use to set up 2FA have our own styling. Disable the below setting so the wizards use the styling of your website\'s theme.', 'wp-2fa' ); ?>
			</p>
			<table class="form-table">
				<tbody>
					<tr>
						<th><label for="enable_wizard_styling"><?php \esc_html_e( 'Enable styling', 'wp-2fa' ); ?></label></th>
						<td>
							<fieldset>
								<input type="checkbox" id="enable_wizard_styling" name="wp_2fa_white_label[enable_wizard_styling]" value="enable_wizard_styling"
								<?php \checked( 'enable_wizard_styling', WP2FA::get_wp2fa_white_label_setting( 'enable_wizard_styling' ), true ); ?>
								>
								<?php \esc_html_e( 'Enable our CSS within user wizards', 'wp-2fa' ); ?>
							</fieldset>
						</td>
					</tr>
				</tbody>
			</table>

			<?php
			/**
			 * Fires after the white label settings tab is rendered.
			 *
			 * @since 2.0.0
			 */
			do_action( WP_2FA_PREFIX . 'white_labeling_settings_page_after_default_text' );
		}

		/**
		 * Simple function to create a neat text editor in free.
		 *
		 * @param string $content
		 * @param string $requested_slide
		 * @return void
		 */
		private static function create_standard_editor( $content, $requested_slide ) {
			$settings = array(
				'media_buttons' => false,
				'editor_height' => 200,
				'textarea_name' => 'wp_2fa_white_label[' . $requested_slide . ']',
			);

			if ( isset( $content ) ) {
				wp_editor( $content, $requested_slide, $settings );
			}
		}
	}
}
includes/classes/Admin/SettingsPages/class-settings-page-email.php000064400000051174150755130600021304 0ustar00<?php
/**
 * Email settings class.
 *
 * @package    wp2fa
 * @subpackage settings-pages
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 * @link       https://wordpress.org/plugins/wp-2fa/
 */

namespace WP2FA\Admin\SettingsPages;

use WP2FA\Email_Template;
use WP2FA\WP2FA;
use WP2FA\Utils\Debugging;
use WP2FA\Admin\Helpers\WP_Helper;
use WP2FA\Admin\Controllers\Settings;
use WP2FA\Utils\Settings_Utils;
use WP2FA\Admin\Settings_Page;
use WP2FA\Extensions\WhiteLabeling\White_Labeling_Render;

/**
 * Email settings tab
 */
if ( ! class_exists( '\WP2FA\Admin\SettingsPages\Settings_Page_Email' ) ) {
	/**
	 * Settings_Page_Email - Class for handling email settings
	 *
	 * @since 2.0.0
	 */
	class Settings_Page_Email {

		/**
		 * Render the settings
		 *
		 * @return void
		 *
		 * @since 2.0.0
		 */
		public static function render() {
			\settings_fields( WP_2FA_EMAIL_SETTINGS_NAME );
			self::email_from_settings();
			self::email_settings();
			\submit_button( \esc_html__( 'Save email settings and templates', 'wp-2fa' ) );
		}

		/**
		 * Handle saving email options to the network main site options.
		 *
		 * @return void
		 *
		 * @since 2.0.0
		 *
		 * @SuppressWarnings(PHPMD.ExitExpressions)
		 */
		public static function update_wp2fa_network_options() {
			if ( isset( $_POST['email_from_setting'] ) ) { // phpcs:ignore
				$options = self::validate_and_sanitize( wp_unslash( $_POST ) ); // phpcs:ignore

				if ( isset( $_POST['email_from_setting'] ) && 'use-custom-email' === $_POST['email_from_setting'] && isset( $_POST['custom_from_display_name'] ) && empty( $_POST['custom_from_display_name'] ) || isset( $_POST['email_from_setting'] ) && 'use-custom-email' === $_POST['email_from_setting'] && isset( $_POST['custom_from_email_address'] ) && empty( $_POST['custom_from_email_address'] ) ) { // phpcs:ignore
					// redirect back to our options page.
					\wp_safe_redirect(
						\add_query_arg(
							array(
								'page' => 'wp-2fa-settings',
								'wp_2fa_network_settings_updated' => 'false',
								'tab'  => 'email-settings',
							),
							\network_admin_url( 'admin.php' )
						)
					);
					exit;
				}

				Settings_Utils::update_option( WP_2FA_EMAIL_SETTINGS_NAME, $options );
			}

			// redirect back to our options page.
			\wp_safe_redirect(
				\add_query_arg(
					array(
						'page'                            => 'wp-2fa-settings',
						'wp_2fa_network_settings_updated' => 'true',
						'tab'                             => 'email-settings',
					),
					\network_admin_url( 'admin.php' )
				)
			);
			exit;
		}

		/**
		 * Email settings
		 *
		 * @return void
		 *
		 * @since 2.0.0
		 */
		private static function email_from_settings() {
			?>
		<h3><?php \esc_html_e( 'Which email address should the plugin use as a from address?', 'wp-2fa' ); ?></h3>
		<p class="description">
			<?php \esc_html_e( 'Use these settings to customize the "from" name and email address for all correspondence sent from our plugin.', 'wp-2fa' ); ?>
		</p>
		<table class="form-table">
			<tbody>
				<tr>
					<th><label for="2fa-method"><?php \esc_html_e( 'From email & name', 'wp-2fa' ); ?></label>
					</th>
					<td>
						<fieldset class="contains-hidden-inputs">
							<label for="use-defaults">
								<input type="radio" name="email_from_setting" id="use-defaults" value="use-defaults"
								<?php \checked( WP2FA::get_wp2fa_email_templates( 'email_from_setting' ), 'use-defaults' ); ?>
								>
							<span><?php \esc_html_e( 'Use the email address ', 'wp-2fa' ); ?> <?php echo Settings_Page::get_default_email_address(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span>
							</label>
							<br/>
							<label for="use-custom-email">
								<input type="radio" name="email_from_setting" id="use-custom-email" value="use-custom-email"
								<?php \checked( WP2FA::get_wp2fa_email_templates( 'email_from_setting' ), 'use-custom-email' ); ?>
								data-unhide-when-checked=".custom-from-inputs">
								<span><?php \esc_html_e( 'Use another email address', 'wp-2fa' ); ?></span>
							</label>
							<fieldset class="hidden custom-from-inputs">
								<p class="description">
									<?php \esc_html_e( 'A \'From email\' address with a domain different than that of your website domain name, or with a domain that the hosting does not relay might cause the notification emails to be blocked, marked as spam, or not delivered at all. If you are not 100% sure about this change, consult with your web host.', 'wp-2fa' ); ?>
								</p>
								<br/>
								<span><?php \esc_html_e( 'Email Address:', 'wp-2fa' ); ?></span> <input type="text" id="custom_from_email_address" name="custom_from_email_address" value="<?php echo \esc_attr( WP2FA::get_wp2fa_email_templates( 'custom_from_email_address' ) ); ?>"><br><br>
								<span><?php \esc_html_e( 'Display Name:', 'wp-2fa' ); ?></span> <input type="text" id="custom_from_display_name" name="custom_from_display_name" value="<?php echo \esc_attr( WP2FA::get_wp2fa_email_templates( 'custom_from_display_name' ) ); ?>">
							</fieldset>

						</fieldset>
					</td>
				</tr>
			</tbody>
		</table>
		<div class="description"><i><?php \esc_html_e( 'Tip: The \'From email\' address should match your website domain. If the "from address" does not match your website domain, the emails may be blocked or marked as spam. If you are not sure about this please consult with your website administrator / developer or ', 'wp-2fa' ); ?><a href="<?php echo \esc_url( 'https://melapress.com/contact/?utm_source=plugin&utm_medium=link&utm_campaign=wp2fa' ); ?>" target="_blank"><?php \esc_html_e( 'contact us', 'wp-2fa' ); ?></a> <?php \esc_html_e( 'for more information.', 'wp-2fa' ); ?></i></div>
		<br>
		<hr>

		<h3><?php \esc_html_e( 'Email delivery test', 'wp-2fa' ); ?></h3>
		<p class="description">
			<?php \esc_html_e( 'The plugin sends emails with one-time codes, blocked account notifications and more. Use the button below to confirm the plugin can successfully send emails.', 'wp-2fa' ); ?>
		</p>
		<p>
			<button type="button" name="test_email_config_test"
					class="button js-button-test-email-trigger"
					data-email-id="config_test"
					<?php echo WP_Helper::create_data_nonce( 'wp-2fa-email-test-config_test' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
				<?php \esc_html_e( 'Test email delivery', 'wp-2fa' ); ?>
			</button>
		</p>

		<br>
		<hr>

			<?php
		}

		/**
		 * Creates the email notification definitions.
		 *
		 * @return Email_Template[]
		 *
		 * @since 2.0.0
		 */
		public static function get_email_notification_definitions() {

			$backup_codes = new Email_Template(
				'user_backup_codes',
				\esc_html__( 'User backup codes email', 'wp-2fa' ),
				\esc_html__( 'This email can be sent a user once backup codes are generated.', 'wp-2fa' )
			);
			$backup_codes->set_can_be_toggled( false );

			$result = array(
				new Email_Template(
					'login_code_setup',
					\esc_html__( '2FA setup code email', 'wp-2fa' ),
					\esc_html__( 'This is the email sent to a user when setting up 2FA via email.', 'wp-2fa' )
				),
				new Email_Template(
					'login_code',
					\esc_html__( 'Login code email', 'wp-2fa' ),
					\esc_html__( 'This is the email sent to a user when a login code is required.', 'wp-2fa' )
				),
				new Email_Template(
					'account_locked',
					\esc_html__( 'User account locked email', 'wp-2fa' ),
					\esc_html__( 'This is the email sent to a user upon grace period expiry.', 'wp-2fa' )
				),
				new Email_Template(
					'account_unlocked',
					\esc_html__( 'User account unlocked email', 'wp-2fa' ),
					\esc_html__( 'This is the email sent to a user when the user\'s account has been unlocked.', 'wp-2fa' )
				),
				new Email_Template(
					'reset_password_code',
					\esc_html__( 'User reset password code email', 'wp-2fa' ),
					\esc_html__( 'This is the email sent to a user when a password reset is requested.', 'wp-2fa' )
				),
				$backup_codes,
			);

			/**
			 * Add an option for external providers to implement their own email template settings for the settings tab.
			 *
			 * @param array $result - The array with all the email templates.
			 *
			 * @since 2.0.0
			 */
			$result = \apply_filters( WP_2FA_PREFIX . 'email_notification_definitions', $result );

			if ( count( $result ) > 6 ) {
				$result[0]->set_can_be_toggled( false );
				$result[1]->set_can_be_toggled( false );
				$result[2]->set_can_be_toggled( false );
				$result[3]->set_email_content_id( 'user_account_locked' );
				$result[4]->set_email_content_id( 'user_account_unlocked' );
				$result[5]->set_can_be_toggled( false );
			} else {
				$result[0]->set_can_be_toggled( false );
				$result[1]->set_can_be_toggled( false );
				$result[2]->set_email_content_id( 'user_account_locked' );
				$result[3]->set_email_content_id( 'user_account_unlocked' );
				$result[4]->set_can_be_toggled( false );
			}
			return $result;
		}

		/**
		 * Validate email templates before saving
		 *
		 * @since 2.0.0
		 *
		 * @SuppressWarnings(PHPMD.ExitExpressions)
		 */
		public static function validate_and_sanitize() {

			// Bail if user doesn't have permissions to be here.
			if ( ! \current_user_can( 'manage_options' ) ) {
				return;
			}

			Debugging::log( 'The following settings will be processed (E-mail): ' . "\n" . wp_json_encode( $_POST ) ); // phpcs:ignore

			if ( empty( $_POST ) || ! isset( $_POST['_wpnonce'] ) || empty( $_POST['_wpnonce'] ) || ! \wp_verify_nonce( $_POST['_wpnonce'], WP_2FA_PREFIX . 'email_settings-options' ) && ! \wp_verify_nonce( $_POST['_wpnonce'], WP_2FA_PREFIX . 'settings-options' ) || ! \wp_verify_nonce( $_POST['_wpnonce'], WP_2FA_PREFIX . 'email_settings-options' ) && ! \wp_verify_nonce( $_POST['_wpnonce'], WP_2FA_PREFIX . 'settings-options' ) ) { // phpcs:ignore
				die( \esc_html__( 'Nonce verification failed.', 'wp-2fa' ) );
			}

			$output = array();

			if ( isset( $_POST['email_from_setting'] ) && 'use-defaults' === $_POST['email_from_setting'] || isset( $_POST['email_from_setting'] ) && 'use-custom-email' === $_POST['email_from_setting'] ) {
				$output['email_from_setting'] = \sanitize_text_field( \wp_unslash( $_POST['email_from_setting'] ) );
			}

			if ( isset( $_POST['email_from_setting'] ) && 'use-custom-email' === $_POST['email_from_setting'] && isset( $_POST['custom_from_email_address'] ) && empty( $_POST['custom_from_email_address'] ) ) {
				\add_settings_error(
					WP_2FA_SETTINGS_NAME,
					\esc_attr( 'email_from_settings_error' ),
					\esc_html__( 'Please provide an email address', 'wp-2fa' ),
					'error'
				);
				$output['custom_from_email_address'] = '';
			}

			if ( isset( $_POST['email_from_setting'] ) && 'use-custom-email' === $_POST['email_from_setting'] && isset( $_POST['custom_from_display_name'] ) && empty( $_POST['custom_from_display_name'] ) ) {
				\add_settings_error(
					WP_2FA_SETTINGS_NAME,
					\esc_attr( 'display_name_settings_error' ),
					\esc_html__( 'Please provide a display name.', 'wp-2fa' ),
					'error'
				);
				$output['custom_from_email_address'] = '';
			}

			if ( isset( $_POST['custom_from_email_address'] ) && ! empty( $_POST['custom_from_email_address'] ) ) {
				if ( ! filter_var( \wp_unslash( $_POST['custom_from_email_address'] ), FILTER_VALIDATE_EMAIL ) ) {
					\add_settings_error(
						WP_2FA_SETTINGS_NAME,
						\esc_attr( 'email_invalid_settings_error' ),
						\esc_html__( 'Please provide a valid email address. Your email address has not been updated.', 'wp-2fa' ),
						'error'
					);
				}
				$output['custom_from_email_address'] = \sanitize_email( \wp_unslash( $_POST['custom_from_email_address'] ) );

				Settings_Utils::delete_option( 'dismiss_notice_mail_domain' );
			}

			if ( ! isset( $_POST['email_from_setting'] ) ) {
				Settings_Utils::delete_option( 'dismiss_notice_mail_domain' );
			}

			if ( isset( $_POST['custom_from_display_name'] ) && ! empty( $_POST['custom_from_display_name'] ) ) {
				// Check if the string contains HTML/tags.
				preg_match( "/<\/?\w+((\s+\w+(\s*=\s*(?:\".*?\"|'.*?'|[^'\">\s]+))?)+\s*|\s*)\/?>/", sanitize_text_field( wp_unslash( $_POST['custom_from_display_name'] ) ), $matches );
				if ( count( $matches ) > 0 ) {
					\add_settings_error(
						WP_2FA_SETTINGS_NAME,
						\esc_attr( 'display_name_invalid_settings_error' ),
						\esc_html__( 'Please only use alphanumeric text. Your display name has not been updated.', 'wp-2fa' ),
						'error'
					);
				} else {
					$output['custom_from_display_name'] = \sanitize_text_field( \wp_unslash( $_POST['custom_from_display_name'] ) );
				}
			}

			if ( isset( $_POST['login_code_email_subject'] ) ) {
				$output['login_code_email_subject'] = \wp_kses_post( \wp_unslash( $_POST['login_code_email_subject'] ) );
			}

			if ( isset( $_POST['login_code_email_body'] ) ) {
				$output['login_code_email_body'] = \wpautop( \wp_kses_post( \wp_unslash( $_POST['login_code_email_body'] ) ) );
			}

			if ( isset( $_POST['login_code_setup_email_subject'] ) ) {
				$output['login_code_setup_email_subject'] = \wp_kses_post( \wp_unslash( $_POST['login_code_setup_email_subject'] ) );
			}

			if ( isset( $_POST['login_code_setup_email_body'] ) ) {
				$output['login_code_setup_email_body'] = \wpautop( \wp_kses_post( \wp_unslash( $_POST['login_code_setup_email_body'] ) ) );
			}

			if ( isset( $_POST['user_account_locked_email_subject'] ) ) {
				$output['user_account_locked_email_subject'] = \wp_kses_post( \wp_unslash( $_POST['user_account_locked_email_subject'] ) );
			}

			if ( isset( $_POST['user_account_locked_email_body'] ) ) {
				$output['user_account_locked_email_body'] = \wpautop( \wp_kses_post( \wp_unslash( $_POST['user_account_locked_email_body'] ) ) );
			}

			if ( isset( $_POST['user_account_unlocked_email_subject'] ) ) {
				$output['user_account_unlocked_email_subject'] = \wp_kses_post( \wp_unslash( $_POST['user_account_unlocked_email_subject'] ) );
			}

			if ( isset( $_POST['user_account_unlocked_email_body'] ) ) {
				$output['user_account_unlocked_email_body'] = \wpautop( \wp_kses_post( \wp_unslash( $_POST['user_account_unlocked_email_body'] ) ) );
			}

			if ( isset( $_POST['reset_password_code_email_body'] ) ) {
				$output['reset_password_code_email_body'] = \wpautop( \wp_kses_post( \wp_unslash( $_POST['reset_password_code_email_body'] ) ) );
			}

			if ( isset( $_POST['reset_password_code_email_subject'] ) ) {
				$output['reset_password_code_email_subject'] = \wp_kses_post( \wp_unslash( $_POST['reset_password_code_email_subject'] ) );
			}

			$output['send_account_locked_email'] = '';
			if ( isset( $_POST['send_account_locked_email'] ) && 'enable_account_locked_email' === $_POST['send_account_locked_email'] ) {
				$output['send_account_locked_email'] = \sanitize_text_field( \wp_unslash( $_POST['send_account_locked_email'] ) );
			}

			$output['send_account_unlocked_email'] = '';
			if ( isset( $_POST['send_account_unlocked_email'] ) && 'enable_account_unlocked_email' === $_POST['send_account_unlocked_email'] ) {
				$output['send_account_unlocked_email'] = \sanitize_text_field( \wp_unslash( $_POST['send_account_unlocked_email'] ) );
			}

			$output['send_login_code_email'] = '';
			if ( isset( $_POST['send_login_code_email'] ) && 'enable_send_login_code_email' === $_POST['send_login_code_email'] ) {
				$output['send_login_code_email'] = \sanitize_text_field( \wp_unslash( $_POST['send_login_code_email'] ) );
			}

			if ( isset( $_POST['user_backup_codes_email_subject'] ) ) {
				$output['user_backup_codes_email_subject'] = \wp_kses_post( \wp_unslash( $_POST['user_backup_codes_email_subject'] ) );
			}

			if ( isset( $_POST['user_backup_codes_email_body'] ) ) {
				$output['user_backup_codes_email_body'] = \wpautop( \wp_kses_post( \wp_unslash( $_POST['user_backup_codes_email_body'] ) ) );
			}

			/**
			 * Filter the values we are about to store in the plugin settings.
			 *
			 * @param array $output - The output array with all the data we will store in the settings.
			 *
			 * @since 2.0.0
			 */
			$output = \apply_filters( WP_2FA_PREFIX . 'filter_output_email_template_content', $output );

			Debugging::log( 'The following settings are being saved (E-mail): ' . "\n" . \wp_json_encode( $output ) );

			// Remove duplicates from settings errors. We do this as this sanitization callback is actually fired twice, so we end up with duplicates when saving the settings for the FIRST TIME only. The issue is not present once the settings are in the DB as the sanitization wont fire again. For details on this core issue - https://core.trac.wordpress.org/ticket/21989.
			global $wp_settings_errors;
			if ( isset( $wp_settings_errors ) ) {
				$errors             = array_map( 'unserialize', array_unique( array_map( 'serialize', $wp_settings_errors ) ) );
				$wp_settings_errors = $errors; // phpcs:ignore
			}

			if ( isset( $output ) ) {
				return $output;
			} else {
				return;
			}
		}

		/**
		 * Email settings
		 *
		 * @return void
		 *
		 * @since 2.0.0
		 */
		private static function email_settings() {
			$custom_user_page_id = Settings::check_setting_in_all_roles( 'custom-user-page-id' );
			if ( empty( $custom_user_page_id ) ) {
				$custom_user_page_id = Settings::check_setting_in_all_roles( 'custom-user-page-url' );
			}
			$email_template_definitions = self::get_email_notification_definitions();
			?>
		<h1><?php \esc_html_e( 'Email Templates', 'wp-2fa' ); ?></h1>
			<?php foreach ( $email_template_definitions as $email_template ) : ?>
				<?php $template_id = $email_template->get_id(); ?>
		<h3><?php echo \esc_html( $email_template->get_title() ); ?></h3>
		<p class="description"><?php echo $email_template->get_description(); // phpcs:ignore ?></p>
		<table class="form-table">
			<tbody>
				<?php if ( $email_template->can_be_toggled() ) : ?>
				<tr>
					<th><label for="send_<?php echo \esc_attr( $template_id ); ?>_email"><?php \esc_html_e( 'Send this email', 'wp-2fa' ); ?></label></th>
					<td>
						<fieldset>
							<input type="checkbox" id="send_<?php echo \esc_attr( $template_id ); ?>_email" name="send_<?php echo \esc_attr( $template_id ); ?>_email" value="enable_<?php echo \esc_attr( $template_id ); ?>_email"
							<?php \checked( 'enable_' . $template_id . '_email', WP2FA::get_wp2fa_email_templates( 'send_' . $template_id . '_email' ) ); ?>
							>
							<label for="send_<?php echo \esc_attr( $template_id ); ?>_email"><?php \esc_html_e( 'Uncheck to disable this message.', 'wp-2fa' ); ?></label>
						</fieldset>
					</td>
				</tr>
			<?php endif; ?>
				<?php $template_id = $email_template->get_email_content_id(); ?>
				<tr>
					<th><label for="<?php echo \esc_attr( $template_id ); ?>_email_subject"><?php \esc_html_e( 'Email subject', 'wp-2fa' ); ?></label></th>
					<td>
						<fieldset>
							<input type="text" id="<?php echo \esc_attr( $template_id ); ?>_email_subject" name="<?php echo \esc_attr( $template_id ); ?>_email_subject" class="large-text" value="<?php echo \esc_attr( WP2FA::get_wp2fa_email_templates( $template_id . '_email_subject' ) ); ?>">
						</fieldset>
					</td>
				</tr>
				<tr>
					<th>
						<label for="<?php echo \esc_attr( $template_id ); ?>_email_body"><?php \esc_html_e( 'Email body', 'wp-2fa' ); ?></label>
						</br>
						<label for="<?php echo \esc_attr( $template_id ); ?>_email_tags" style="font-weight: 400;"><?php \esc_html_e( 'Available template tags:', 'wp-2fa' ); ?></label>
						</br>
						</br>
						<span style="font-weight: 400;">
							{site_url}</br>
							{site_name}</br>
							{grace_period}</br>
							{user_login_name}</br>
							{user_first_name}</br>
							{user_last_name}</br>
							{user_display_name}</br>
							{login_code}</br>
							{user_ip_address}</br>
							{backup_codes}
							<?php
							if ( ! empty( $custom_user_page_id ) ) {
								echo '</br>{2fa_settings_page_url}';
							}
							?>
						</span>
					</th>
					<td>
						<fieldset>
							<?php
							$message   = WP2FA::get_wp2fa_email_templates( $template_id . '_email_body' );
							$content   = $message;
							$editor_id = $template_id . '_email_body';
							$settings  = array(
								'media_buttons' => false,
								'editor_height' => 200,
							);
							\wp_editor( $content, $editor_id, $settings );
							?>
						</fieldset>
						<p>
							<button type="button" name="test_email_<?php echo \esc_attr( $template_id ); ?>"
									class="button js-button-test-email-trigger"
									data-email-id="<?php echo \esc_attr( $template_id ); ?>"
									<?php echo WP_Helper::create_data_nonce( 'wp-2fa-email-test-' . $template_id ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
								<?php \esc_html_e( 'Send test email', 'wp-2fa' ); ?>
							</button>
						</p>
					</td>
				</tr>
			</tbody>
		</table>
		<br>
		<hr>
		<?php endforeach; ?>
		<?php
			$additional_content = apply_filters( WP_2FA_PREFIX . 'append_to_email_and_sms_template_settings', '' );
			echo $additional_content;
		}
	}
}
includes/classes/Admin/index.php000064400000000046150755130600012661 0ustar00<?php
/**
 * Nothing to see here.
 */
includes/classes/Admin/class-plugin-updated-notice.php000064400000010505150755130600017057 0ustar00<?php
/**
 * Responsible for WP2FA update notices.
 *
 * @package    wp2fa
 * @subpackage user-utils
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 * @link       https://wordpress.org/plugins/wp-2fa/
 */

namespace WP2FA\Admin;

use WP2FA\Utils\Settings_Utils;

/**
 * Plugin_Updated_Notice class with user notification filters
 *
 * @since 2.7.0
 */
if ( ! class_exists( '\WP2FA\Admin\Plugin_Updated_Notice' ) ) {
	/**
	 * Plugin_Updated_Notice - Class for displaying notices to our users.
	 */
	class Plugin_Updated_Notice {

		/**
		 * Lets set things up
		 *
		 * @since 2.7.0
		 */
		public static function init() {
			add_action( 'admin_init', array( __CLASS__, 'on_plugin_update' ), 10 );
			add_action( 'admin_notices', array( __CLASS__, 'plugin_update_banner' ) );
			add_action( 'network_admin_notices', array( __CLASS__, 'plugin_update_banner' ) );
			add_action( 'wp_ajax_dismiss_update_notice', array( __CLASS__, 'dismiss_update_notice' ) );
		}

		/**
		 * The nag content
		 *
		 * @since 2.7.0
		 * @return void
		 */
		public static function plugin_update_banner() {
			$screen         = get_current_screen();
			$correct_screen = ( 'toplevel_page_wp-2fa-policies-network' === $screen->base || 'toplevel_page_wp-2fa-policies' === $screen->base ) ? true : false;

			if ( $correct_screen && Settings_Utils::get_option( 'wp_2fa_update_notice_needed', false ) ) {
				/* translators: %s: version number. */
				printf( '<div id="wp_2fa_update_notice" class="notice notice-success is-dismissible"><img src="' . esc_url( WP_2FA_URL . 'dist/images/wp-2fa-square.png' ) . '"><p><strong>' . esc_html__( 'Thank you for updating WP 2FA.', 'wp-2fa' ) . '</strong></p><p>' . esc_html__( 'This is version %s. Check out the release notes to see what is new and improved in this update.', 'wp-2fa' ) . '</p><a href="https://melapress.com/wordpress-2fa/releases/" target="_blank" class="button button-primary dismiss_update_notice" data-dismiss-nonce="%2s">' . esc_html__( 'Release notes', 'wp-2fa' ) . '</a></p></div>', WP_2FA_VERSION, wp_create_nonce( 'wp_2fa_dismiss_update_notice_nonce' ) );
				?>
					<script type="text/javascript">
					//<![CDATA[
					jQuery(document).ready(function( $ ) {
						jQuery( 'body' ).on( 'click', 'a.dismiss_update_notice, #wp_2fa_update_notice .notice-dismiss', function ( e ) {
							var nonce  = jQuery( '#wp_2fa_update_notice [data-dismiss-nonce]' ).attr( 'data-dismiss-nonce' );
							
							jQuery.ajax({
								type: 'POST',
								url: '<?php echo esc_url( admin_url( 'admin-ajax.php' ) ); ?>',
								async: true,
								data: {
									action: 'dismiss_update_notice',
									nonce : nonce,
								},
								success: function ( result ) {		
									jQuery( '#wp_2fa_update_notice' ).slideUp( 300 );
								}
							});
						});
					});
					//]]>
					</script>
					<style>
						#wp_2fa_update_notice {
							border: 2px solid #0f5cf2
						}
						#wp_2fa_update_notice .button-primary {
							background: #0f5cf2;
							border-color: #0f5cf2;
						}
						#wp_2fa_update_notice img {
							float: left;
							max-width: 100px;
							margin: 10px 12px 10px 0;
						}
					</style>
				<?php
			}
		}

		/**
		 * Redirects user to admin on plugin update.
		 *
		 * @since 2.7.0
		 * @return void
		 */
		public static function on_plugin_update() {
			if ( Settings_Utils::get_option( 'wp_2fa_update_redirection_needed', false ) ) {
				delete_site_option( 'wp_2fa_update_redirection_needed' );
				update_site_option( 'wp_2fa_update_notice_needed', true );
				$args = array(
					'page' => 'wp-2fa-policies',
				);
				$url  = add_query_arg( $args, network_admin_url( 'admin.php' ) );
				wp_safe_redirect( $url );
				exit;
			}
		}

		/**
		 * Handle notice dismissal.
		 *
		 * @since 2.7.0
		 * @return void
		 */
		public static function dismiss_update_notice() {
			// Grab POSTed data.
			$nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : false;
			// Check nonce.
			if ( ! current_user_can( 'manage_options' ) || empty( $nonce ) || ! $nonce || ! wp_verify_nonce( $nonce, 'wp_2fa_dismiss_update_notice_nonce' ) ) {
				wp_send_json_error( esc_html__( 'Nonce Verification Failed.', 'wp-2fa' ) );
			}

			delete_site_option( 'wp_2fa_update_notice_needed' );

			wp_send_json_success( esc_html__( 'Complete.', 'wp-2fa' ) );
		}
	}
}
includes/classes/Admin/class-user-profile.php000064400000100045150755130600015271 0ustar00<?php
/**
 * Responsible for WP2FA user's profile settings.
 *
 * @package    wp2fa
 * @subpackage user-utils
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 * @link       https://wordpress.org/plugins/wp-2fa/
 */

namespace WP2FA\Admin;

use WP2FA\WP2FA;
use WP2FA\Methods\TOTP;
use WP2FA\Methods\Email;
use WP2FA\Utils\User_Utils;
use WP2FA\Extensions_Loader;
use WP2FA\Methods\Backup_Codes;
use WP2FA\Utils\Generate_Modal;
use WP2FA\Utils\Settings_Utils;
use WP2FA\Authenticator\Open_SSL;
use WP2FA\Admin\Helpers\WP_Helper;
use WP2FA\Freemius\User_Licensing;
use WP2FA\Admin\Views\Wizard_Steps;
use WP2FA\Admin\Controllers\Methods;
use WP2FA\Admin\Helpers\User_Helper;
use WP2FA\Admin\Controllers\Settings;
use WP2FA\Authenticator\Authentication;
use WP2FA\Extensions\OutOfBand\Out_Of_Band;

/**
 * User_Profile class responsible for the profile page operations
 *
 * @since 2.4.0
 */
if ( ! class_exists( '\WP2FA\Admin\User_Profile' ) ) {
	/**
	 * User_Profile - Class for handling user things such as profile settings and admin list views.
	 *
	 * @since 2.7.0
	 */
	class User_Profile {

		/**
		 * Add our buttons to the user profile editing screen.
		 *
		 * @param object $user User data.
		 * @param array  $additional_args - Array with extra parameters for the method.
		 *
		 * @since 2.7.0
		 */
		public static function user_2fa_options( $user, $additional_args = array() ) {

			if ( isset( $_GET['user_id'] ) ) { // phpcs:ignore
				$user_id = (int) $_GET['user_id']; // phpcs:ignore
				$user    = \get_user_by( 'id', $user_id );
			} else {
				// Get current user, we're going to need this regardless.
				$user = \wp_get_current_user();
			}

			if ( ! is_a( $user, '\WP_User' ) ) {
				return;
			}

			// Ensure we have something in the settings.
			if ( empty( Settings_Utils::get_option( WP_2FA_POLICY_SETTINGS_NAME ) ) ) {
				return;
			}

			$show_preamble = true;
			if ( isset( $additional_args['show_preamble'] ) ) {
				$show_preamble = \filter_var( $additional_args['show_preamble'], FILTER_VALIDATE_BOOLEAN );
			}

			$user_type = User_Utils::determine_user_2fa_status( $user );

			$form_output     = '';
			$form_content    = '';
			$description     = WP2FA::get_wp2fa_white_label_setting( 'user-profile-form-preamble-desc', true );
			$show_form_table = true;
			$page_url        = ( WP_Helper::is_multisite() ) ? 'index.php' : 'options-general.php';

			// Orphan user (a user with no role or capabilities).
			if ( in_array( 'orphan_user', $user_type, true ) ) {
				// We want to use the same form/buttons used in the shortcode.
				$additional_args['is_shortcode'] = true;

				// Create useful message for admin.
				if ( User_Utils::in_array_all( array( 'user_needs_to_setup_2fa', 'can_manage_options' ), $user_type ) ) {
					$description = \esc_html__( 'This user is required to setup 2FA but has not yet done so.', 'wp-2fa' );
				}

				if ( User_Utils::in_array_all( array( 'user_is_excluded', 'can_manage_options' ), $user_type ) ) {
					$description = \esc_html__( 'This user is excluded from configuring 2FA.', 'wp-2fa' );
				}
			}

			// Excluded user.
			if ( in_array( 'user_is_excluded', $user_type, true ) ) {
				return;
			}

			// A user viewing their own profile AND has a 2FA method configured.
			if ( User_Utils::in_array_all( array( 'viewing_own_profile' ), $user_type ) ) {
				if (
				User_Utils::in_array_all( array( 'has_enabled_methods' ), $user_type ) ||
				User_Utils::in_array_all( array( 'no_required_has_enabled' ), $user_type )
				) {

					if ( isset( $additional_args['is_shortcode'] ) && $additional_args['is_shortcode'] ) {
						$form_content = '';

						/**
						 * Gives the ability to remove the user's settings.
						 *
						 * @param bool - The status of the settings.
						 *
						 * @since 2.2.2
						 */
						$show_enable2fa = \apply_filters( WP_2FA_PREFIX . 'enable_2fa_user_setting', true );

						/**
						 * Gives the ability to change the user profile description message.
						 *
						 * @param bool - The status of the settings.
						 *
						 * @since 2.4.0
						 */
						$description = \apply_filters( WP_2FA_PREFIX . 'enable_2fa_user_setting_description', $description );

						$styling_class = ( empty( WP2FA::get_wp2fa_white_label_setting( 'enable_wizard_styling' ) ) ) ? 'default_styling' : 'enable_styling';

						if ( $show_enable2fa ) {
							$form_content = '<a href="#" class="button button-primary remove-2fa ' . \esc_attr( $styling_class ) . '" data-open-configure-2fa-wizard>' . \esc_html__( 'Change 2FA settings', 'wp-2fa' ) . '</a>';
						}

						if ( self::can_user_remove_2fa( $user->ID ) ) {
							$form_content .= '<a href="#" class="button button-primary remove-2fa ' . \esc_attr( $styling_class ) . '" onclick="MicroModal.show(\'confirm-remove-2fa\');">' . \esc_html__( 'Remove 2FA', 'wp-2fa' ) . '</a>';
						}

						$form_content     .= '</td><tr><th class="backup-methods-label">';
						$backup_codes_desc = '';
						if ( Backup_Codes::are_backup_codes_enabled_for_role( User_Helper::get_user_role( $user ) ) ) {
							$codes_remaining = Backup_Codes::codes_remaining_for_user( $user );
							if ( $codes_remaining > 0 ) {
								$backup_codes_desc = '<span class="description mt-5px">' . \esc_attr( (int) $codes_remaining ) . ' ' . \esc_html__( 'unused backup codes remaining.', 'wp-2fa' ) . '</span>';
							} elseif ( 0 === $codes_remaining ) {
								$backup_codes_desc = '<a class="learn_more_link" href="https://melapress.com/2fa-backup-codes/?utm_source=plugin&utm_medium=link&utm_campaign=wp2fa" target="_blank">' . \esc_html__( 'Learn more about backup codes', 'wp-2fa' ) . '</a>';
							}

							if ( ! empty( $backup_codes_desc ) ) {
								$backup_codes_desc = Wizard_Steps::get_backup_codes_link() . $backup_codes_desc;
							}
						}

						/**
						 * Add an option for external providers to add their own user form buttons.
						 *
						 * @since 2.0.0
						 */
						$backup_codes_desc = apply_filters( WP_2FA_PREFIX . 'additional_form_buttons', $backup_codes_desc );

						if ( ! empty( $backup_codes_desc ) ) {
							$form_content .= Wizard_Steps::get_generate_codes_label() . $backup_codes_desc;
						}

						$form_content .= '</th></tr>';
					}
				}

				$show_if_user_is_not_in = array(
					'user_is_excluded',
					'has_enabled_methods',
					'no_required_has_enabled',
				);

				// User viewing own profile and needs to enable 2FA.
				if (
				User_Utils::in_array_all( array( 'user_needs_to_setup_2fa' ), $user_type ) ||
				User_Utils::role_is_not( $show_if_user_is_not_in, $user_type )
				) {

					$first_time_setup_url = Settings::get_setup_page_link();

					/**
					 * Gives the ability to remove the user's settings.
					 *
					 * @param bool - The status of the settings.
					 *
					 * @since 2.2.2
					 */
					$show_enable2fa = \apply_filters( WP_2FA_PREFIX . 'enable_2fa_user_setting', true );


					/**
					 * Gives the ability to change the user profile description message.
					 *
					 * @param bool - The status of the settings.
					 *
					 * @since 2.4.0
					 */
					$description = \apply_filters( WP_2FA_PREFIX . 'enable_2fa_user_setting_description', $description );

					$styling_class = ( empty( WP2FA::get_wp2fa_white_label_setting( 'enable_wizard_styling' ) ) ) ? 'default_styling' : 'enable_styling';

					if ( $show_enable2fa ) {

						if ( isset( $additional_args['is_shortcode'] ) && $additional_args['is_shortcode'] ) {
							$form_content .= '<a href="#" class="button button-primary ' . \esc_attr( $styling_class ) . '" data-open-configure-2fa-wizard>' . \esc_html__( 'Configure 2FA', 'wp-2fa' ) . '</a>';
						}

						if ( empty( $additional_args ) ) {
							$form_content .= '<a href="' . \esc_url( $first_time_setup_url ) . '" class="button button-primary ' . \esc_attr( $styling_class ) . '">' . \esc_html__( 'Configure Two-factor authentication (2FA)', 'wp-2fa' ) . '</a>';
						}
					}
				}
			}

			// Admin viewing users profile AND user has a configured 2FA method.
			if ( User_Utils::in_array_all( array( 'can_manage_options', 'has_enabled_methods' ), $user_type ) && ! in_array( 'viewing_own_profile', $user_type, true ) ) {
				$description = \esc_html__( 'The user has already configured 2FA. When you reset the user\'s current 2FA configuration, the user can log back in with just the username and password.', 'wp-2fa' );

				$remove_users_2fa_url = add_query_arg(
					array(
						'action'       => 'remove_user_2fa',
						'user_id'      => $user->ID,
						'wp_2fa_nonce' => wp_create_nonce( 'wp-2fa-remove-user-2fa-nonce' ),
						'admin_reset'  => 'yes',
					),
					admin_url( 'user-edit.php' )
				);

				$form_content .= '<a href="' . \esc_url( $remove_users_2fa_url ) . '" class="button button-primary">' . \esc_html__( 'Reset 2FA configuration', 'wp-2fa' ) . '</a>';
			}

			// Admin viewing users profile AND users grace period has expired.
			if ( User_Utils::in_array_all( array( 'can_manage_options', 'grace_has_expired' ), $user_type ) ) {
				$unlock_user_url = add_query_arg(
					array(
						'action'       => 'unlock_account',
						'user_id'      => $user->ID,
						'wp_2fa_nonce' => wp_create_nonce( 'wp-2fa-unlock-account-nonce' ),
					),
					admin_url( 'user-edit.php' )
				);
				$form_content   .= '<a href="' . \esc_url( $unlock_user_url ) . '" class="button button-primary">' . \esc_html__( 'Unlock user and reset the grace period', 'wp-2fa' ) . '</a>';
			}

			if ( $show_preamble ) {
				$form_output .= '<h2>' . WP2FA::get_wp2fa_white_label_setting( 'user-profile-form-preamble-title', true ) . '</h2>';

				if ( $description ) {
					$form_output .= '<p class="description">' . $description . '</p>';
				}
			}
			/**
			 * Gives the ability to add more content to the profile page.
			 *
			 * @param string $form_content - The parsed HTML of the form.
			 */
			$form_content = apply_filters( WP_2FA_PREFIX . 'append_to_profile_form_content', $form_content );

			if ( $show_form_table && ! empty( $form_content ) ) {

				$enabled_methods        = User_Helper::get_enabled_method_for_user( $user );
				$primary_label          = ( isset( $enabled_methods ) && ! empty( $enabled_methods ) ) ? Settings::get_providers_translate_names()[ $enabled_methods ] : \esc_html__( 'No enabled primary method', 'wp-2fa' );
				$enabled_backup_methods = User_Helper::get_enabled_backup_methods_for_user( $user );
				$backup_methods_enabled = \esc_html__( 'No enabled backup methods', 'wp-2fa' );

				if ( isset( $enabled_backup_methods ) && ! empty( $enabled_backup_methods ) ) {
					$backup_methods_enabled = \implode( ', ', $enabled_backup_methods );
				}

				$show_enabled = true;

				if ( isset( $additional_args ) && ! empty( $additional_args ) && isset( $additional_args['options'] ) && ! empty( $additional_args['options'] ) ) {
					if ( isset( $additional_args['options']['do_not_show_enabled'] ) && 'false' !== $additional_args['options']['do_not_show_enabled'] ) {
						$show_enabled = false;
					}
				}

				if ( $show_enabled ) {

					$form_output .= '<h3>' . \esc_html__( 'Currently configured:', 'wp-2fa' ) . '</h3>';

					$form_output .= '
					<table id="2fa-currently-configured-methods" class="form-table wp-2fa-user-profile-form" role="presentation">
						<tbody>
							<tr>
								<th><label>' . \esc_html__( 'Primary method:', 'wp-2fa' ) . '</label></th>
								<td>
								' . $primary_label . '
								</td>
							</tr>';

							$form_output .= '
							<tr>
								<th><label>' . \esc_html__( 'Secondary method(s):', 'wp-2fa' ) . '</label></th>
								<td>
								' . $backup_methods_enabled . '
								</td>
							</tr>';

							$form_output .= '
						</tbody>
					</table>';
				}

				$form_output .= '<h3>' . \esc_html__( '2FA configuration:', 'wp-2fa' ) . '</h3>';

				if ( User_Utils::in_array_all( array( 'has_enabled_methods', 'viewing_own_profile' ), $user_type ) && isset( $enabled_methods ) && TOTP::METHOD_NAME === $enabled_methods ) {
					$form_output .= '
					<table id="2fa-configuration-options" class="form-table wp-2fa-user-profile-form remove-tr-padding" role="presentation">
						<tbody>
							<tr>
								<th><label>' . Settings::get_providers_translate_names()[ $enabled_methods ] . '</label></th>
								<td>
								<details>
									<summary class="qr-btn">' . \esc_html__( 'Show QR code', 'wp-2fa' ) . '</summary>
									<p><img class="qr-code" src="' . ( TOTP::get_qr_code() ) . '" /></p>
									<div class="app-key-wrapper">
										<input type="text" id="app-key-input" readonly value="' . \esc_html( TOTP::get_totp_decrypted() ) . '" class="app-key">
										' .
										( ( is_ssl() ) ?
											'<span class="click-to-copy">' . \esc_html__( 'COPY', 'wp-2fa' ) . '</span>' : '' ) . '
									</div>	
								</details>
								</td>
							</tr>
						</tbody>
					</table>';
				}

				$form_output .= '
					<table id="2fa-user-global-configuration" class="form-table wp-2fa-user-profile-form" role="presentation">
						<tbody>
							<tr>
								<th><label>' . \esc_html__( '2FA Setup:', 'wp-2fa' ) . '</label></th>
								<td>
								' . $form_content . '
								</td>
							</tr>
						</tbody>
					</table>';

				if ( ( isset( $_GET['show'] ) && 'wp-2fa-setup' === $_GET['show'] ) || User_Helper::get_user_enforced_instantly( $user ) ) { // phpcs:ignore
					$form_output .= '
					<script>
					window.addEventListener("load", function() {
						wp2fa_fireWizard();
					});
					</script>
				';
				}
			}

			echo $form_output; // phpcs:ignore

			self::generate_inline_modals( $user_type );
		}

		/**
		 * Responsible for the building of all the modals.
		 *
		 * @param array $user_type - The WP user type.
		 *
		 * @return void
		 *
		 * @since 2.7.0
		 */
		public static function generate_inline_modals( $user_type = array() ) {

			ob_start();

			$user = \wp_get_current_user();

			$styling_class = ( empty( WP2FA::get_wp2fa_white_label_setting( 'enable_wizard_styling' ) ) ) ? 'default_styling' : 'enable_styling';

			if ( User_Utils::in_array_all( array( 'user_needs_to_setup_2fa', 'viewing_own_profile' ), $user_type ) || User_Utils::in_array_all( array( 'has_enabled_methods', 'viewing_own_profile' ), $user_type ) || User_Utils::in_array_all( array( 'no_required_not_enabled', 'viewing_own_profile' ), $user_type ) || User_Utils::in_array_all( array( User_Helper::USER_UNDETERMINED_STATUS, 'viewing_own_profile' ), $user_type ) ) { ?>
			<div>
				<div class="wp2fa-modal micromodal-slide <?php echo \esc_attr( $styling_class ); ?>" id="configure-2fa" aria-hidden="true">
					<div class="modal__overlay" tabindex="-1">
						<div class="modal__container" role="dialog" aria-modal="true" aria-labelledby="modal-1-title">
								<?php
								echo Generate_Modal::generate_modal( // phpcs:ignore
									'notify-users',
									__( 'Are you sure?', 'wp-2fa' ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
									__( 'Any unsaved changes will be lost!', 'wp-2fa' ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
									array(
										'<button class="button wp-2fa-button-primary button-primary button-confirm" aria-label="Close this dialog window and the wizard">' . \esc_html__( 'Yes', 'wp-2fa' ) . '</button>',
										'<button class="button wp-2fa-button-secondary button-secondary button-decline" data-micromodal-close aria-label="Close this dialog window">' . \esc_html__( 'No', 'wp-2fa' ) . '</button>',
									),
									'',
									'430px'
								);
								?>
							<button class="modal__close modal_cancel" aria-label="Close modal"></button>
							<main class="modal__content wp2fa-form-styles" id="modal-1-content">
								<?php
								$logo_url     = WP2FA::get_wp2fa_white_label_setting( 'logo-code-page', false );
								$logo_section = ( $logo_url ) ? '<p class="modal-logo-wrapper"><img style="max-height: 60px;margin: 0 auto 30px;" src="' . \esc_url( $logo_url ) . '" /></p>' : '';
								$enable_logo  = WP2FA::get_wp2fa_white_label_setting( 'enable_wizard_logo', false );

								if ( $enable_logo ) {
									echo $logo_section; // phpcs:ignore */
								}

								if ( User_Utils::in_array_all( array( 'user_needs_to_setup_2fa', 'viewing_own_profile' ), $user_type ) || User_Utils::in_array_all( array( 'no_required_not_enabled', 'viewing_own_profile' ), $user_type ) || User_Utils::in_array_all( array( User_Helper::USER_UNDETERMINED_STATUS, 'viewing_own_profile' ), $user_type ) ) {

									$available_methods = Methods::get_enabled_methods( User_Helper::get_user_role( $user ) );
									$optional_welcome  = WP2FA::get_wp2fa_white_label_setting( 'welcome', false );
									$enable_welcome    = WP2FA::get_wp2fa_white_label_setting( 'enable_welcome', false );

									$intro_text = '';
									if ( count( $available_methods[ User_Helper::get_user_role( $user ) ] ) > 1 ) {
										$intro_text = WP2FA::replace_wizard_strings( WP2FA::get_wp2fa_white_label_setting( 'method_selection', true ), $user );
									} elseif ( 1 === count( $available_methods[ User_Helper::get_user_role( $user ) ] ) ) {
										$intro_text = WP2FA::get_wp2fa_white_label_setting( 'method_selection_single', true );
									} else {
										$intro_text = '<h3>' . __( 'No available 2FA methods set', 'wp-2fa' ) . '</h3><p>' . __( 'Ask your administrator to enable 2FA methods', 'wp-2fa' ) . '</p>';
									}

									if ( ! empty( $optional_welcome ) && $enable_welcome ) {
										Wizard_Steps::optional_user_welcome_step();
									}
									?>

									<div class="wizard-step <?php echo ( empty( $optional_welcome ) ) ? 'active' : ''; ?>" id="choose-2fa-method">
										<div class="mb-20"><?php echo \wp_kses_post( $intro_text ); ?></div>
										<fieldset class="radio-cells">
										<?php
											/**
											 * Adds an option for external providers to add their own 2fa methods options. And sorts them (our logic).
											 *
											 * @since 2.0.0
											 */
											\do_action( WP_2FA_PREFIX . 'methods_options' );
										?>
										</fieldset>
										<br>
										<?php
										if ( 0 !== count( $available_methods[ User_Helper::get_user_role( $user ) ] ) ) {
											?>
										<a href="#" class="button wp-2fa-button-primary button-primary 2fa-choose-method" data-name="next_step_setting_modal_wizard" data-next-step><?php \esc_html_e( 'Next Step', 'wp-2fa' ); ?></a>
											<?php
										}
										?>
										<button class="button wp-2fa-button-secondary button-secondary" data-close-2fa-modal aria-label="Close this dialog window"><?php \esc_html_e( 'Cancel', 'wp-2fa' ); ?></button>
									</div>
								<?php } ?>

								<?php if ( User_Utils::in_array_all( array( 'has_enabled_methods', 'viewing_own_profile' ), $user_type ) ) { ?>
									<div class="wizard-step active">
										<fieldset class="radio-cells max-3">
											<?php
												/**
												 * Add an option for external providers to add their own reconfigure methods options.
												 *
												 * @since 2.0.0
												 */
												\do_action( WP_2FA_PREFIX . 'methods_reconfigure_options' );
											?>
										</fieldset>
									</div>
								<?php } ?>

									<?php Wizard_Steps::show_modal_methods(); ?>
								<?php

								$backup_methods = Settings::get_enabled_backup_methods_for_user_role( $user );

								if ( count( $backup_methods ) > 1 ) {
									Wizard_Steps::choose_backup_method();
								}

								/**
								 * Add an option for external providers to add their own wizard steps.
								 *
								 * @since 2.0.0
								 */
								\do_action( WP_2FA_PREFIX . 'additional_settings_steps' );

								// Create a nonce for use in ajax call to generate codes.
								if ( Backup_Codes::are_backup_codes_enabled_for_role( User_Helper::get_user_role( $user ) ) ) {
									?>
								<div class="wizard-step" id="2fa-wizard-config-backup-codes">
									<?php Wizard_Steps::backup_codes_configure(); ?>
									<?php Wizard_Steps::generated_backup_codes(); ?>
								</div>
								<?php } else { ?>
								<div class="wizard-step" id="2fa-wizard-config-backup-codes">
									<?php Wizard_Steps::congratulations_step(); ?>
								</div>
							<?php } ?>
							</main>
						</div>
					</div>
				</div>
			</div>
			<?php } ?>

			<?php
			/**
			 * Add an option for external providers to add their own 2fa methods options.
			 *
			 * @since 2.0.0
			 */
			\do_action( WP_2FA_PREFIX . 'methods_wizards' );
			?>

			<?php if ( Backup_Codes::are_backup_codes_enabled_for_role( User_Helper::get_user_role( $user ) ) ) { ?>
			<div>
				<div class="wp2fa-modal micromodal-slide <?php echo \esc_attr( $styling_class ); ?>" id="configure-2fa-backup-codes" aria-hidden="true">
					<div class="modal__overlay" tabindex="-1">
						<div class="modal__container" role="dialog" aria-modal="true" aria-labelledby="modal-1-title">
						<button class="modal__close modal_cancel" aria-label="Close modal" data-close-2fa-modal></button>
						<main class="modal__content wp2fa-form-styles" id="modal-1-content">
							<?php Wizard_Steps::generated_backup_codes( true ); ?>
						</main>
						</div>
					</div>
				</div>
			</div>
			<?php } ?>
				<div>
					<?php

					if ( self::can_user_remove_2fa( $user->ID ) ) :
						echo Generate_Modal::generate_modal( // phpcs:ignore
							'confirm-remove-2fa',
							__( 'Remove 2FA?', 'wp-2fa' ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
							__( 'Are you sure you want to remove two-factor authentication and lower the security of your user account?', 'wp-2fa' ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
							array(
								'<a href="#" class="button wp-2fa-button-primary" data-trigger-remove-2fa data-user-id="' . \esc_attr( $user->ID ) . '" ' . WP_Helper::create_data_nonce( 'wp-2fa-remove-user-2fa-nonce' ) . '>' . \esc_html__( 'Yes', 'wp-2fa' ) . '</a>', // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
								'<button class="modal__btn  wp-2fa-button-secondary button" data-close-2fa-modal aria-label="Close this dialog window">' . \esc_html__( 'No', 'wp-2fa' ) . '</button>',
							)
						);
					endif;
					?>
				</div>
				<?php

				$output = ob_get_contents();
				ob_end_clean();

				echo $output; // phpcs:ignore
		}

		/**
		 * Produces the 2FA configuration form for network users, or any user with no roles.
		 *
		 * @param string  $is_shortcode - Current logic expects that to be set always.
		 * @param boolean $show_preamble - Show / hide preamble.
		 * @param array   $options - Array with additional options.
		 *
		 * @return void
		 *
		 * @since 2.7.0
		 */
		public static function inline_2fa_profile_form( $is_shortcode = 'true', $show_preamble = true, array $options = array() ) {

			if ( isset( $_GET['user_id'] ) ) { // phpcs:ignore
				$user_id = (int) $_GET['user_id']; // phpcs:ignore
				$user    = \get_user_by( 'id', $user_id );
			} else {
				$user = \wp_get_current_user();
			}

			// Get current user, we going to need this regardless.
			$current_user = \wp_get_current_user();

			if ( \is_multisite() ) {
				if ( '' === trim( (string) \WP2FA\Admin\Helpers\User_Helper::get_user_role( $user ) ) ) {
					return;
				}
			}

			// Bail if we still dont have an object.
			if ( ! is_a( $user, '\WP_User' ) || ! is_a( $current_user, '\WP_User' ) ) {
				return;
			}

			$additional_args = array(
				'is_shortcode'  => $is_shortcode,
				'show_preamble' => $show_preamble,
				'options'       => $options,
			);

			self::user_2fa_options( $user, $additional_args );
		}

		/**
		 * Add custom unlock account link to user edit admin list.
		 *
		 * @param  string $actions     Default actions.
		 * @param  object $user_object User data.
		 * @return string              Appended actions.
		 *
		 * @since 2.7.0
		 */
		public static function user_2fa_row_actions( $actions, $user_object ) {
			$nonce                = wp_create_nonce( 'wp-2fa-unlock-account-nonce' );
			$grace_period_expired = User_Helper::get_grace_period( $user_object );
			$url                  = add_query_arg(
				array(
					'action'       => 'unlock_account',
					'user_id'      => $user_object->ID,
					'wp_2fa_nonce' => $nonce,
				),
				admin_url( 'users.php' )
			);

			if ( $grace_period_expired ) {
				$actions['edit_badges'] = '<a href="' . \esc_url( $url ) . '">' . \esc_html__( 'Unlock user', 'wp-2fa' ) . '</a>';
			}

			return $actions;
		}

		/**
		 * Save user profile information.
		 *
		 * @param array $input - The array with values to process.
		 *
		 * @return void
		 *
		 * @since 2.7.0
		 */
		public static function save_user_2fa_options( $input ) {

			// Ensure we have the inputs we want before we process.
			// To avoid causing issues with the rest of the user profile.
			if ( ! is_array( $input ) ) {
				return;
			}

			// Assign the input to post, in case we are dealing with saving the data from another page.
			if ( isset( $input ) ) {
				$_POST = $input;
			}

			// Grab current user.
			$user = wp_get_current_user();

			// phpcs:disable
			// Grab authcode and ensure its a number.
			if ( isset( $_POST['wp-2fa-totp-authcode'] ) ) {
				$_POST['wp-2fa-totp-authcode'] = (int) $_POST['wp-2fa-totp-authcode'];
			}
			if ( ( ! isset( $_POST['custom-email-address'] ) || isset( $_POST['custom-email-address'] ) && empty( $_POST['custom-email-address'] ) ) &&
			( ! isset( $_POST['custom-oob-email-address'] ) || isset( $_POST['custom-oob-email-address'] ) && empty( $_POST['custom-oob-email-address'] ) ) ) {
				if ( isset( $_POST['email'] ) ) {
					User_Helper::set_nominated_email_for_user( $_POST['email'], $user );
				} elseif ( isset( $_POST['wp_2fa_email_address'] ) && isset( $_POST['wp-2fa-totp-authcode'] ) && ! empty( $_POST['wp-2fa-totp-authcode'] ) ) {
					User_Helper::set_nominated_email_for_user( $_POST['wp_2fa_email_address'], $user );
				} elseif ( isset( $_POST['wp_2fa_email_oob_address'] ) && isset( $_POST['wp-2fa-oob-authcode'] ) && ! empty( $_POST['wp-2fa-oob-authcode'] ) ) {
					if ( 'use_custom_email' !== $_POST['wp_2fa_email_oob_address'] ) {
						User_Helper::set_nominated_email_for_user( $_POST['wp_2fa_email_oob_address'], $user );
					}
				}
			} elseif ( isset( $_POST['custom-email-address'] ) && ! empty( $_POST['custom-email-address'] ) ) {
				User_Helper::set_nominated_email_for_user( $_POST['custom-email-address'], $user );
			} elseif ( isset( $_POST['custom-oob-email-address'] ) && ! empty( $_POST['custom-oob-email-address'] ) ) {
				User_Helper::set_nominated_email_for_user( $_POST['custom-oob-email-address'], $user );
			}

			// Check its one of our options.
			if ( ( isset( $_POST['wp_2fa_enabled_methods'] ) && TOTP::METHOD_NAME === $_POST['wp_2fa_enabled_methods'] ) ||
			( isset( $_POST['wp_2fa_enabled_methods'] ) && Email::METHOD_NAME === $_POST['wp_2fa_enabled_methods'] ) ||
			( isset( $_POST['wp_2fa_enabled_methods'] ) && ( class_exists( '\WP2FA\Extensions\OutOfBand\Out_Of_Band', false ) && Out_Of_Band::METHOD_NAME === $_POST['wp_2fa_enabled_methods'] ) ) ) {
				User_Helper::set_enabled_method_for_user(sanitize_text_field( wp_unslash( $_POST['wp_2fa_enabled_methods'] ) ), $user);
				self::delete_expire_and_enforced_keys( $user->ID );
				User_Helper::set_user_status( $user );
			}

			if ( isset( $_POST['wp-2fa-email-authcode'] ) && ! empty( $_POST['wp-2fa-email-authcode'] ) ) {
				User_Helper::set_enabled_method_for_user( Email::METHOD_NAME, $user );
				self::delete_expire_and_enforced_keys( $user->ID );
				User_Helper::set_user_status( $user );
			}

			if ( isset( $_POST['wp-2fa-totp-authcode'] ) && ! empty( $_POST['wp-2fa-totp-authcode'] ) ) {
				$totp_key = $_POST['wp-2fa-totp-key'];
				if ( Authentication::is_valid_key( $totp_key ) ) {
					if ( Open_SSL::is_ssl_available() ) {
						$totp_key = Open_SSL::SECRET_KEY_PREFIX . Open_SSL::encrypt( $totp_key );
					}

					TOTP::set_user_method( $user, $totp_key );
				}
			}
			// phpcs:enable
		}

		/**
		 * Utility function to remove user expiry and enforced data.
		 *
		 * @param  int $user_id User id to process.
		 *
		 * @since 2.7.0
		 */
		public static function delete_expire_and_enforced_keys( $user_id ) {
			User_Helper::remove_user_expiry_date( $user_id );
			User_Helper::remove_user_enforced_instantly( $user_id );
			User_Helper::remove_grace_period( $user_id );
		}

		/**
		 * Validate a user's code when setting up 2fa via the inline form.
		 *
		 * @return void
		 *
		 * @since 2.7.0
		 */
		public static function validate_authcode_via_ajax() {
			check_ajax_referer( 'wp-2fa-validate-authcode' );

			if ( isset( $_POST['form'] ) ) {
				$input = wp_unslash( $_POST['form'] ); // phpcs:ignore
			} else {
				wp_send_json_error(
					array(
						'error' => \esc_html__( 'No form', 'wp-2fa' ),
					)
				);
			}

			$user = wp_get_current_user();

			$our_errors = '';

			// Grab key from the $_POST.
			if ( isset( $input['wp-2fa-totp-key'] ) ) {
				$current_key = sanitize_text_field( wp_unslash( $input['wp-2fa-totp-key'] ) );
			}

			// Grab authcode and ensure its a number.
			if ( isset( $input['wp-2fa-totp-authcode'] ) ) {
				$input['wp-2fa-totp-authcode'] = (int) $input['wp-2fa-totp-authcode'];
			}

			// Check if we are dealing with totp or email, if totp validate and store a new secret key.
			if ( ! empty( $input['wp-2fa-totp-authcode'] ) && ! empty( $current_key ) ) {
				if ( Authentication::is_valid_key( $current_key ) || ! is_numeric( $input['wp-2fa-totp-authcode'] ) ) {
					if ( ! Authentication::is_valid_authcode( $current_key, sanitize_text_field( wp_unslash( $input['wp-2fa-totp-authcode'] ) ) ) ) {
						$our_errors = \esc_html__( 'Invalid Two Factor Authentication code.', 'wp-2fa' );
					}
				} else {
					$our_errors = \esc_html__( 'Invalid Two Factor Authentication secret key.', 'wp-2fa' );
				}

				// If its not totp, is it email.
			} elseif ( ! empty( $input['wp-2fa-email-authcode'] ) ) {
				if ( ! Authentication::validate_token( $user, sanitize_text_field( wp_unslash( $input['wp-2fa-email-authcode'] ) ) ) ) {
					$our_errors = \esc_html__( 'Invalid Email Authentication code.', 'wp-2fa' );
				}
			} else {
				$our_errors = \esc_html__( 'Please enter the code to finalize the 2FA setup.', 'wp-2fa' );
			}

			if ( ! empty( $our_errors ) ) {
				// Send the response.
				wp_send_json_error(
					array(
						'error' => $our_errors,
					)
				);
			} else {
				self::save_user_2fa_options( $input );
				// Send the response.
				wp_send_json_success();
			}

			wp_send_json_error(
				array(
					'error' => \esc_html__( 'Error processing form', 'wp-2fa' ),
				)
			);
		}

		/**
		 * Checks the user for remove 2FA capabilities.
		 *
		 * @param int $user_id User ID.
		 *
		 * @return bool True if the user can remove 2FA from their account.
		 *
		 * @since 2.7.0
		 */
		public static function can_user_remove_2fa( $user_id ) {
			// check the "Hide the Remove 2FA button" setting.
			if ( Settings::get_role_or_default_setting( 'hide_remove_button', $user_id ) ) {
				return false;
			}

			// check grace period policy.
			$grace_policy = Settings::get_role_or_default_setting( 'grace-policy', $user_id );
			if ( 'no-grace-period' === $grace_policy ) {
				// we only need to run further checks to find out if the 2FA is enforced for the user in question if there
				// is no grace period.
				$enforcement_policy = WP2FA::get_wp2fa_setting( 'enforcement-policy' );

				if ( 'all-users' === $enforcement_policy ) {
					// enforced for all users, target user is definitely included.
					return false;
				}

				if ( 'certain-roles-only' === $enforcement_policy && ! User_Helper::is_enforced( $user_id ) ) {
					// Users specific role is not enforced, allow removal.
					return true;
				}

				if ( 'do-not-enforce' !== $enforcement_policy ) {
					// one of possible enforcement options is set, check the target user.
					return User_Helper::is_enforced( $user_id );
				}
			}

			return true;
		}

		/**
		 * Add script to admin footer to allow for nags to be dismissed from all admin pages.
		 *
		 * @return void
		 *
		 * @since 2.7.0
		 */
		public static function dismiss_nag_notice() {
			?>
			<script type="text/javascript">
				jQuery( document ).on( 'click', '.dismiss-user-configure-nag', function() {
					const thisNotice = jQuery( this ).closest( '.notice' );
					jQuery.ajax( {
						url: '<?php echo admin_url( 'admin-ajax.php' ); // phpcs:ignore ?>',
						data: {
							action: 'dismiss_nag'
						},
						complete: function() {
							jQuery( thisNotice ).slideUp();
						},
					} );
				} );
			</script>
			<?php
		}
	}
}
includes/classes/Admin/class-user-registered.php000064400000002421150755130600015765 0ustar00<?php
/**
 * Responsible for WP2FA user's grace periods.
 *
 * @package    wp2fa
 * @subpackage user-utils
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 * @link       https://wordpress.org/plugins/wp-2fa/
 */

namespace WP2FA\Admin;

use WP2FA\Admin\Helpers\User_Helper;

if ( ! class_exists( '\WP2FA\Admin\User_Registered' ) ) {
	/**
	 * User_Profile - Class for handling user things such as profile settings and admin list views.
	 */
	class User_Registered {

		/**
		 * Apply 2FA Grace period
		 *
		 * @param  int $user_id User id.
		 *
		 * @return void
		 */
		public static function apply_2fa_grace_period( $user_id ) {
			if ( User_Helper::is_user_method_in_role_enabled_methods( $user_id ) ) {
				return;
			} else {
				User_Helper::remove_enabled_method_for_user( $user_id );
				User_Helper::remove_global_settings_hash_for_user( $user_id );
			}
		}

		/**
		 * Checks the user on role change.
		 *
		 * @param integer $user_id - The ID of the user.
		 * @param string  $role - The user role.
		 * @param array   $old_roles - Old roles for the user.
		 *
		 * @return void
		 */
		public static function check_user_upon_role_change( $user_id, $role, $old_roles ) {
			self::apply_2fa_grace_period( $user_id );
		}
	}
}
includes/classes/Admin/class-settings-page.php000064400000042174150755130600015437 0ustar00<?php
/**
 * Settings rendering class.
 *
 * @package    wp2fa
 * @subpackage settings
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 * @link       https://wordpress.org/plugins/wp-2fa/
 */

namespace WP2FA\Admin;

use WP2FA\WP2FA;
use WP2FA\Admin\SettingsPages\{
	Settings_Page_Policies,
	Settings_Page_General,
	Settings_Page_Email
};
use WP2FA\Admin\Controllers\Settings;
use WP2FA\Utils\Settings_Utils;

/**
 * Class for handling settings
 */
if ( ! class_exists( '\WP2FA\Admin\Settings_Page' ) ) {
	/**
	 * Class for handling settings
	 */
	class Settings_Page {

		const TOP_MENU_SLUG = 'wp-2fa-policies';

		/**
		 * Create admin menu entry and settings page
		 */
		public static function create_settings_admin_menu() {
			// Create admin menu item.
			\add_menu_page(
				\esc_html__( 'WP 2FA', 'wp-2fa' ),
				\esc_html__( 'WP 2FA', 'wp-2fa' ),
				'manage_options',
				self::TOP_MENU_SLUG,
				null,
			'data:image/svg+xml;base64,' . base64_encode( file_get_contents( WP_2FA_PATH . 'dist/images/wp-2fa-white-icon20x28.svg' ) ), // phpcs:ignore
				81
			);

			\add_submenu_page(
				self::TOP_MENU_SLUG,
				\esc_html__( '2FA Policies', 'wp-2fa' ),
				\esc_html__( '2FA Policies', 'wp-2fa' ),
				'manage_options',
				self::TOP_MENU_SLUG,
				array( \WP2FA\Admin\SettingsPages\Settings_Page_Policies::class, 'render' ),
				1
			);

			\add_submenu_page(
				self::TOP_MENU_SLUG,
				\esc_html__( 'WP 2FA Settings', 'wp-2fa' ),
				\esc_html__( 'Settings', 'wp-2fa' ),
				'manage_options',
				'wp-2fa-settings',
				array( \WP2FA\Admin\SettingsPages\Settings_Page_Render::class, 'render' ),
				2
			);

			// Register our policy settings.
			\register_setting(
				WP_2FA_POLICY_SETTINGS_NAME,
				WP_2FA_POLICY_SETTINGS_NAME,
				array( \WP2FA\Admin\SettingsPages\Settings_Page_Policies::class, 'validate_and_sanitize' )
			);

			// Register our white label settings.
			\register_setting(
				WP_2FA_WHITE_LABEL_SETTINGS_NAME,
				WP_2FA_WHITE_LABEL_SETTINGS_NAME,
				array( \WP2FA\Admin\SettingsPages\Settings_Page_White_Label::class, 'validate_and_sanitize' )
			);

			// Register our settings page.
			\register_setting(
				WP_2FA_SETTINGS_NAME,
				WP_2FA_SETTINGS_NAME,
				array( \WP2FA\Admin\SettingsPages\Settings_Page_General::class, 'validate_and_sanitize' )
			);

			\register_setting(
				WP_2FA_EMAIL_SETTINGS_NAME,
				WP_2FA_EMAIL_SETTINGS_NAME,
				array( \WP2FA\Admin\SettingsPages\Settings_Page_Email::class, 'validate_and_sanitize' )
			);

			/**
			 * Fires after the main menu settings are registered.
			 *
			 * @param string - The menu slug.
			 * @param bool - Is that multisite install or not.
			 *
			 * @since 2.0.0
			 */
			\do_action( WP_2FA_PREFIX . 'after_admin_menu_created', self::TOP_MENU_SLUG, false );

			\add_action( WP_2FA_PREFIX . 'before_plugin_settings', array( __CLASS__, 'check_email' ) );
		}

		/**
		 * Create admin menu entry and settings page
		 */
		public static function create_settings_admin_menu_multisite() {
			// Create admin menu item.
			\add_menu_page(
				\esc_html__( 'WP 2FA Settings', 'wp-2fa' ),
				\esc_html__( 'WP 2FA', 'wp-2fa' ),
				'manage_options',
				self::TOP_MENU_SLUG,
				null,
			'data:image/svg+xml;base64,' . base64_encode( file_get_contents( WP_2FA_PATH . 'dist/images/wp-2fa-white-icon20x28.svg' ) ), // phpcs:ignore
				81
			);

			\add_submenu_page(
				self::TOP_MENU_SLUG,
				\esc_html__( '2FA Policies', 'wp-2fa' ),
				\esc_html__( '2FA Policies', 'wp-2fa' ),
				'manage_options',
				self::TOP_MENU_SLUG,
				array( \WP2FA\Admin\SettingsPages\Settings_Page_Policies::class, 'render' ),
				1
			);

			\add_submenu_page(
				self::TOP_MENU_SLUG,
				\esc_html__( 'WP 2FA Settings', 'wp-2fa' ),
				\esc_html__( 'Settings', 'wp-2fa' ),
				'manage_options',
				'wp-2fa-settings',
				array( \WP2FA\Admin\SettingsPages\Settings_Page_Render::class, 'render' ),
				2
			);

			/**
			 * Fires after the main menu settings are registered.
			 *
			 * @param string - The menu slug.
			 * @param bool - Is that multisite install or not.
			 *
			 * @since 2.0.0
			 */
			\do_action( WP_2FA_PREFIX . 'after_admin_menu_created', self::TOP_MENU_SLUG, true );
		}
		/**
		 * Send account unlocked notification via email.
		 *
		 * @param int $user_id user ID.
		 *
		 * @return boolean
		 */
		public static function send_account_unlocked_email( $user_id ) {
			// Bail if the user has not enabled this email.
			if ( 'enable_account_unlocked_email' !== WP2FA::get_wp2fa_email_templates( 'send_account_unlocked_email' ) ) {
				return false;
			}

			// Grab user data.
			$user = get_userdata( $user_id );
			// Grab user email.
			$email = $user->user_email;
			// Setup the email contents.
			$subject = wp_strip_all_tags( WP2FA::replace_email_strings( WP2FA::get_wp2fa_email_templates( 'user_account_unlocked_email_subject' ) ) );
			$message = wpautop( WP2FA::replace_email_strings( WP2FA::get_wp2fa_email_templates( 'user_account_unlocked_email_body' ), $user_id ) );

			return self::send_email( $email, $subject, $message );
		}

		/**
		 * Hide settings menu item
		 */
		public static function hide_settings() {
			$user = wp_get_current_user();

			// Check we have a user before doing anything else.
			if ( is_a( $user, '\WP_User' ) ) {
				if ( ! empty( WP2FA::get_wp2fa_setting( '2fa_settings_last_updated_by' ) ) ) {
					$main_user = (int) WP2FA::get_wp2fa_setting( '2fa_settings_last_updated_by' );
				} else {
					$main_user = get_current_user_id();
				}
				if ( ! empty( WP2FA::get_wp2fa_general_setting( 'limit_access' ) ) && $user->ID !== $main_user ) {
					// Remove admin menu item.
					remove_submenu_page( 'options-general.php', self::TOP_MENU_SLUG );
				}
			}
		}

		/**
		 * Add unlock user link to user actions.
		 *
		 * @param array $links Default row content.
		 *
		 * @return array
		 * @throws \Freemius_Exception - freemius exception.
		 */
		public static function add_plugin_action_links( $links ) {
			// add link to the external free trial page in free version and also in premium version if license is not active.
			if ( ! function_exists( 'wp2fa_freemius' ) || ! wp2fa_freemius()->has_active_valid_license() ) {
				$trial_link = 'https://melapress.com/wordpress-2fa/pricing/?utm_source=plugin&utm_medium=link&utm_campaign=wp2fa';
				$links      = array_merge(
					array(
						'<a style="font-weight:bold" href="' . $trial_link . '" target="_blank">' . __( 'Upgrade to Premium', 'wp-2fa' ) . '</a>',
					),
					$links
				);
			}

			// add link to the plugin settings page.
			$url   = Settings::get_settings_page_link();
			$links = array_merge(
				array(
					'<a href="' . \esc_url( $url ) . '">' . \esc_html__( 'Configure 2FA Settings', 'wp-2fa' ) . '</a>',
				),
				$links
			);

			return $links;
		}

		/**
		 * Updates options for multisite
		 *
		 * @return void
		 *
		 * @since 2.0.0
		 */
		public static function update_wp2fa_network_options() {

			Settings_Page_Policies::update_wp2fa_network_options();

			Settings_Page_General::update_wp2fa_network_options();

			\WP2FA\Admin\SettingsPages\Settings_Page_White_Label::update_wp2fa_network_options();

			/**
			 * Gives the ability for extensions to set their settings in the plugin.
			 *
			 * @since 2.2.0
			 */
			do_action( WP_2FA_PREFIX . 'update_network_settings' );
		}

		/**
		 * Handle saving email options to the network main site options.
		 */
		public static function update_wp2fa_network_email_options() {
			Settings_Page_Email::update_wp2fa_network_options();
		}

		/**
		 * These are used instead of add_settings_error which in a network site. Used to show if settings have been updated or failed.
		 */
		public static function settings_saved_network_admin_notice() {
			if ( isset( $_GET['wp_2fa_network_settings_updated'] ) && 'true' === $_GET['wp_2fa_network_settings_updated'] ) {
				?>
			<div class="notice notice-success is-dismissible">
				<p><?php \esc_html_e( '2FA Settings Updated', 'wp-2fa' ); ?></p>
				<button type="button" class="notice-dismiss">
					<span class="screen-reader-text"><?php \esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
				</button>
			</div>
				<?php
			}
			if ( isset( $_GET['wp_2fa_network_settings_updated'] ) && 'false' === $_GET['wp_2fa_network_settings_updated'] ) { // phpcs:ignore
				?>
			<div class="notice notice-error is-dismissible">
				<?php
				if ( isset( $_GET['wp_2fa_network_settings_custom_error_message'] ) ) { // phpcs:ignore
					$error = \sanitize_text_field( \wp_unslash( $_GET['wp_2fa_network_settings_custom_error_message'] ) );
					?>
					<p><?php echo \esc_attr( \esc_url_raw( \urldecode_deep( $error ) ) ); ?></p>
					<button type="button" class="notice-dismiss">
						<span class="screen-reader-text"><?php \esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
					</button>
					<?php
				} else {
					?>
				<p><?php \esc_html_e( 'Please ensure both custom email address and display name are provided.', 'wp-2fa' ); ?></p>
				<button type="button" class="notice-dismiss">
					<span class="screen-reader-text"><?php \esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
				</button>
					<?php
				}
				?>
			</div>
				<?php
			}
			if ( isset( $_GET['wp_2fa_network_settings_error'] ) ) { // phpcs:ignore
				?>
			<div class="notice notice-error is-dismissible">
				<?php
					$error = \sanitize_text_field( \wp_unslash( $_GET['wp_2fa_network_settings_error'] ) );

				if ( true === \strpos( $error, 'http' ) ) {
					?>
				<p><?php echo \esc_attr( \esc_url_raw( \urldecode_deep( $error ) ) ); ?></p>
					<?php
				} else {
					?>
				<p><?php echo \esc_attr( ( $error ) ); ?></p>
				<?php } ?>
				<button type="button" class="notice-dismiss">
					<span class="screen-reader-text"><?php \esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
				</button>
			</div>
				<?php
			}
		}

		/**
		 * These are used instead of add_settings_error which in a network site. Used to show if settings have been updated or failed.
		 *
		 * @return void
		 *
		 * @since 2.0.0
		 */
		public static function settings_saved_admin_notice() {
			if ( isset( $_GET['page'] ) && 0 === strpos( \sanitize_text_field( \wp_unslash( $_GET['page'] ) ), 'wp-2fa-' ) ) {
				if ( isset( $_GET['settings-updated'] ) && 'true' === $_GET['settings-updated'] ) {
					$wp_settings_errors = get_settings_errors();

					if ( count( $wp_settings_errors ) ) {
						foreach ( $wp_settings_errors as $error ) {
							?>
					<div class="notice notice-<?php echo \esc_attr( $error['type'] ); ?> is-dismissible">
						<p><?php echo \esc_html( $error['message'] ); ?></p>
						<button type="button" class="notice-dismiss">
							<span class="screen-reader-text"><?php \esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
						</button>
					</div>
							<?php
						}
					} else {
						?>
					<div class="notice notice-success is-dismissible">
						<p><?php \esc_html_e( '2FA Settings Updated', 'wp-2fa' ); ?></p>
						<button type="button" class="notice-dismiss">
							<span class="screen-reader-text"><?php \esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
						</button>
					</div>
						<?php
					}
				}
				if ( isset( $_GET['settings-updated'] ) && 'false' === $_GET['settings-updated'] ) {
					?>
				<div class="notice notice-error is-dismissible">
					<p><?php \esc_html_e( 'Please ensure both custom email address and display name are provided.', 'wp-2fa' ); ?></p>
					<button type="button" class="notice-dismiss">
						<span class="screen-reader-text"><?php \esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
					</button>
				</div>
					<?php
				}
				if ( isset( $_GET['settings_error'] ) ) {
					?>
				<div class="notice notice-error is-dismissible">
					<p><?php echo \esc_attr( \esc_url_raw( \urldecode_deep( \sanitize_text_field( \wp_unslash( $_GET['settings_error'] ) ) ) ) ); ?></p>
					<button type="button" class="notice-dismiss">
						<span class="screen-reader-text"><?php \esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
					</button>
				</div>
					<?php
				}
			}
		}

		/**
		 * Add our custom state to our created page.
		 *
		 * @param array   $post_states - array with the post states.
		 * @param WP_Post $post - the WP post.
		 *
		 * @return array
		 */
		public static function add_display_post_states( $post_states, $post ) {
			if ( ! empty( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) ) ) {
				if ( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) === $post->ID ) {
					$post_states['wp_2fa_page_for_user'] = __( 'WP 2FA User Page', 'wp-2fa' );
				}
			}

			return $post_states;
		}

		/**
		 * Handles sending of an email. It sets necessary header such as content type and custom from email address and name.
		 *
		 * @param string $recipient_email Email address to send message to.
		 * @param string $subject Email subject.
		 * @param string $message Message contents.
		 *
		 * @return bool Whether the email contents were sent successfully.
		 */
		public static function send_email( $recipient_email, $subject, $message ) {

			// Specify our desired headers.
			$headers = 'Content-type: text/html;charset=utf-8' . "\r\n";

			if ( 'use-custom-email' === WP2FA::get_wp2fa_email_templates( 'email_from_setting' ) ) {
				$headers .= 'From: ' . WP2FA::get_wp2fa_email_templates( 'custom_from_display_name' ) . ' <' . WP2FA::get_wp2fa_email_templates( 'custom_from_email_address' ) . '>' . "\r\n";
			} else {

				$headers .= 'From: wp2fa <' . self::get_default_email_address() . '>' . "\r\n";
				// $headers .= 'From: ' . get_bloginfo( 'name' ) . ' <' . get_bloginfo( 'admin_email' ) . '>' . "\r\n";
			}

			// Fire our email.
			return wp_mail( $recipient_email, stripslashes_deep( html_entity_decode( $subject, ENT_QUOTES, 'UTF-8' ) ), $message, $headers );
		}

		/**
		 * Builds and returns the default email address used for the "from" email address when email is send
		 *
		 * @return string
		 *
		 * @since 2.6.4
		 */
		public static function get_default_email_address(): string {
			$sitename   = wp_parse_url( network_home_url(), PHP_URL_HOST );
			$from_email = 'wp2fa@';

			if ( null !== $sitename ) {
				if ( str_starts_with( $sitename, 'www.' ) ) {
					$sitename = substr( $sitename, 4 );
				}

				$from_email .= $sitename;
			}

			return $from_email;
		}

		/**
		 * Turns user roles data in any form and shape to an array of strings.
		 *
		 * @param mixed $value User role names (slugs) as raw value.
		 *
		 * @return string[] List of user role names (slugs).
		 */
		public static function extract_roles_from_input( $value ) {
			if ( is_array( $value ) ) {
				return $value;
			}

			if ( is_string( $value ) && ! empty( $value ) ) {
				return explode( ',', $value );
			}

			return array();
		}

		/**
		 * Determine if any BG processes are currently running.
		 *
		 * @return int|false Number of jobs.
		 */
		public static function get_current_number_of_active_bg_processes() {
			global $wpdb;

			$bg_jobs = $wpdb->get_results( // phpcs:ignore
				"SELECT option_value FROM $wpdb->options
				WHERE option_name LIKE '%_2fa_bg_%'"
			);

			return count( $bg_jobs );
		}

		/**
		 * Checks the email against the current domain and shows an error message if they do not match.
		 *
		 * @return void
		 *
		 * @since 2.6.0
		 */
		public static function check_email() {
			$is_dismissed = (bool) Settings_Utils::get_option( 'dismiss_notice_mail_domain', false );
			if ( ! $is_dismissed ) {
				$admin_email = null;
				if ( 'use-custom-email' === WP2FA::get_wp2fa_email_templates( 'email_from_setting' ) ) {
					$admin_email = WP2FA::get_wp2fa_email_templates( 'custom_from_email_address' );
				}

				if ( '' === trim( (string) $admin_email ) ) {
					$email_settings_url = \esc_url(
						add_query_arg(
							array(
								'page' => 'wp-2fa-settings',
								'tab'  => 'email-settings',
							),
							network_admin_url( 'admin.php' )
						)
					);
					?>
					<div class="notice notice-error" style="padding-top: 10px; padding-bottom: 10px;">
						<p class="description" ><?php \esc_html_e( 'By default, the plugin uses ', 'wp-2fa' ); ?> <b><?php echo \sanitize_email( self::get_default_email_address() ); ?></b> <?php \esc_html_e( 'as the "from address" when sending emails with the 2FA code for users to log in. Do you want to keep using this or change it?', 'wp-2fa' ); ?></p>
						<p>
							<a class="button button-primary" href="<?php echo \esc_url( $email_settings_url ); ?>"><?php \esc_html_e( 'Change it', 'wp-2fa' ); ?></a>
							<a class="button button-secondary 2fa-email-notice" style="margin-left:20px" href="#">
								<?php \esc_html_e( 'Keep using it', 'wp-2fa' ); ?>
							</a>
						</p>
						
						<?php wp_nonce_field( 'wp2fa_dismiss_notice_mail_domain', 'wp2fa_dismiss_notice_mail_domain', false ); ?>
					</div>
					<?php
				} else {
					Settings_Utils::update_option( 'dismiss_notice_mail_domain', true );
				}
			}
		}

		/**
		 * Sets the email domain do not match setting as dismissed.
		 *
		 * @return void
		 *
		 * @since 2.6.0
		 */
		public static function dismiss_notice_mail_domain() {
			// Verify nonce.
			if ( isset( $_POST['nonce'] ) && \wp_verify_nonce( \sanitize_text_field( \wp_unslash( $_POST['nonce'] ) ), 'wp2fa_dismiss_notice_mail_domain' ) ) {
				Settings_Utils::update_option( 'dismiss_notice_mail_domain', true );
				die();
			}

			die( 'Nonce verification failed!' );
		}
	}
}
includes/classes/Admin/Methods/class-totp.php000064400000036351150755130600015256 0ustar00<?php
/**
 * Responsible for WP2FA user's TOTP manipulation.
 *
 * @package    wp2fa
 * @subpackage methods
 *
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 *
 * @see       https://wordpress.org/plugins/wp-2fa/
 *
 * @since 2.6.0
 */

declare(strict_types=1);

namespace WP2FA\Methods;

use WP2FA\WP2FA;
use WP2FA\Admin\User_Profile;
use WP2FA\Authenticator\Open_SSL;
use WP2FA\Admin\Helpers\User_Helper;
use WP2FA\Admin\Controllers\Settings;
use WP2FA\Authenticator\Authentication;
use WP2FA\Methods\Wizards\TOTP_Wizard_Steps;

/**
 * Class for handling totp codes.
 *
 * @since 2.6.0
 *
 * @package WP2FA
 */
if ( ! class_exists( '\WP2FA\Methods\TOTP' ) ) {
	/**
	 * TOTP code class, for handling totp (app) code generation and such.
	 *
	 * @since 2.6.0
	 */
	class TOTP {

		/**
		 * The name of the method.
		 *
		 * @var string
		 *
		 * @since 2.6.0
		 */
		public const METHOD_NAME = 'totp';

		/**
		 * Secret TOTP key meta name.
		 *
		 * @var string
		 *
		 * @since 2.6.0
		 */
		public const TOTP_META_KEY = WP_2FA_PREFIX . 'totp_key';

		/**
		 * The name of the method stored in the policy
		 *
		 * @var string
		 *
		 * @since 2.6.0
		 */
		public const POLICY_SETTINGS_NAME = 'enable_totp';

		/**
		 * Is the totp method enabled
		 *
		 * @since 1.7
		 *
		 * @var bool
		 */
		private static $totp_enabled = null;

		/**
		 * Totp key assigned to user
		 *
		 * @var string
		 */
		private static $totp_key = '';

		/**
		 * Inits the class and sets the filters.
		 *
		 * @return void
		 *
		 * @since 2.6.0
		 */
		public static function init() {

			\add_filter( WP_2FA_PREFIX . 'providers_translated_names', array( __CLASS__, 'totp_provider_name_translated' ) );

			\add_filter( WP_2FA_PREFIX . 'providers', array( __CLASS__, 'totp_provider' ) );

			\add_filter( WP_2FA_PREFIX . 'default_settings', array( __CLASS__, 'add_default_settings' ) );

			\add_filter( WP_2FA_PREFIX . 'loop_settings', array( __CLASS__, 'settings_loop' ), 10, 1 );

			\add_filter( WP_2FA_PREFIX . 'no_method_enabled', array( __CLASS__, 'return_default_selection' ), 10, 1 );

			// add the TOTP methods to the list of available methods if enabled.
			\add_filter(
				WP_2FA_PREFIX . 'available_2fa_methods',
				function ( $available_methods ) {
					if ( ! empty( Settings::get_role_or_default_setting( self::POLICY_SETTINGS_NAME, 'current' ) ) ) {
						array_push( $available_methods, self::METHOD_NAME );
					}

					return $available_methods;
				}
			);

			TOTP_Wizard_Steps::init();
		}

		/**
		 * Adds TOTP translated name
		 *
		 * @param array $providers - Array with all currently supported providers and their translated names.
		 *
		 * @return array
		 *
		 * @since 2.6.0
		 */
		public static function totp_provider_name_translated( array $providers ) {
			$providers[ self::METHOD_NAME ] = esc_html__( 'TOTP (one-time code via app)', 'wp-2fa' );

			return $providers;
		}

		/**
		 * Extracts the selected value from the global settings (if set), and adds it to the output array
		 *
		 * @param array $output - The array with output values.
		 *
		 * @return array
		 *
		 * @since 2.6.0
		 */
		public static function return_default_selection( array $output ) {
			// No method is enabled, fall back to previous selected one - we don't want to break the logic.
			$totp_enabled = WP2FA::get_wp2fa_setting( self::POLICY_SETTINGS_NAME );

			if ( $totp_enabled ) {
				$output[ self::POLICY_SETTINGS_NAME ] = $totp_enabled;
			}

			return $output;
		}

		/**
		 * Sets the TOTP as a method for the given user
		 *
		 * @param \WP_User $user - The user for which the method has to be set, if null, it uses the current user.
		 * @param string   $totp_key - The totp key for the user to be set.
		 *
		 * @return void
		 *
		 * @throws \LogicException - If the method is called without $totp_key.
		 *
		 * @since 2.6.0
		 */
		public static function set_user_method( $user = null, string $totp_key = '' ) {
			if ( null === $user ) {
				$user = wp_get_current_user();
			}

			if ( '' === \trim( $totp_key ) ) {
				throw new \LogicException( 'TOTP key must not be empty' );
			}

			User_Helper::set_enabled_method_for_user( self::METHOD_NAME, $user );
			self::set_user_totp_key( $totp_key, $user );
			User_Profile::delete_expire_and_enforced_keys( $user->ID );
			User_Helper::set_user_status( $user );
		}

		/**
		 * Adds TOTP as a provider
		 *
		 * @param array $providers - Array with all currently supported providers.
		 *
		 * @return array
		 *
		 * @since 2.6.0
		 */
		public static function totp_provider( array $providers ) {
			array_push( $providers, self::METHOD_NAME );

			return $providers;
		}

		/**
		 * Retrieves the QR code
		 *
		 * @since 2.6.0
		 *
		 * @return string
		 */
		public static function get_qr_code(): string {

			// Setup site information, used when generating our QR code.
			$site_name = site_url();
			$site_name = trim( str_replace( array( 'http://', 'https://' ), '', (string) $site_name ), '/' );
			/**
			 * Changing the title of the login screen for the TOTP method.
			 *
			 * @param string $title - The default title.
			 * @param \WP_User $user - The WP user.
			 *
			 * @since 2.0.0
			 */
			$totp_title = apply_filters(
				WP_2FA_PREFIX . 'totp_title',
				$site_name . ':' . User_Helper::get_user_object()->user_login,
				User_Helper::get_user_object()
			);

			return Authentication::get_google_qr_code( $totp_title, self::get_totp_key(), $site_name );
		}

		/**
		 * Validates authentication.
		 *
		 * @param \WP_User $user - The WP user, if presented.
		 *
		 * @return bool Whether the user gave a valid code
		 *
		 * @since 2.6.0
		 */
		public static function validate_totp_authentication( \WP_User $user = null ) {
			if ( ! empty( $_REQUEST['authcode'] ) ) {  //phpcs:ignore
				$valid = Authentication::is_valid_authcode(
					self::get_totp_key( $user ),
					\sanitize_text_field( \wp_unslash( $_REQUEST['authcode'] ) )
				);
				if ( $valid ) {
					Authentication::clear_login_attempts( $user );
				} else {
					Authentication::increase_login_attempts( $user );
				}
				return $valid;
			}

			return false;
		}

		/**
		 * Add extension settings to the loop array
		 *
		 * @param array $loop_settings - Currently available settings array.
		 *
		 * @return array
		 *
		 * @since 2.6.0
		 */
		public static function settings_loop( array $loop_settings ) {
			array_push( $loop_settings, self::POLICY_SETTINGS_NAME );

			return $loop_settings;
		}

		/**
		 * Returns the status of the totp method (enabled | disabled)
		 *
		 * @since 2.6.0
		 *
		 * @return boolean
		 */
		public static function is_enabled(): bool {
			if ( null === self::$totp_enabled ) {
				self::$totp_enabled = empty( Settings::get_role_or_default_setting( self::POLICY_SETTINGS_NAME, 'current' ) ) ? false : true;
			}

			return self::$totp_enabled;
		}

		/**
		 * Regenerates the TOTP key for the user
		 *
		 * @return void - JSON - object with "key" - stores the new key and "qr" - stores the new QR code.
		 *
		 * @since 2.5.0
		 */
		public static function regenerate_authentication_key() {
			// Grab current user.
			$user = wp_get_current_user();

			$key = Authentication::generate_key();

			$site_name = site_url();
			$site_name = trim( str_replace( array( 'http://', 'https://' ), '', (string) $site_name ), '/' );

			/**
			 * Changing the title of the login screen for the TOTP method.
			 *
			 * @param string $title - The default title.
			 * @param \WP_User $user - The WP user.
			 *
			 * @since 2.0.0
			 */
			$totp_title = apply_filters( WP_2FA_PREFIX . 'totp_title', $site_name . ':' . $user->user_login, $user );
			$new_qr     = Authentication::get_google_qr_code( $totp_title, $key, $site_name );

			wp_send_json_success(
				array(
					'key' => Authentication::decrypt_key_if_needed( $key ),
					'qr'  => $new_qr,
				)
			);
		}

		/**
		 * Adds the extension default settings to the main plugin settings
		 *
		 * @param array $default_settings - array with plugin default settings.
		 *
		 * @return array
		 *
		 * @since 2.6.0
		 */
		public static function add_default_settings( array $default_settings ) {
			$default_settings[ self::POLICY_SETTINGS_NAME ]   = self::POLICY_SETTINGS_NAME;
			$default_settings['method_help_totp_intro']       = '<h3>' . __( 'Setting up TOTP (one-time code via app)', 'wp-2fa' ) . '</h3>';
			$default_settings['method_help_totp_step_1']      = __( 'Download and start the application of your choice', 'wp-2fa' );
			$default_settings['method_help_totp_step_2']      = __( 'From within the application scan the QR code provided on the left. Otherwise, enter the following code manually in the application:', 'wp-2fa' );
			$default_settings['method_help_totp_step_3']      = __( 'Click the "I\'m ready" button below when you complete the application setup process to proceed with the wizard.', 'wp-2fa' );
			$default_settings['method_verification_totp_pre'] = '<h3>' . __( 'Almost there…', 'wp-2fa' ) . '</h3><p>' . __( 'Please type in the one-time code from your chosen authentication app to finalize the setup.', 'wp-2fa' ) . '</p>';
			$default_settings['totp_reconfigure_intro']       = '<h3>' . __( '{reconfigure_or_configure_capitalized} the 2FA App', 'wp-2fa' ) . '</h3><p>' . __( 'Click the below button to {reconfigure_or_configure} the current 2FA method. Note that once reset you will have to re-scan the QR code on all devices you want this to work on because the previous codes will stop working.', 'wp-2fa' ) . '</p>';
			$default_settings['totp-option-label']            = __( 'One-time code via 2FA app', 'wp-2fa' );
			$default_settings['totp-option-label-hint']       = sprintf(
				/* translators: link to the knowledge base website */
				\esc_html__( 'Refer to the %s for more information on how to setup these apps and which apps are supported.', 'wp-2fa' ),
			'<a href="https://melapress.com/support/kb/wp-2fa-configuring-2fa-apps/?&utm_source=plugin&utm_medium=link&utm_campaign=wp2fa" target="_blank">' . \esc_html__( 'guide on how to set up 2FA apps', 'wp-2fa' ) . '</a>'
			);

			return $default_settings;
		}

		/**
		 * User totp key getter
		 *
		 * @param int|\WP_User|null $user - The WP user we should extract the meta data for.
		 *
		 * @return string
		 *
		 * @since 2.6.0
		 */
		public static function get_totp_key( $user = null ): string {
			if ( '' === trim( (string) self::$totp_key ) ) {
				self::$totp_key = self::get_user_totp_key_auth( User_Helper::get_user( $user )->ID );
				if ( empty( self::$totp_key ) ) {
					self::$totp_key = Authentication::generate_key();

					self::set_user_totp_key( self::$totp_key, $user );
				} elseif ( Open_SSL::is_ssl_available() && false === \strpos( self::$totp_key, Open_SSL::SECRET_KEY_PREFIX ) ) {
						self::$totp_key = Open_SSL::SECRET_KEY_PREFIX . Open_SSL::encrypt( self::$totp_key );
						self::set_user_totp_key( self::$totp_key, $user );
				}
			}

			return self::$totp_key;
		}

		/**
		 * Returns the encoded TOTP when we need to show the actual code to the user
		 * If for some reason the code is invalid it recreates it
		 *
		 * @param int|\WP_User|null $user - The WP user we should extract the meta data for.
		 *
		 * @return string
		 *
		 * @since 2.6.0
		 */
		public static function get_totp_decrypted( $user = null ): string {
			$key = self::get_totp_key( $user );
			if ( Open_SSL::is_ssl_available() && false !== \strpos( $key, 'ssl_' ) ) {

				/**
				 * Old key detected - convert.
				 */
				$key = Open_SSL::decrypt_legacy( substr( $key, 4 ) );

				self::remove_user_totp_key( $user );
				self::$totp_key = '';

				$key = self::get_totp_key( $user );
			}

			if ( Open_SSL::is_ssl_available() && false !== \strpos( $key, 'wps_' ) ) {

				/**
				 * Old key detected - convert.
				 */
				$key = Open_SSL::decrypt_wps( substr( $key, 4 ) );

				self::remove_user_totp_key( $user );

				$secret = Open_SSL::encrypt( $key );

				if ( Open_SSL::is_ssl_available() ) {
					$secret = Open_SSL::SECRET_KEY_PREFIX . $secret;
				}

				self::set_user_totp_key( $secret, $user );

				self::$totp_key = $secret;
			}

			if ( Open_SSL::is_ssl_available() && false !== \strpos( $key, Open_SSL::SECRET_KEY_PREFIX ) ) {
				$key = Open_SSL::decrypt( substr( $key, 4 ) );

				/**
				 * If for some reason the key is not valid, that means that we have to clear the stored TOTP for the user, and create new on
				 * That could happen if the global stored secret (plugin level) is deleted.
				 *
				 * Lets check and if that is the case - create new one
				 */
				if ( ! Authentication::validate_base32_string( $key ) ) {
					self::$totp_key = '';
					self::remove_user_totp_key( $user );
					$key = self::get_totp_key( $user );
					$key = Open_SSL::decrypt( substr( $key, 4 ) );
				}
			}

			return $key;
		}

		/**
		 * Deletes the TOTP secret key for a user.
		 *
		 * @param int|\WP_User|null $user - The WP user that must be used.
		 *
		 * @return void
		 */
		public static function remove_user_totp_key( $user = null ) {
			User_Helper::remove_meta( self::TOTP_META_KEY, $user );

			self::$totp_key = '';
		}

		/**
		 * Returns the TOTP secret key for a user.
		 *
		 * @param int|\WP_User|null $user - The WP user that must be used.
		 *
		 * @return string
		 */
		public static function get_user_totp_key( $user = null ) {
			return User_Helper::get_meta( self::TOTP_META_KEY, $user );
		}

		/**
		 * Updates the TOTP secret key for a user.
		 *
		 * @param string            $value - The value of the TOTP key.
		 * @param int|\WP_User|null $user  - The WP user that must be used.
		 *
		 * @return void
		 *
		 * @since 2.2.0
		 */
		public static function set_user_totp_key( string $value, $user = null ) {
			User_Helper::set_meta( self::TOTP_META_KEY, $value, $user );
		}

		/**
		 * Get the TOTP secret key for a user.
		 *
		 * @param  int $user_id User ID.
		 *
		 * @return string
		 *
		 * @since 2.6.0
		 */
		public static function get_user_totp_key_auth( $user_id ) {

			$key = (string) self::get_user_totp_key( $user_id );

			$test = $key;

			if ( Open_SSL::is_ssl_available() && false !== \strpos( $key, 'ssl_' ) ) {

				/**
				 * Old key detected - convert.
				 */
				$key = Open_SSL::decrypt_legacy( substr( $key, 4 ) );

				self::remove_user_totp_key();

				$secret = Open_SSL::encrypt( $key );

				if ( Open_SSL::is_ssl_available() ) {
					$secret = Open_SSL::SECRET_KEY_PREFIX . $secret;
				}

				self::set_user_totp_key( $key, $user_id );

				$test = $key = (string) self::get_user_totp_key( $user_id ); // phpcs:ignore Squiz.PHP.DisallowMultipleAssignments.Found
			}

			// We've tried tried to use WP core functionality, but that doesn't work - lets update.
			if ( Open_SSL::is_ssl_available() && false !== \strpos( $key, 'wps_' ) ) {

				/**
				 * Old key detected - convert.
				 */
				$key = Open_SSL::decrypt_wps( substr( $key, 4 ) );

				self::remove_user_totp_key();

				$secret = Open_SSL::encrypt( $key );

				if ( Open_SSL::is_ssl_available() ) {
					$secret = Open_SSL::SECRET_KEY_PREFIX . $secret;
				}

				self::set_user_totp_key( $key, $user_id );

				$test = $key = (string) self::get_user_totp_key( $user_id );  // phpcs:ignore Squiz.PHP.DisallowMultipleAssignments.Found
			}

			Authentication::decrypt_key_if_needed( $test );

			if ( ! Authentication::is_valid_key( $test ) ) {
				$key = Authentication::generate_key();
				self::set_user_totp_key( $key, $user_id );
				Authentication::clear_decrypted_key();
			}

			return $key;
		}
	}
}
includes/classes/Admin/Methods/Traits/index.php000064400000000046150755130600015532 0ustar00<?php
/**
 * Nothing to see here.
 */
includes/classes/Admin/Methods/Traits/class-login-attempts.php000064400000006052150755130600020500 0ustar00<?php
/**
 * Responsible for the plugin login attempts
 *
 * @package    wp2fa
 * @subpackage traits
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 * @link       https://wordpress.org/plugins/wp-2fa/
 */

namespace WP2FA\Admin\Methods\Traits;

use WP2FA\Admin\Helpers\User_Helper;

defined( 'ABSPATH' ) || exit; // Exit if accessed directly.
/**
 * Responsible for the login attempts
 *
 * @since 2.4.1
 */
trait Login_Attempts {

	/**
	 * Holds the number of allowed attempts to login
	 *
	 * @var integer
	 *
	 * @since 2.4.1
	 */
	private static $number_of_allowed_attempts = 3;

	/**
	 * Increasing login attempts for User
	 *
	 * @since 2.4.1
	 *
	 * @param \WP_User $user - the WP User.
	 *
	 * @return void
	 */
	public static function increase_login_attempts( \WP_User $user ) {
		$attempts = self::get_login_attempts( $user );
		if ( '' === $attempts ) {
			$attempts = 0;
		}
		User_Helper::set_meta( self::$logging_attempts_meta_key, ++$attempts, $user );
	}

	/**
	 * Returns the number of unsuccessful attempts for the User
	 *
	 * @since 2.4.1
	 *
	 * @param \WP_User $user - the WP User.
	 *
	 * @return integer
	 */
	public static function get_login_attempts( \WP_User $user ): int {
		return (int) User_Helper::get_meta( self::$logging_attempts_meta_key, $user );
	}

	/**
	 * Clearing login attempts for User
	 *
	 * @since 2.4.1
	 *
	 * @param \WP_User $user - the WP User.
	 *
	 * @return void
	 */
	public static function clear_login_attempts( \WP_User $user ) {
		User_Helper::remove_meta( self::$logging_attempts_meta_key, $user );
	}

	/**
	 * Returns the number of allowed login attempts
	 *
	 * @return integer
	 *
	 * @since 2.4.1
	 */
	public static function get_allowed_login_attempts(): int {
		return self::$number_of_allowed_attempts;
	}

	/**
	 * Sets the number of allowed attempts
	 *
	 * @param integer $number - The number of the allowed attempts.
	 *
	 * @return integer
	 *
	 * @since 2.4.1
	 */
	public static function set_number_of_login_attempts( int $number ): int {
		self::$number_of_allowed_attempts = $number;

		return self::$number_of_allowed_attempts;
	}

	/**
	 * Returns the name of the meta key holding the login attempts for the user
	 *
	 * @return string
	 *
	 * @since 2.4.1
	 */
	public static function get_meta_key(): string {

		return self::$logging_attempts_meta_key;
	}

	/**
	 * Sets the login attempts meta key
	 *
	 * @param string $logging_attempts_meta_key - The name of the meta.
	 *
	 * @return string
	 *
	 * @since 2.4.1
	 */
	public static function set_meta_key( string $logging_attempts_meta_key ): string {
		self::$logging_attempts_meta_key = $logging_attempts_meta_key;

		return self::$logging_attempts_meta_key;
	}

	/**
	 * Checks the number of login attempts
	 *
	 * @param \WP_User $user - The user we have to check for.
	 *
	 * @return boolean
	 *
	 * @since 2.4.1
	 */
	public static function check_number_of_attempts( \WP_User $user ): bool {
		if ( self::get_allowed_login_attempts() < self::get_login_attempts( $user ) ) {
			return false;
		}

		return true;
	}
}
includes/classes/Admin/Methods/Traits/class-methods-wizards-trait.php000064400000006055150755130600022001 0ustar00<?php
/**
 * Responsible for the plugin wizard ordering
 *
 * @package    wp2fa
 * @subpackage traits
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 * @link       https://wordpress.org/plugins/wp-2fa/
 */

namespace WP2FA\Admin\Methods\Traits;

use WP2FA\Admin\Controllers\Settings;
use WP2FA\Admin\Helpers\Methods_Helper;
use WP2FA\Extensions\RoleSettings\Role_Settings_Controller;

defined( 'ABSPATH' ) || exit; // Exit if accessed directly.
/**
 * Responsible for the login attempts
 *
 * @since 2.6.0
 */
trait Methods_Wizards_Trait {

	/**
	 * Returns the order in the wizard
	 *
	 * @param string $role - The name of the role - could be empty.
	 * @param array  $methods - The array with currently collected methods.
	 *
	 * @return integer
	 *
	 * @since 2.6.0
	 */
	public static function get_order( string $role = null, array $methods = array() ): int {
		if ( null !== $role && ! empty( $role ) && class_exists( '\WP2FA\Extensions\RoleSettings\Role_Settings_Controller' ) ) {
			$methods_order = Role_Settings_Controller::get_setting( $role, Methods_Helper::POLICY_SETTINGS_NAME );

			if ( \is_array( $methods_order ) ) {
				$methods_order = \array_flip( $methods_order );

				if ( isset( $methods_order[ self::get_main_class()::METHOD_NAME ] ) ) {
					static::$order = (int) $methods_order[ self::get_main_class()::METHOD_NAME ];
				}
			}
		} else {
			$use_role_setting = null;
			if ( null === $role || '' === trim( (string) $role ) ) {
				$use_role_setting = \WP_2FA_PREFIX . 'no-user';
			}

			$methods_order = Settings::get_role_or_default_setting( Methods_Helper::POLICY_SETTINGS_NAME, $use_role_setting, $role, true );

			if ( \is_array( $methods_order ) ) {
				$methods_order = \array_flip( $methods_order );

				if ( isset( $methods_order[ self::get_main_class()::METHOD_NAME ] ) ) {
					static::$order = (int) $methods_order[ self::get_main_class()::METHOD_NAME ];
				}
			}
		}

		if ( isset( $methods[ static::$order ] ) ) {
			// Obviously we have a problem here - such order already exists in the methods array, so grab the biggest order and increase it.
			// TODO: maybe we need to update the settings for that method as well ?

			static::$order = max( array_keys( $methods ) );

			++static::$order;
		}

		return static::$order;
	}

	/**
	 * Returns the main class of the given wizard steps class.
	 *
	 * @return string
	 *
	 * @since 2.6.0
	 */
	public static function get_main_class(): string {
		return static::$main_class;
	}

	/**
	 * Creates hidden field for the method order
	 *
	 * @param string $role - The name of the role (if present).
	 *
	 * @return string
	 *
	 * @since 2.6.0
	 */
	public static function hidden_order_setting( string $role = null ): string {
		$name_prefix = WP_2FA_POLICY_SETTINGS_NAME;
		if ( null !== $role && '' !== trim( (string) $role ) ) {
			$name_prefix .= "[{$role}]";
		}
		$hidden_field = '<input type="hidden" name="' . \esc_attr( $name_prefix ) . '[methods_order][]" value="' . \esc_attr( self::get_main_class()::METHOD_NAME ) . '">';

		return $hidden_field;
	}
}
includes/classes/Admin/Methods/class-email-wizard-steps.php000064400000034572150755130600020014 0ustar00<?php
/**
 * Responsible for WP2FA user's Email manipulation.
 *
 * @package    wp2fa
 * @subpackage methods-wizard
 * @since      2.6.0
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 *
 * @see       https://wordpress.org/plugins/wp-2fa/
 */

declare(strict_types=1);

namespace WP2FA\Methods\Wizards;

use WP2FA\WP2FA;
use WP2FA\Methods\Email;
use WP2FA\Admin\Helpers\WP_Helper;
use WP2FA\Admin\Views\Wizard_Steps;
use WP2FA\Admin\Helpers\User_Helper;
use WP2FA\Admin\Controllers\Settings;
use WP2FA\Admin\Methods\Traits\Methods_Wizards_Trait;
use WP2FA\Extensions\RoleSettings\Role_Settings_Controller;

/**
 * Class for handling email codes.
 *
 * @since 2.6.0
 *
 * @package WP2FA
 */
if ( ! class_exists( '\WP2FA\Methods\Wizards\Email_Wizard_Steps' ) ) {
	/**
	 * Email code class, for handling email code generation and such.
	 *
	 * @since 2.6.0
	 */
	class Email_Wizard_Steps extends Wizard_Steps {

		use Methods_Wizards_Trait;

		/**
		 * Keeps the main class method name, so we can call it when needed.
		 *
		 * @var string
		 *
		 * @since 2.6.0
		 */
		private static $main_class = Email::class;

		/**
		 * The default value of the method order in the wizards.
		 *
		 * @var integer
		 *
		 * @since 2.6.0
		 */
		private static $order = 2;

		/**
		 * Inits the class hooks
		 *
		 * @return void
		 *
		 * @since 2.4.0
		 */
		public static function init() {
			\add_filter( WP_2FA_PREFIX . 'methods_modal_options', array( __CLASS__, 'email_option' ), 10, 2 );
			\add_action( WP_2FA_PREFIX . 'modal_methods', array( __CLASS__, 'email_modal_configure' ) );
			\add_filter( WP_2FA_PREFIX . 'methods_re_configure', array( __CLASS__, 'email_re_configure' ), 10, 2 );
			\add_filter( WP_2FA_PREFIX . 'methods_settings', array( __CLASS__, 'email_wizard_settings' ), 10, 4 );
		}

		/**
		 * Shows the option for email method reconfiguring (if applicable)
		 *
		 * @param array  $methods - Array of methods collected.
		 * @param string $role - The name of the role to show option to.
		 *
		 * @since 2.6.0 - Parameter $methods is added, parameter $role (name) is added and array is now returned
		 *
		 * @return array
		 */
		public static function email_re_configure( array $methods, string $role ): array {

			if ( ! Email::is_enabled() ) {
				return $methods;
			}
			\ob_start();
			?>
			<div class="option-pill">
				<?php echo \wp_kses_post( WP2FA::contextual_reconfigure_text( WP2FA::get_wp2fa_white_label_setting( 'hotp_reconfigure_intro', true ), User_Helper::get_user_object()->ID, 'hotp' ) ); ?>
				<div class="wp2fa-setup-actions">
					<a class="button button-primary wp-2fa-button-primary" data-name="next_step_setting_modal_wizard" value="<?php \esc_attr_e( 'I\'m Ready', 'wp-2fa' ); ?>" data-user-id="<?php echo \esc_attr( User_Helper::get_user_object()->ID ); ?>" <?php echo WP_Helper::create_data_nonce( 'wp-2fa-send-setup-email' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> data-next-step="2fa-wizard-email"><?php \esc_html_e( 'Change email address', 'wp-2fa' ); ?></a>
				</div>
			</div>
			<?php
				$output = ob_get_contents();
				ob_end_clean();

				$methods[ self::get_order( $role, $methods ) ] = array(
					'name'   => self::$main_class::METHOD_NAME,
					'output' => $output,
				);

				return $methods;
		}

		/**
		 * Shows the initial email setup options based on enabled methods
		 *
		 * @param array  $methods - Array of methods collected.
		 * @param string $role - The name of the role to show option to.
		 *
		 * @since 2.6.0 - Parameter $methods is added, parameter $role (name) is added and array is now returned
		 *
		 * @return array
		 */
		public static function email_option( array $methods, string $role ): array {
			if ( Email::is_enabled() ) {
				\ob_start();
				?>
					<div class="option-pill">
						<label for="geek">
							<input id="geek" name="wp_2fa_enabled_methods" type="radio" value="email">
						<?php echo \wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'email-option-label', true ) ); ?>
						</label>
					</div>
				<?php
				$output = ob_get_contents();
				ob_end_clean();

				$methods[ self::get_order( $role, $methods ) ] = $output;
			}

			return $methods;
		}

		/**
		 * Settings page and first time wizard settings render
		 *
		 * @param array   $methods - Array with all the methods in which we have to add this one.
		 * @param boolean $setup_wizard - Is that the first time setup wizard.
		 * @param string  $data_role - Additional HTML data attribute.
		 * @param mixed   $role - Name of the role.
		 *
		 * @return array - The array with the methods with all the methods wizard steps.
		 *
		 * @since 2.6.0
		 */
		public static function email_wizard_settings( array $methods, bool $setup_wizard, string $data_role, $role = null ) {
			$name_prefix = \WP_2FA_POLICY_SETTINGS_NAME;
			$role_id     = '';
			if ( null !== $role && '' !== trim( (string) $role ) ) {
				$name_prefix .= "[{$role}]";
				$data_role    = 'data-role="' . $role . '"';
				$role_id      = '-' . $role;
			}
			\ob_start();
			?>
				<div id="<?php echo \esc_attr( Email::METHOD_NAME ); ?>-method-wrapper" class="method-wrapper">
					<?php echo self::hidden_order_setting( $role ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
					<label for="hotp<?php echo \esc_attr( $role_id ); ?>" style="margin-bottom: 0 !important;">
							<input type="checkbox" id="hotp<?php echo \esc_attr( $role_id ); ?>" name="<?php echo \esc_attr( $name_prefix ); ?>[enable_email]" value="enable_email"
							<?php echo $data_role; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
							<?php if ( null !== $role && ! empty( $role ) ) { ?>
								<?php \checked( Email::POLICY_SETTINGS_NAME, Role_Settings_Controller::get_setting( $role, Email::POLICY_SETTINGS_NAME ), true ); ?>
								<?php
							} else {
								$use_role_setting = null;
								if ( null === $role || '' === trim( (string) $role ) ) {
									$use_role_setting = \WP_2FA_PREFIX . 'no-user';
								}

								$enabled_settings = Settings::get_role_or_default_setting( Email::POLICY_SETTINGS_NAME, $use_role_setting, $role, true );
								?>
								<?php \checked( $enabled_settings, Email::POLICY_SETTINGS_NAME ); ?>
							<?php } ?>
							>
							<?php
							\esc_html_e( 'One-time code via email (HOTP)', 'wp-2fa' );
							\esc_html_e( ' - ensure email deliverability with the free plugin ', 'wp-2fa' );
							echo '<a href="https://wordpress.org/plugins/wp-mail-smtp/" target="_blank" rel="nofollow">WP Mail SMTP</a>.';
							?>
					</label>
					<?php
					if ( $setup_wizard ) {
						echo '<p class="description">' . \esc_html__( 'When using this method, users will receive the one-time login code over email. Therefore, email deliverability is very important. Users using this method should whitelist the address from which the codes are sent. By default, this is the email address configured in your WordPress. You can run an email test from the plugin\'s settings to confirm email deliverability. If you have had email deliverability / reliability issues, we highly recommend you to install the free plugin ', 'wp-2fa' ) . '<a href="https://wordpress.org/plugins/wp-mail-smtp/" target="_blank" rel="nofollow">WP Mail SMTP</a><br><br>' . \esc_html__( 'Allowing users to set up a secondary 2FA method is highly recommended. You can do this in the next step of the wizard. This will allow users to log in using an alternative method should they, for example lose access to their phone.', 'wp-2fa' ) . '</p>';
					}
					?>
					<?php
					if ( null !== $role ) {
						$enabled_settings = Role_Settings_Controller::get_setting( $role, 'enable_email' );
					} else {
						$enabled_settings = Settings::get_role_or_default_setting( 'enable_email', ( ( null !== $role && '' !== $role ) ? '' : false ), $role, true, true );
					}
					?>
					<?php
					?>
					<?php if ( ! $setup_wizard ) { ?>
						<div class="use-different-hotp-mail<?php echo \esc_attr( ( false === $enabled_settings ? ' disabled' : '' ) ); ?>">
							<p class="description">
								<?php \esc_html_e( 'Allow user to specify the email address of choice', 'wp-2fa' ); ?>
							</p>
							<fieldset class="email-hotp-options">
							<?php
							$options = array(
								'yes' => array(
									'label' => \esc_html__( 'Yes', 'wp-2fa' ),
									'value' => 'specify-email_hotp',
								),
								'no'  => array(
									'label' => \esc_html__( 'No', 'wp-2fa' ),
									'value' => '',
								),
							);

							foreach ( $options as $option_key => $option_settings ) {
								?>
								<label for="specify-email_hotp-<?php echo \esc_attr( $option_key ); ?>">
									<input type="radio"
									name="<?php echo \esc_attr( $name_prefix ); ?>[specify-email_hotp]"
									<?php echo $data_role; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
									id="specify-email_hotp-<?php echo \esc_attr( $option_key ); ?>"
									value="<?php echo \esc_attr( $option_settings['value'] ); ?>" class="js-nested"
									<?php if ( null !== $role ) { ?>
										<?php \checked( Role_Settings_Controller::get_setting( $role, 'specify-email_hotp' ), $option_settings['value'] ); ?>
										<?php
									} else {

										$use_role_setting = null;
										if ( null === $role || '' === trim( (string) $role ) ) {
											$use_role_setting = \WP_2FA_PREFIX . 'no-user';
										}

										\checked( Settings::get_role_or_default_setting( 'specify-email_hotp', $use_role_setting, $role, true, false ), $option_settings['value'] );
										?>
									<?php } ?>
									>
									<span><?php echo $option_settings['label']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span>
								</label>
								<?php
							}
							?>
							</fieldset>
						</div>
					<?php } ?>
				</div>
			<?php
			$output = ob_get_contents();
			ob_end_clean();

			$methods[ self::get_order( $role, $methods ) ] = $output;

			return $methods;
		}

		/**
		 * Reconfigures email form
		 *
		 * @since 2.6.0
		 *
		 * @return void
		 */
		public static function email_modal_configure() {

			if ( ! Email::is_enabled() ) {
				return;
			}
			?>
			<div class="wizard-step" id="2fa-wizard-email">
				<fieldset>
					<div class="step-setting-wrapper active">
						<div class="mb-20">
							<?php echo \wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'method_help_hotp_intro', true ) ); ?>
						</div>
						<fieldset class="radio-cells">
						<div class="option-pill">
							<label for="use_wp_email">
								<input type="radio" name="wp_2fa_email_address" id="use_wp_email" value="<?php echo \esc_attr( User_Helper::get_user_object()->user_email ); ?>" checked>
								<span><?php \esc_html_e( 'Use my user email (', 'wp-2fa' ); ?><small><?php echo \esc_attr( User_Helper::get_user_object()->user_email ); ?></small><?php \esc_html_e( ')', 'wp-2fa' ); ?></span>
							</label>
						</div>
						<?php
						if ( Settings::get_role_or_default_setting( 'specify-email_hotp', User_Helper::get_user_object() ) ) {
							?>
						<div class="option-pill">
							<label for="use_custom_email">
								<input type="radio" name="wp_2fa_email_address" id="use_custom_email" value="use_custom_email">
								<span><?php \esc_html_e( 'Use a different email address:', 'wp-2fa' ); ?></span>
								<?php \esc_html_e( 'Email address', 'wp-2fa' ); ?>
								<input type="email" name="custom-email-address" id="custom-email-address" class="input" value=""/>
							</label>
						</div>
							<?php
						}
						?>
						</fieldset>
						<p class="description"><?php echo \wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'method_help_hotp_help', true ) ); ?></p><br>

						<?php
						$from_email = \get_option( 'admin_email' );

						$custom_mail = WP2FA::get_wp2fa_email_templates( 'custom_from_email_address' );

						if ( isset( $custom_mail ) && ! empty( (string) $custom_mail ) ) {
							$from_email = $custom_mail;
						}

						echo \wp_kses_post( str_replace( '{from_email}', $from_email, WP2FA::get_wp2fa_white_label_setting( 'method_help_hotp_help_email', true ) ) );
						?>

						<div class="wp2fa-setup-actions">
							<button class="button button-primary wp-2fa-button-primary" name="next_step_setting_email_verify" value="<?php \esc_attr_e( 'I\'m Ready', 'wp-2fa' ); ?>" data-trigger-setup-email data-user-id="<?php echo \esc_attr( User_Helper::get_user_object()->ID ); ?>" <?php echo WP_Helper::create_data_nonce( 'wp-2fa-send-setup-email' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> type="button"><?php \esc_html_e( 'I\'m Ready', 'wp-2fa' ); ?></button>
							<a class="button button-primary wp-2fa-button-primary modal_cancel"><?php \esc_attr_e( 'Cancel', 'wp-2fa' ); ?></a>
						</div>
					</div>

					<div class="step-setting-wrapper" data-step-title="<?php \esc_html_e( 'Verify configuration', 'wp-2fa' ); ?>" id="2fa-wizard-email">
						<div class="mb-20">
							<?php echo \wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'method_verification_hotp_pre', true ) ); ?>
						</div>
						<fieldset>
							<label for="2fa-email-authcode">
								<?php \esc_html_e( 'Authentication Code', 'wp-2fa' ); ?>
								<input type="tel" name="wp-2fa-email-authcode" id="wp-2fa-email-authcode" class="input" value="" size="20" pattern="[0-9]*" autocomplete="off"/>
								<script>
									const email_authcode = document.getElementById('wp-2fa-email-authcode');
									email_authcode.addEventListener('input', function() {
									this.value = this.value.trim();
									});
								</script>
							</label>
							<div class="verification-response"></div>
						</fieldset>
						<br />
						<a href="#" class="button wp-2fa-button-primary" data-validate-authcode-ajax <?php echo WP_Helper::create_data_nonce( 'wp-2fa-validate-authcode' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>><?php \esc_html_e( 'Validate & Save', 'wp-2fa' ); ?></a>
						<a href="#" class="button wp-2fa-button-primary resend-email-code" data-trigger-setup-email data-user-id="<?php echo \esc_attr( User_Helper::get_user_object()->ID ); ?>" <?php echo WP_Helper::create_data_nonce( 'wp-2fa-send-setup-email' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
							<span class="resend-inner"><?php \esc_html_e( 'Send me another code', 'wp-2fa' ); ?></span>
						</a>
						<button class="wp-2fa-button-secondary button" data-close-2fa-modal aria-label="Close this dialog window"><?php \esc_html_e( 'Cancel', 'wp-2fa' ); ?></button>
					</div>
				</fieldset>
			</div>
			<?php
		}
	}
}
includes/classes/Admin/Methods/class-email.php000064400000014063150755130600015353 0ustar00<?php
/**
 * Responsible for WP2FA user's email method manipulation.
 *
 * @package    wp2fa
 * @subpackage methods
 *
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 *
 * @see       https://wordpress.org/plugins/wp-2fa/
 *
 * @since 2.6.0
 */

declare(strict_types=1);

namespace WP2FA\Methods;

use WP2FA\WP2FA;
use WP2FA\Admin\Controllers\Settings;
use WP2FA\Methods\Wizards\Email_Wizard_Steps;

/**
 * Class for handling email codes.
 *
 * @since 2.6.0
 *
 * @package WP2FA
 */
if ( ! class_exists( '\WP2FA\Methods\Email' ) ) {
	/**
	 * Email code class, for handling email method code generation and such.
	 *
	 * @since 2.6.0
	 */
	class Email {

		/**
		 * The name of the method.
		 *
		 * @var string
		 *
		 * @since 2.6.0
		 */
		public const METHOD_NAME = 'email';

		/**
		 * The name of the method stored in the policy
		 *
		 * @var string
		 *
		 * @since 2.6.0
		 */
		public const POLICY_SETTINGS_NAME = 'enable_email';

		/**
		 * Is the mail enabled
		 *
		 * @since 2.6.0
		 *
		 * @var bool
		 */
		private static $email_enabled = null;

		/**
		 * Inits the class and sets the filters.
		 *
		 * @return void
		 *
		 * @since 2.6.0
		 */
		public static function init() {

			\add_filter( WP_2FA_PREFIX . 'providers_translated_names', array( __CLASS__, 'email_provider_name_translated' ) );

			\add_filter( WP_2FA_PREFIX . 'providers', array( __CLASS__, 'email_provider' ) );

			\add_filter( WP_2FA_PREFIX . 'default_settings', array( __CLASS__, 'add_default_settings' ) );

			\add_filter( WP_2FA_PREFIX . 'loop_settings', array( __CLASS__, 'settings_loop' ), 10, 1 );

			\add_filter( WP_2FA_PREFIX . 'no_method_enabled', array( __CLASS__, 'return_default_selection' ), 10, 1 );

			// add the TOTP methods to the list of available methods if enabled.
			\add_filter(
				WP_2FA_PREFIX . 'available_2fa_methods',
				function ( $available_methods ) {
					if ( ! empty( Settings::get_role_or_default_setting( self::POLICY_SETTINGS_NAME, 'current' ) ) ) {
						array_push( $available_methods, self::METHOD_NAME );
					}

					return $available_methods;
				}
			);

			Email_Wizard_Steps::init();
		}

		/**
		 * Adds email provider translatable name
		 *
		 * @param array $providers - Array with all currently supported providers and their translated names.
		 *
		 * @return array
		 *
		 * @since 2.6.0
		 */
		public static function email_provider_name_translated( array $providers ) {
			$providers[ self::METHOD_NAME ] = \esc_html__( 'HOTP (Email)', 'wp-2fa' );

			return $providers;
		}

		/**
		 * Adds email as a provider
		 *
		 * @param array $providers - Array with all currently supported providers.
		 *
		 * @return array
		 *
		 * @since 2.6.0
		 */
		public static function email_provider( array $providers ) {
			array_push( $providers, self::METHOD_NAME );

			return $providers;
		}

		/**
		 * Adds the extension default settings to the main plugin settings
		 *
		 * @param array $default_settings - array with plugin default settings.
		 *
		 * @return array
		 *
		 * @since 2.6.0
		 */
		public static function add_default_settings( array $default_settings ) {
			$default_settings[ self::POLICY_SETTINGS_NAME ] = self::POLICY_SETTINGS_NAME;
			$default_settings['specify-email_hotp']           = 'specify-email_hotp';
			$default_settings['method_help_hotp_intro']       = '<h3>' . __( 'Setting up HOTP (one-time code via email)', 'wp-2fa' ) . '</h3><p>' . __( 'Please select the email address where the one-time code should be sent:', 'wp-2fa' ) . '</p>';
			$default_settings['method_help_hotp_help']        = __( 'To complete the 2FA configuration you will be sent a one-time code over email, therefore you should have access to the mailbox of this email address. If you do not receive the email with the one-time code please check your spam folder and contact your administrator', 'wp-2fa' );
			$default_settings['method_help_hotp_help_email']  = '<b>' . __( 'IMPORTANT', 'wp-2fa' ) . '</b><p>' . __( 'To ensure you always receive the one-time code whitelist the email address from which the codes are sent. This is {from_email}', 'wp-2fa' ) . '</p>';
			$default_settings['method_verification_hotp_pre'] = '<h3>' . __( 'Almost there…', 'wp-2fa' ) . '</h3><p>' . __( 'Please type in the one-time code sent to your email address to finalize the setup', 'wp-2fa' ) . '</p>';
			$default_settings['hotp_reconfigure_intro']       = '<h3>' . __( '{reconfigure_or_configure_capitalized} one-time code over email method', 'wp-2fa' ) . '</h3><p>' . __( 'Click the below button to {reconfigure_or_configure} the email address where the one-time code should be sent.', 'wp-2fa' ) . '</p>';
			$default_settings['email-option-label']           = __( 'One-time code via email', 'wp-2fa' );

			return $default_settings;
		}

		/**
		 * Add extension settings to the loop array
		 *
		 * @param array $loop_settings - Currently available settings array.
		 *
		 * @return array
		 *
		 * @since 2.6.0
		 */
		public static function settings_loop( array $loop_settings ) {
			array_push( $loop_settings, self::POLICY_SETTINGS_NAME );
			array_push( $loop_settings, 'specify-email_hotp' );


			return $loop_settings;
		}

		/**
		 * Extracts the selected value from the global settings (if set), and adds it to the output array
		 *
		 * @param array $output - The array with output values.
		 *
		 * @return array
		 *
		 * @since 2.6.0
		 */
		public static function return_default_selection( array $output ) {
			// No method is enabled, fall back to previous selected one - we don't want to break the logic.
			$email_enabled = WP2FA::get_wp2fa_setting( self::POLICY_SETTINGS_NAME );

			if ( $email_enabled ) {
				$output[ self::POLICY_SETTINGS_NAME ] = $email_enabled;
			}

			return $output;
		}

		/**
		 * Returns the status of the mail method (enabled | disabled)
		 *
		 * @since 2.6.0
		 *
		 * @return boolean
		 */
		public static function is_enabled(): bool {
			if ( null === self::$email_enabled ) {
				self::$email_enabled = empty( Settings::get_role_or_default_setting( 'enable_email', 'current' ) ) ? false : true;
			}

			return self::$email_enabled;
		}
	}
}
includes/classes/Admin/Methods/class-backup-codes.php000064400000037615150755130600016634 0ustar00<?php
/**
 * Responsible for WP2FA user's backup codes manipulation.
 *
 * @package    wp2fa
 * @subpackage methods
 *
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 *
 * @see       https://wordpress.org/plugins/wp-2fa/
 *
 * @since 2.6.0
 */

declare(strict_types=1);

namespace WP2FA\Methods;

use WP2FA\WP2FA;
use WP2FA\Admin\Settings_Page;
use WP2FA\Utils\Settings_Utils;
use WP2FA\Admin\Helpers\User_Helper;
use WP2FA\Admin\Controllers\Settings;
use WP2FA\Authenticator\Authentication;
use WP2FA\Admin\Methods\Traits\Login_Attempts;

/**
 * Class for handling backup codes.
 *
 * @since 0.1-dev
 *
 * @package WP2FA
 */
if ( ! class_exists( '\WP2FA\Methods\Backup_Codes' ) ) {
	/**
	 * Backup code class, for handling backup code generation and such.
	 *
	 * @since 2.6.0
	 */
	class Backup_Codes {

		use Login_Attempts;

		/**
		 * Holds the name of the meta key for the allowed login attempts.
		 *
		 * @var string
		 *
		 * @since 2.0.0
		 */
		private static $logging_attempts_meta_key = WP_2FA_PREFIX . 'backup-login-attempts';

		/**
		 * Key used for backup codes.
		 *
		 * @var string
		 *
		 * @since 2.6.0
		 */
		public const BACKUP_CODES_META_KEY = 'wp_2fa_backup_codes';

		/**
		 * The number backup codes.
		 *
		 * @var int
		 *
		 * @since 2.6.0
		 */
		public const NUMBER_OF_CODES = 10;

		/**
		 * The name of the method.
		 *
		 * @var string
		 *
		 * @since 2.0.0
		 */
		public const METHOD_NAME = 'backup_codes';

		/**
		 * The login attempts class.
		 *
		 * @var \WP2FA\Admin\Controllers\Login_Attempts
		 *
		 * @since 2.0.0
		 */
		private static $login_attempts = null;

		/**
		 * Holds the status of the backup codes functionality
		 *
		 * @var bool[]
		 *
		 * @since 2.6.0
		 */
		private static $backup_codes_enabled = array();

		/**
		 * Default extension settings.
		 *
		 * @var array
		 *
		 * @since 2.6.0
		 */
		private static $settings = array(
			'backup_codes_enabled' => 'yes',
		);

		/**
		 * Inits the backup codes class hooks
		 *
		 * @return void
		 *
		 * @since 2.6.0
		 */
		public static function init() {
			\add_filter( WP_2FA_PREFIX . 'backup_methods_list', array( __CLASS__, 'add_backup_method' ), 10, 2 );
			\add_filter( WP_2FA_PREFIX . 'backup_methods_enabled', array( __CLASS__, 'check_backup_method_for_role' ), 10, 2 );
			\add_action( 'wp_ajax_wp2fa_run_ajax_generate_json', array( __CLASS__, 'run_ajax_generate_json' ) );

			\add_action( WP_2FA_PREFIX . 'remove_backup_methods_for_user', array( __CLASS__, 'remove_backup_methods_for_user' ) );

			\add_filter( WP_2FA_PREFIX . 'loop_settings', array( __CLASS__, 'settings_loop' ), 10, 2 );

			\add_filter( WP_2FA_PREFIX . 'default_settings', array( __CLASS__, 'add_default_settings' ) );

			\add_filter( WP_2FA_PREFIX . 'providers', array( __CLASS__, 'backup_codes' ) );

			\add_filter( WP_2FA_PREFIX . 'providers_translated_names', array( __CLASS__, 'fill_providers_array_with_method_name_translated' ) );

			\add_filter( WP_2FA_PREFIX . 'user_enabled_backup_methods', array( __CLASS__, 'method_enabled_for_user' ), 10, 2 );
		}

		/**
		 * Generate backup codes.
		 *
		 * @param object $user User data.
		 * @param string $args possible args.
		 *
		 * @since 2.6.0
		 */
		public static function generate_codes( $user, $args = '' ) {
			$codes        = array();
			$codes_hashed = array();

			// Check for arguments.
			if ( isset( $args['number'] ) ) {
				$num_codes = (int) $args['number'];
			} else {
				$num_codes = self::NUMBER_OF_CODES;
			}

			// Append or replace (default).
			if ( isset( $args['method'] ) && 'append' === $args['method'] ) {
				$codes_hashed = (array) \get_user_meta( $user->ID, self::BACKUP_CODES_META_KEY, true );
			}

			for ( $i = 0; $i < $num_codes; ++$i ) {
				$code           = Authentication::get_code();
				$codes_hashed[] = \wp_hash_password( $code );
				$codes[]        = $code;
				unset( $code );
			}

			\update_user_meta( $user->ID, self::BACKUP_CODES_META_KEY, $codes_hashed );

			// Unhashed.
			return $codes;
		}

		/**
		 * Fills the array of the enabled backup methods is it is provided for the given user
		 *
		 * @param array    $array_methods - Array to fill if the method is enabled for user.
		 * @param \WP_User $user - The user to check for.
		 *
		 * @return array
		 *
		 * @since 2.6.0
		 */
		public static function method_enabled_for_user( array $array_methods, $user ): array {
			if ( self::is_enabled_for_user( $user ) ) {
				$array_methods[ self::METHOD_NAME ] = self::get_translated_name();
			}

			return $array_methods;
		}

		/**
		 * Adds Backup codes as a provider.
		 *
		 * @param array $providers - Array with all currently supported providers.
		 *
		 * @return array
		 *
		 * @since 2.6.0
		 */
		public static function backup_codes( array $providers ) {
			array_push( $providers, self::METHOD_NAME );

			return $providers;
		}

		/**
		 * Adds Backup code as a provider.
		 *
		 * @param array $providers - Array with all currently supported providers and their translated names.
		 *
		 * @return array
		 *
		 * @since 2.6.0
		 */
		public static function fill_providers_array_with_method_name_translated( array $providers ) {
			$providers[ self::METHOD_NAME ] = self::get_translated_name();

			return $providers;
		}

		/**
		 * Returns the name of the provider
		 *
		 * @return string
		 *
		 * @since 2.6.0
		 */
		public static function get_translated_name(): string {
			return esc_html__( 'Backup codes', 'wp-2fa' );
		}

		/**
		 * Removes the backup method (user meta key) from the database.
		 *
		 * @param \WP_User,int,null $user - The user to remove method for.
		 *
		 * @return void
		 *
		 * @since 2.5.0
		 */
		public static function remove_backup_methods_for_user( $user ) {
			if ( ! Settings::is_provider_enabled_for_role( User_Helper::get_user_role( $user ), self::get_method_name() ) ) {
				\delete_user_meta( $user->ID, self::BACKUP_CODES_META_KEY );
			}
		}

		/**
		 * Generate codes and check remaining amount for user.
		 *
		 * @return void
		 *
		 * @since 2.6.0
		 */
		public static function run_ajax_generate_json() {
			$user = wp_get_current_user();

			check_ajax_referer( 'wp-2fa-backup-codes-generate-json-' . $user->ID, 'nonce' );

			// Setup the return data.
			$codes = self::generate_codes( $user );

			$count = self::codes_remaining_for_user( $user );
			$i18n  = array(
				'count' => esc_html(
					sprintf(
						/* translators: %s: count */
						_n( '%s unused code remaining.', '%s unused codes remaining.', $count, 'wp-2fa' ),
						$count
					)
				),
				/* translators: %s: the site's domain */
				'title' => esc_html__( 'Two-Factor Backup Codes for %s', 'wp-2fa' ),
			);

			// Send the response.
			wp_send_json_success(
				array(
					'codes' => $codes,
					'i18n'  => $i18n,
				)
			);
		}

		/**
		 * Grab number of unused backup codes within the users position.
		 *
		 * @param object $user - User data.
		 *
		 * @return int Count of codes.
		 *
		 * @since 2.6.0
		 */
		public static function codes_remaining_for_user( $user ) {
			$backup_codes = \get_user_meta( $user->ID, self::BACKUP_CODES_META_KEY, true );
			if ( is_array( $backup_codes ) && ! empty( $backup_codes ) ) {
				return count( $backup_codes );
			}

			return 0;
		}

		/**
		 * Validate backup codes.
		 *
		 * @param object $user User data.
		 * @param string $code The code we are checking.
		 *
		 * @return bool Is is valid or not.
		 *
		 * @since 2.6.0
		 */
		public static function validate_code( $user, $code ) {
			$backup_codes = \get_user_meta( $user->ID, self::BACKUP_CODES_META_KEY, true );
			if ( is_array( $backup_codes ) && ! empty( $backup_codes ) ) {
				foreach ( $backup_codes as $code_hashed ) {
					if ( \wp_check_password( $code, $code_hashed, $user->ID ) ) {
						self::delete_code( $user, $code_hashed );
						self::clear_login_attempts( $user );

						return true;
					}
				}
			}
			self::increase_login_attempts( $user );

			return false;
		}

		/**
		 * Delete code once its used.
		 *
		 * @param object $user        User data.
		 * @param string $code_hashed Code to delete.
		 *
		 * @since 2.6.0
		 */
		public static function delete_code( $user, $code_hashed ) {
			$backup_codes = get_user_meta( $user->ID, self::BACKUP_CODES_META_KEY, true );

			// Delete the current code from the list since it's been used.
			$backup_codes = array_flip( $backup_codes );
			unset( $backup_codes[ $code_hashed ] );
			$backup_codes = array_values( array_flip( $backup_codes ) );

			// Update the backup code master list.
			\update_user_meta( $user->ID, self::BACKUP_CODES_META_KEY, $backup_codes );
		}

		/**
		 * Add the method to the existing backup methods array.
		 *
		 * @param array $backup_methods - Array with the currently supported backup methods.
		 *
		 * @since 2.0.0
		 */
		public static function add_backup_method( array $backup_methods ): array {
			return array_merge(
				$backup_methods,
				array(
					self::METHOD_NAME => array(
						'wizard-step' => '2fa-wizard-config-backup-codes',
						'button_name' => sprintf(
							/* translators: URL with more information about the backup codes */
							esc_html__( 'Login with a backup code: you will get 10 backup codes and you can use one of them when you need to login and you cannot generate a code from the app. %s', 'wp-2fa' ),
							'<a href="https://melapress.com/2fa-backup-codes/" target="_blank">' . esc_html__( 'More information.', 'wp-2fa' ) . '</a>'
						),
					),
				)
			);
		}

		/**
		 * Changes the global backup methods array - removes the method if it is not enabled.
		 *
		 * @param array    $backup_methods - Array with all global backup methods.
		 * @param \WP_User $user           - User to check for is that method enabled.
		 *
		 * @since 2.0.0
		 */
		public static function check_backup_method_for_role( array $backup_methods, \WP_User $user ): array {
			$enabled = self::are_backup_codes_enabled_for_role( User_Helper::get_user_role( $user ) );

			if ( ! $enabled ) {
				unset( $backup_methods[ self::METHOD_NAME ] );
			}

			return $backup_methods;
		}

		/**
		 * Returns the name of the method.
		 *
		 * @since 2.0.0
		 */
		public static function get_method_name(): string {
			return self::METHOD_NAME;
		}

		/**
		 * Checks if the backup codes option is enabled for the role
		 *
		 * @param string $role - The role name.
		 *
		 * @return bool
		 *
		 * @since 2.6.0
		 */
		public static function are_backup_codes_enabled_for_role( $role = 'global' ) {

			$role = ( is_null( $role ) || empty( $role ) ) ? 'global' : $role;

			if ( ! isset( self::$backup_codes_enabled[ $role ] ) ) {
				self::$backup_codes_enabled[ $role ] = false;

				if ( 'global' === $role ) {
					$setting_value = Settings::get_role_or_default_setting( self::get_settings_name() );
				} else {
					$setting_value = Settings::get_role_or_default_setting( self::get_settings_name(), 'current', $role );
				}
				self::$backup_codes_enabled[ $role ] = Settings_Utils::string_to_bool( $setting_value );
			}

			return self::$backup_codes_enabled[ $role ];
		}

		/**
		 * Checks if the backup codes are enabled for the user.
		 *
		 * @param int|\WP_User|null $user - The WP user we should extract the meta data for.
		 *
		 * @return bool
		 *
		 * @since 2.6.0
		 *
		 * @throws \LogicException - can not extract user from the given parameters.
		 */
		public static function is_enabled_for_user( $user ): bool {
			$user = User_Helper::get_user_object( $user );

			if ( ! \is_a( $user, '\WP_User' ) ) {
				throw new \LogicException( 'Not a proper user object provided!' );
			}

			$codes_remaining = self::codes_remaining_for_user( $user );

			return (bool) $codes_remaining;
		}

		/**
		 * Adds settings names to the extraction array - grabs the values and stores them based on names.
		 *
		 * @param array $settings - Array with all the settings.
		 *
		 * @since 2.6.0
		 */
		public static function settings_loop( array $settings ): array {
			return array_merge( $settings, array_keys( self::$settings ) );
		}

		/**
		 * Adds the extension default settings to the main plugin settings.
		 *
		 * @param array $default_settings - Array with plugin default settings.
		 *
		 * @return array
		 *
		 * @since 2.6.0
		 */
		public static function add_default_settings( array $default_settings ) {
			return array_merge( $default_settings, self::$settings );
		}

		/**
		 * Returns the method settings name
		 *
		 * @return string
		 *
		 * @since 2.6.0
		 */
		public static function get_settings_name(): string {
			return \array_key_first( self::$settings );
		}

		/**
		 * Returns the method settings default value
		 *
		 * @return mixed
		 *
		 * @since 2.6.0
		 */
		public static function get_settings_default_value() {
			return \reset( self::$settings );
		}

		/**
		 * Validates a backup code.
		 *
		 * Backup Codes are single use and are deleted upon a successful validation.
		 *
		 * @since 2.6.0
		 *
		 * @param \WP_User $user \WP_User object of the logged-in user.
		 *
		 * @return boolean
		 */
		public static function validate_backup_codes( $user ) {
			if ( ! isset( $user->ID ) || ! isset( $_REQUEST['wp-2fa-backup-code'] ) ) { //phpcs:ignore
				return false;
			}

			return self::validate_code( $user, \sanitize_text_field( \wp_unslash( $_REQUEST['wp-2fa-backup-code'] ) ) );
		}

		/**
		 * Returns the backup codes for the user.
		 *
		 * @param \WP_User $user \WP_User - object of the logged-in user.
		 *
		 * @return array
		 *
		 * @since 2.6.0
		 */
		public static function get_backup_codes_for_user( $user ): array {
			$backup_codes = get_user_meta( $user->ID, self::BACKUP_CODES_META_KEY, true );

			if ( ! \is_array( $backup_codes ) ) {
				return array();
			}

			return $backup_codes;
		}

		/**
		 * Send email with fresh code, or to setup email 2fa.
		 *
		 * @param int    $user_id User id we want to send the message to.
		 * @param string $nominated_email_address - The user custom address to use (name of the meta key to check for).
		 *
		 * @return bool
		 *
		 * @since 2.6.0
		 */
		public static function send_backup_codes_email( $user_id, $nominated_email_address = 'nominated_email_address' ) {

			// If we have a nonce posted, check it.
			if ( \wp_doing_ajax() && isset( $_POST['_wpnonce'] ) ) {
				$nonce_check = \wp_verify_nonce( \sanitize_text_field( \wp_unslash( $_POST['_wpnonce'] ) ), 'wp-2fa-send-backup-codes-email-nonce' );
				if ( ! $nonce_check ) {
					return false;
				}
			} else {
				\wp_die();
			}

			$user = User_Helper::get_user_object();

			$enabled_email_address = '';
			if ( ! empty( $nominated_email_address ) ) {
				if ( 'nominated_email_address' === $nominated_email_address ) {
					$enabled_email_address = User_Helper::get_nominated_email_for_user( $user );
				} else {
					$enabled_email_address = get_user_meta( $user->ID, WP_2FA_PREFIX . $nominated_email_address, true );
				}
			}

			if ( isset( $_POST['codes'] ) ) {
				$codes = substr( str_replace( '\\n', '<br>', \sanitize_text_field( \wp_unslash( $_POST['codes'] ) ) ), 1, -1 );

				$posted_codes = array_filter( \explode( '<br>', $codes ) );

				$stored_codes = self::get_backup_codes_for_user( $user );

				foreach ( $posted_codes as $key => $check_code ) {
					$check_code = trim( \explode( ':', $check_code )[1] );
					if ( ! \wp_check_password( $check_code, $stored_codes[ $key ], $user->ID ) ) {

						\wp_die();
					}
				}
			} else {
				\wp_die();
			}

			$subject = wp_strip_all_tags( WP2FA::replace_email_strings( WP2FA::get_wp2fa_email_templates( 'user_backup_codes_email_subject' ), $user->ID ) );
			$message = wpautop( WP2FA::replace_email_strings( WP2FA::get_wp2fa_email_templates( 'user_backup_codes_email_body' ), $user->ID ) );

			$final_output = str_replace( '{backup_codes}', $codes, $message );

			if ( ! empty( $enabled_email_address ) ) {
				$email_address = $enabled_email_address;
			} else {
				$email_address = $user->user_email;
			}

			return Settings_Page::send_email( $email_address, $subject, $final_output );
		}

		/**
		 * Marks methods as secondary.
		 *
		 * @return boolean
		 *
		 * @since 2.7.0
		 */
		public static function is_secondary() {
			return true;
		}
	}
}
includes/classes/Admin/Methods/class-totp-wizard-steps.php000064400000036112150755130600017703 0ustar00<?php
/**
 * Responsible for WP2FA user's TOTP manipulation.
 *
 * @package    wp2fa
 * @subpackage methods-wizard
 * @since      2.6.0
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 *
 * @see       https://wordpress.org/plugins/wp-2fa/
 */

declare(strict_types=1);

namespace WP2FA\Methods\Wizards;

use WP2FA\WP2FA;
use WP2FA\Methods\TOTP;
use WP2FA\Admin\Helpers\WP_Helper;
use WP2FA\Admin\Views\Wizard_Steps;
use WP2FA\Admin\Helpers\User_Helper;
use WP2FA\Admin\Controllers\Settings;
use WP2FA\Admin\Methods\Traits\Methods_Wizards_Trait;
use WP2FA\Authenticator\Authentication;
use WP2FA\Extensions\RoleSettings\Role_Settings_Controller;

/**
 * Class for handling totp codes.
 *
 * @since 2.6.0
 *
 * @package WP2FA
 */
if ( ! class_exists( '\WP2FA\Methods\Wizards\TOTP_Wizard_Steps' ) ) {
	/**
	 * TOTP code class, for handling totp (app) code generation and such.
	 *
	 * @since 2.6.0
	 */
	class TOTP_Wizard_Steps extends Wizard_Steps {

		use Methods_Wizards_Trait;

		/**
		 * Keeps the main class method name, so we can call it when needed.
		 *
		 * @var string
		 *
		 * @since 2.6.0
		 */
		private static $main_class = TOTP::class;

		/**
		 * The default value of the method order in the wizards.
		 *
		 * @var integer
		 *
		 * @since 2.6.0
		 */
		private static $order = 1;

		/**
		 * Inits the class hooks
		 *
		 * @return void
		 *
		 * @since 2.4.0
		 */
		public static function init() {
			\add_filter( WP_2FA_PREFIX . 'methods_modal_options', array( __CLASS__, 'totp_option' ), 10, 2 );
			\add_action( WP_2FA_PREFIX . 'modal_methods', array( __CLASS__, 'totp_modal_configure' ) );
			\add_filter( WP_2FA_PREFIX . 'methods_re_configure', array( __CLASS__, 'totp_re_configure' ), 10, 2 );
			\add_filter( WP_2FA_PREFIX . 'methods_settings', array( __CLASS__, 'totp_wizard_settings' ), 10, 4 );
		}

		/**
		 * Shows the option to reconfigure email (if applicable)
		 *
		 * @param array  $methods - Array of methods collected.
		 * @param string $role - The name of the role to show option to.
		 *
		 * @since 2.6.0
		 *
		 * @return array
		 */
		public static function totp_re_configure( array $methods, string $role ): array {

			if ( ! TOTP::is_enabled() ) {
				return $methods;
			}
			\ob_start();
			?>
				<div class="option-pill">
					<?php echo \wp_kses_post( WP2FA::contextual_reconfigure_text( WP2FA::get_wp2fa_white_label_setting( 'totp_reconfigure_intro', true ), User_Helper::get_user_object()->ID, TOTP::METHOD_NAME ) ); ?>
					<div class="wp2fa-setup-actions">
						<a href="#" class="button button-primary wp-2fa-button-primary" data-name="next_step_setting_modal_wizard" data-trigger-reset-key <?php echo WP_Helper::create_data_nonce( self::json_nonce() ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> data-user-id="<?php echo \esc_attr( User_Helper::get_user_object()->ID ); ?>" data-next-step="2fa-wizard-totp"><?php \esc_html_e( 'Reset Key', 'wp-2fa' ); ?></a>
					</div>
				</div>
			<?php
				$output = ob_get_contents();
				ob_end_clean();

				$methods[ self::get_order( $role, $methods ) ] = array(
					'name'   => self::$main_class::METHOD_NAME,
					'output' => $output,
				);

				return $methods;
		}

		/**
		 * Shows the initial totp setup options based on enabled methods
		 *
		 * @param array  $methods - Array of methods collected.
		 * @param string $role - The name of the role to show option to.
		 *
		 * @since 2.6.0
		 *
		 * @return array
		 */
		public static function totp_option( array $methods, string $role ): array {
			if ( TOTP::is_enabled() ) {
				\ob_start();
				?>
				<div class="option-pill">
					<label for="basic">
						<input id="basic" name="wp_2fa_enabled_methods" type="radio" value="totp">
						<?php echo \wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'totp-option-label', true ) ); ?><span class="wizard-tooltip" data-tooltip-content="data-totp-tooltip-content-wrapper">i</span>
					</label>
					<?php
						echo '<p class="description tooltip-content-wrapper" data-totp-tooltip-content-wrapper>';
						echo \wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'totp-option-label-hint', true ) );
						echo '</p>';
					?>
				</div>
				<?php
				$output = ob_get_contents();
				ob_end_clean();

				$methods[ self::get_order( $role, $methods ) ] = $output;
			}

			return $methods;
		}

		/**
		 * Shows the TOTP modal configuration.
		 *
		 * @return void
		 *
		 * @since 2.6.0
		 */
		public static function totp_modal_configure() {
			if ( TOTP::is_enabled() ) {
				?>
			<div class="wizard-step" id="2fa-wizard-totp">
				<fieldset>
					<?php self::totp_configure(); ?>
				</fieldset>
			</div>
				<?php
			}
		}

		/**
		 * Reconfigures the totp form
		 *
		 * @since 2.6.0
		 *
		 * @return void
		 */
		public static function totp_configure() {

			if ( ! TOTP::is_enabled() ) {
				return;
			}

			// Regenerate the code if the method is not in use.
			if ( TOTP::METHOD_NAME !== User_Helper::get_enabled_method_for_user() ) {
				TOTP::remove_user_totp_key();
			}

			/**
			 * Active on modal, additional attribute is required on standard HTML (check below)
			 */
			$add_step_attributes = 'active';

			/**
			 * Closing div for extra modal wrappers see lines above
			 */
			$close_div = '';

			$qr_code                = '<img class="qr-code" src="' . ( TOTP::get_qr_code() ) . '" id="wp-2fa-totp-qrcode" />';
			$open30_wrapper         = '
				<div class="mb-30 clear-both">
				';
					$open60_wrapper = '
					<div class="modal-60">
				';
					$open40_wrapper = '
					<div class="modal-40">
				';
					$close_div      = '
				</div>
				';

			?>
				<div class="step-setting-wrapper <?php echo \esc_attr( $add_step_attributes ); ?>">
					<div class="mb-20">
						<?php echo wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'method_help_totp_intro', true ) ); ?>
					</div>
					<?php echo $open30_wrapper . $open40_wrapper; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>

					<div class="qr-code-wrapper">
						<?php echo $qr_code; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
					</div>
					<?php
					echo $close_div; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
					echo $open60_wrapper; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
					?>

					<div class="radio-cells option-pill mb-0">
						<ol class="wizard-custom-counter">
							<li><?php echo \wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'method_help_totp_step_1', true ) ); ?>
											<?php
											if ( ! empty( WP2FA::get_wp2fa_white_label_setting( 'show_help_text' ) ) ) {
												?>
								<span class="wizard-tooltip" data-tooltip-content="data-totp-setup-tooltip-content-wrapper">i</span><?php } ?></li>
							<li><?php echo \wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'method_help_totp_step_2', true ) ); ?>
								<div class="app-key-wrapper">
									<input type="text" id="app-key-input" readonly value="<?php echo \esc_html( TOTP::get_totp_decrypted() ); ?>" class="app-key">
									<?php
									if ( is_ssl() ) {
										?>
										<span class="click-to-copy"><?php \esc_html_e( 'COPY', 'wp-2fa' ); ?></span>
									<?php } ?>
								</div>
							</li>
							<li><?php echo \wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'method_help_totp_step_3', true ) ); ?></li>
						</ol>
					</div>
						<?php
						echo $close_div; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
						echo $close_div; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
						?>
						<?php if ( ! empty( WP2FA::get_wp2fa_white_label_setting( 'show_help_text' ) ) ) : ?>
					<div class="tooltip-content-wrapper" data-totp-setup-tooltip-content-wrapper>
						<p class="description"><?php \esc_html_e( 'Click on the icon of the app that you are using for a detailed guide on how to set it up.', 'wp-2fa' ); ?></p>
						<div class="apps-wrapper">
							<?php foreach ( Authentication::get_apps() as $app ) { ?>
								<a href="https://melapress.com/support/kb/wp-2fa-configuring-2fa-apps/?&utm_source=plugin&utm_medium=link&utm_campaign=wp2fa#<?php echo $app['hash']; ?>" target="_blank" class="app-logo"><img src="<?php echo \esc_url( WP_2FA_URL . 'dist/images/' . $app['logo'] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>"></a>
							<?php } ?>
						</div>
					</div>
					<?php endif; ?>
					<div class="wp2fa-setup-actions">
						<button class="button wp-2fa-button-primary" name="next_step_setting" value="<?php \esc_attr_e( 'I\'m Ready', 'wp-2fa' ); ?>" type="button"><?php \esc_html_e( 'I\'m Ready', 'wp-2fa' ); ?></button>
						<a class="button button-primary wp-2fa-button-secondary modal_cancel"><?php \esc_attr_e( 'Cancel', 'wp-2fa' ); ?></a>
					</div>
				</div>
				<div class="step-setting-wrapper" data-step-title="<?php \esc_html_e( 'Verify configuration', 'wp-2fa' ); ?>">
					<div class="mb-20">
						<?php echo \wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'method_verification_totp_pre', true ) ); ?>
					</div>
					<fieldset>
						<label for="2fa-totp-authcode">
							<?php \esc_html_e( 'Authentication Code', 'wp-2fa' ); ?>
							<input type="tel" name="wp-2fa-totp-authcode" id="wp-2fa-totp-authcode" class="input" value="" size="20" pattern="[0-9]*" autocomplete="off"/>
							<script>
								const totp_authcode = document.getElementById('wp-2fa-totp-authcode');
								totp_authcode.addEventListener('input', function() {
								this.value = this.value.trim();
								});
							</script>
						</label>
						<div class="verification-response"></div>
					</fieldset>
					<input type="hidden" name="wp-2fa-totp-key" value="<?php echo \esc_attr( TOTP::get_totp_decrypted() ); ?>" />
					
					<a href="#" class="modal__btn button button-primary wp-2fa-button-primary" data-validate-authcode-ajax <?php echo WP_Helper::create_data_nonce( 'wp-2fa-validate-authcode' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>><?php \esc_html_e( 'Validate & Save', 'wp-2fa' ); ?></a>
					<button class="modal__btn wp-2fa-button-secondary button button-secondary wp-2fa-button-secondary" data-close-2fa-modal aria-label="Close this dialog window"><?php \esc_html_e( 'Cancel', 'wp-2fa' ); ?></button>
				</div>

			<?php
		}

		/**
		 * Settings page and first time wizard settings render
		 *
		 * @param array   $methods - Array with all the methods in which we have to add this one.
		 * @param boolean $setup_wizard - Is that the first time setup wizard.
		 * @param string  $data_role - Additional HTML data attribute.
		 * @param mixed   $role - Name of the role.
		 *
		 * @return array - The array with the methods with all the methods wizard steps.
		 *
		 * @since 2.6.0
		 */
		public static function totp_wizard_settings( array $methods, bool $setup_wizard, string $data_role, $role = null ) {
			$name_prefix = WP_2FA_POLICY_SETTINGS_NAME;
			$role_id     = '';
			if ( null !== $role && '' !== trim( (string) $role ) ) {
				$name_prefix .= "[{$role}]";
				$data_role    = 'data-role="' . $role . '"';
				$role_id      = '-' . $role;
			}
			\ob_start();
			?>
			<div id="<?php echo \esc_attr( TOTP::METHOD_NAME ); ?>-method-wrapper" class="method-wrapper">
				<?php echo self::hidden_order_setting( $role ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
				<label for="totp<?php echo \esc_attr( $role_id ); ?>" style="margin-bottom: 0 !important;">
					<input type="checkbox" id="totp<?php echo \esc_attr( $role_id ); ?>" name="<?php echo \esc_attr( $name_prefix ); ?>[enable_totp]" value="enable_totp"
					<?php echo $data_role; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>

					<?php if ( null !== $role && ! empty( $role ) ) { ?>
						<?php \checked( TOTP::POLICY_SETTINGS_NAME, Role_Settings_Controller::get_setting( $role, TOTP::POLICY_SETTINGS_NAME ), true ); ?>
						<?php
					} else {
						$use_role_setting = null;
						if ( null === $role || '' === trim( (string) $role ) ) {
							$use_role_setting = \WP_2FA_PREFIX . 'no-user';
						}

						$enabled_settings = Settings::get_role_or_default_setting( TOTP::POLICY_SETTINGS_NAME, $use_role_setting, $role, true );
						?>
						<?php \checked( $enabled_settings, TOTP::POLICY_SETTINGS_NAME ); ?>
					<?php } ?>
					>
					<?php \esc_html_e( 'One-time code via 2FA App (TOTP) - ', 'wp-2fa' ); ?><a href="https://melapress.com/support/kb/wp-2fa-configuring-2fa-apps/?&utm_source=plugin&utm_medium=link&utm_campaign=wp2fa" target="_blank" rel=noopener><?php \esc_html_e( 'complete list of supported 2FA apps.', 'wp-2fa' ); ?></a>
				</label>
				<?php
				if ( $setup_wizard ) {
					echo '<p class="description">';
					printf(
						/* translators: link to the knowledge base website */
						\esc_html__( 'When using this method, users will need to configure a 2FA app to get the one-time login code. The plugin supports all standard 2FA apps. Refer to the %s for more information. Allowing users to set up a secondary 2FA method is highly recommended. You can do this in the next step of the wizard. This will allow users to log in using an alternative method should they, for example lose access to their phone.', 'wp-2fa' ),
						'<a href="https://melapress.com/support/kb/wp-2fa-configuring-2fa-apps/?&utm_source=plugin&utm_medium=link&utm_campaign=wp2fa" target="_blank">' . \esc_html__( 'guide on how to set up 2FA apps', 'wp-2fa' ) . '</a>'
					);
					echo '</p>';
				}
				if ( ! $setup_wizard ) {
					echo '<p class="description">';
					printf(
						/* translators: link to the knowledge base website */
						\esc_html__( 'Refer to the %s for more information on how to setup these apps and which apps are supported.', 'wp-2fa' ),
						'<a href="https://melapress.com/support/kb/wp-2fa-configuring-2fa-apps/?&utm_source=plugin&utm_medium=link&utm_campaign=wp2fa" target="_blank">' . \esc_html__( 'guide on how to set up 2FA apps', 'wp-2fa' ) . '</a>'
					);
					echo '</p>';
				}
				?>
			</div>
			<?php
			$output = ob_get_contents();
			ob_end_clean();

			$methods[ self::get_order( $role, $methods ) ] = $output;

			return $methods;
		}

		/**
		 * Prints the form that prompts the user to authenticate.
		 *
		 * @param \WP_User $user - \WP_User object of the logged-in user.
		 *
		 * @since 2.6.0
		 */
		public static function totp_authentication_page( $user ) {
			require_once ABSPATH . '/wp-admin/includes/template.php';
			?>
			<?php
			if ( 'use-custom' == WP2FA::get_wp2fa_white_label_setting( 'use_custom_2fa_message' ) ) {
				echo WP2FA::get_wp2fa_white_label_setting( 'custom-text-app-code-page', true ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
			} else {
				echo WP2FA::get_wp2fa_white_label_setting( 'default-text-code-page', true ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
			}
			?>
				<p>
					</br>
					<label for="authcode"><?php \esc_html_e( 'Authentication Code:', 'wp-2fa' ); ?></label>
					<input type="tel" name="authcode" id="authcode" class="input" value="" size="20" pattern="[0-9]*" autocomplete="off" />
					<script>
						const authcode = document.getElementById('authcode');
						authcode.addEventListener('input', function() {
						this.value = this.value.trim();
						});
					</script>
				</p>
			<?php
		}
	}
}includes/classes/Admin/Methods/index.php000064400000000046150755130600014264 0ustar00<?php
/**
 * Nothing to see here.
 */
includes/classes/Admin/class-help-contact-us.php000064400000042511150755130600015666 0ustar00<?php
/**
 * Contact us and help rendering class.
 *
 * @package    wp2fa
 * @subpackage admin
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 * @link       https://wordpress.org/plugins/wp-2fa/
 * @since      2.0.0
 */

namespace WP2FA\Admin;

use WP2FA\Admin\Settings_Page;
use WP2FA\Admin\Helpers\WP_Helper;
use WP2FA\WP2FA;

if ( ! class_exists( '\WP2FA\Admin\Help_Contact_Us' ) ) {

	/**
	 * Handles contact us tab and content.
	 */
	class Help_Contact_Us {

		const TOP_MENU_SLUG = 'wp-2fa-help-contact-us';

		/**
		 * Create admin menu entry and settings page
		 */
		public static function add_extra_menu_item() {
			add_submenu_page(
				Settings_Page::TOP_MENU_SLUG,
				\esc_html__( 'Help & Contact Us', 'wp-2fa' ),
				\esc_html__( 'Help & Contact Us', 'wp-2fa' ),
				'manage_options',
				self::TOP_MENU_SLUG,
				array( __CLASS__, 'render' ),
				100
			);
		}

		/**
		 * Handles rendering the help tabs and their wrapping element.
		 *
		 * @return void
		 */
		public static function render() {
			$main_user       = get_current_user_id();
			$current_user_id = $main_user;

			if ( ! empty( WP2FA::get_wp2fa_setting( '2fa_settings_last_updated_by' ) ) ) {
				$main_user = (int) WP2FA::get_wp2fa_setting( '2fa_settings_last_updated_by' );
			}
			?>
			<?php if ( ! empty( WP2FA::get_wp2fa_general_setting( 'limit_access' ) ) && $main_user !== $current_user_id ) : ?>
			</br>
				<?php
				echo \esc_html__( 'These settings have been disabled by your site administrator, please contact them for further assistance.', 'wp-2fa' );
				?>
		<?php else : ?>
			<div class="wrap help-wrap">
				<h2><?php \esc_html_e( 'Help', 'wp-2fa' ); ?></h2>
				<hr>
				<br>
				<div class="nav-tab-wrapper">
					<?php
						// Get current tab.
						$current_tab = isset( $_GET['tab'] ) ? \sanitize_text_field( \wp_unslash( $_GET['tab'] ) ) : 'help'; // phpcs:ignore
					?>
					<a href="<?php echo \esc_url( remove_query_arg( 'tab' ) ); ?>" class="nav-tab<?php echo 'help' === $current_tab ? ' nav-tab-active' : ''; ?>"><?php \esc_html_e( 'Help', 'wp-2fa' ); ?></a>
					<a href="<?php echo \esc_url( add_query_arg( 'tab', 'system-info' ) ); ?>" class="nav-tab<?php echo 'system-info' === $current_tab ? ' nav-tab-active' : ''; ?>"><?php \esc_html_e( 'System info', 'wp-2fa' ); ?></a>
				</div>
				<div class="wp2fa-help-section nav-tabs">
					<?php
						self::sidebar();
					if ( 'help' === $current_tab ) {
						self::help();
					} elseif ( 'system-info' === $current_tab ) {
						self::system_info();
					}
					?>
				</div>
			</div>
		<?php endif; ?>
			<?php
		}

		/**
		 * Help tab content.
		 *
		 * @return void
		 */
		public static function help() {
			?>
		<div class="wp2fa-help-main">
			<!-- getting started -->
			<div class="title">
				<h2><?php \esc_html_e( 'Getting started', 'wp-2fa' ); ?></h2>
			</div>
			<p><?php \esc_html_e( 'Getting started with WP 2FA and making 2FA compulsory is as easy as 1 2 3 with WP 2FA. This can be easily done through the install wizard or the plugin settings. If you are stuck, no problem! Below are a few links of guides to help you get started:', 'wp-2fa' ); ?></p>
			<ul>
				<li><?php echo wp_sprintf( '<a href="%1$s" target="_blank">%2$s</a>', \esc_url( 'https://melapress.com/support/kb/wp-2fa-plugin-getting-started/?&utm_source=plugin&utm_medium=link&utm_campaign=wp2fa' ), \esc_html__( 'Getting started with WP 2FA', 'wp-2fa' ) ); ?></li>
				<li><?php echo wp_sprintf( '<a href="%1$s" target="_blank">%2$s</a>', \esc_url( 'https://melapress.com/support/kb/wp-2fa-configure-2fa-policies-enforce/?&utm_source=plugin&utm_medium=link&utm_campaign=wp2fa' ), \esc_html__( 'Configuring 2FA policies & making 2FA mandatory', 'wp-2fa' ) ); ?></li>
				<li><?php echo wp_sprintf( '<a href="%1$s" target="_blank">%2$s</a>', \esc_url( 'https://melapress.com/support/kb/wp-2fa-configure-2fa-front-end-page-wordpress/?&utm_source=plugin&utm_medium=link&utm_campaign=wp2fa' ), \esc_html__( 'Allowing users to configure 2FA from a website page (no dashboard access)', 'wp-2fa' ) ); ?></li>
			</ul>
			<!-- End -->
			<br>
			<p><iframe title="<?php \esc_html_e( 'Getting started', 'wp-2fa' ); ?>" class="wsal-youtube-embed" width="100%" height="315" src="https://www.youtube.com/embed/vRlX_NNGeFo" frameborder="0" allowfullscreen></iframe></p>

			<!-- Plugin documentation -->
			<div class="title">
				<h2><?php \esc_html_e( 'Plugin documentation', 'wp-2fa' ); ?></h2>
			</div>
			<p><?php \esc_html_e( 'For more technical information about the WP 2FA plugin please visit the plugin\'s knowledge base.', 'wp-2fa' ); ?></p>
			<div class="btn">
				<a href="<?php echo \esc_url( 'https://melapress.com/support/kb/?utm_source=plugin&utm_medium=link&utm_campaign=wp2fa' ); ?>" class="button" target="_blank"><?php \esc_html_e( 'Knowledge base', 'wp-2fa' ); ?></a>
			</div>
			<!-- End -->

			<!-- Plugin support -->
			<div class="title">
				<h2><?php \esc_html_e( 'Plugin support', 'wp-2fa' ); ?></h2>
			</div>
			<p><?php \esc_html_e( 'Do you need assistance with the plugin? Have you noticed or encountered an issue while using WP 2FA, or do you just want to report something to us?', 'wp-2fa' ); ?></p>
			<div class="btn">
				<a href="<?php echo \esc_url( 'https://melapress.com/support/submit-ticket/?utm_source=plugin&utm_medium=link&utm_campaign=wp2fa' ); ?>" class="button" target="_blank"><?php \esc_html_e( 'Open support ticket', 'wp-2fa' ); ?></a>
				<a href="<?php echo \esc_url( 'https://melapress.com/contact/?utm_source=plugin&utm_medium=link&utm_campaign=wp2fa' ); ?>" class="button" target="_blank"><?php \esc_html_e( 'Contact us', 'wp-2fa' ); ?></a>
			</div>
			<!-- End -->
		</div>
			<?php
		}

		/**
		 * System info tab content.
		 *
		 * @return void
		 */
		public static function system_info() {
			?>
		<div class="wp2fa-help-main">
			<!-- getting started -->
			<div class="title">
				<h2><?php \esc_html_e( 'System information', 'wp-2fa' ); ?></h2>
			</div>
			<form method="post" dir="ltr">
				<textarea readonly="readonly" onclick="this.focus(); this.select()" id="system-info-textarea" name="wsal-sysinfo"><?php echo self::get_sysinfo(); // phpcs:ignore ?></textarea>
				<p class="submit">
					<input type="hidden" name="ppmwp-action" value="download_sysinfo" />
					<?php submit_button( 'Download System Info File', 'primary', 'wp2fa-download-sysinfo', false ); ?>
				</p>
			</form>
			<script>

				function download(filename, text) {
					// Create temporary element.
					var element = document.createElement('a');
					element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
					element.setAttribute('download', filename);

					// Set the element to not display.
					element.style.display = 'none';
					document.body.appendChild(element);

					// Simlate click on the element.
					element.click();

					// Remove temporary element.
					document.body.removeChild(element);
				}
				jQuery( document ).ready( function() {
					var download_btn = jQuery( '#wp2fa-download-sysinfo' );
					download_btn.click( function( event ) {
						event.preventDefault();
						download( 'wp2fa-system-info.txt', jQuery( '#system-info-textarea' ).val() );
					} );
				} );
			</script>
		</div>
			<?php
		}

		/**
		 * Advertising sidebar.
		 *
		 * @return void
		 */
		public static function sidebar() {
			?>
		<div class="our-wordpress-plugins side-bar">
			<h3><?php \esc_html_e( 'Our WordPress Plugins', 'wp-2fa' ); ?></h3>
			<ul>
				<li>
					<div class="plugin-box">
						<div class="plugin-img">
							<img src="<?php echo WP_2FA_URL; // phpcs:ignore ?>dist/images/wp-activity-log.jpeg" alt="">
						</div>
						<div class="plugin-desc">
							<p><?php \esc_html_e( 'Keep a log of users and under the hood site activity.', 'wp-2fa' ); ?></p>
							<div class="cta-btn">
								<a href="
								<?php
								echo \esc_url(
									add_query_arg(
										array(
											'utm_source'   => 'plugin',
											'utm_medium'   => 'referral',
											'utm_campaign' => 'WSAL',
											'utm_content'  => 'WP2FA+banner',
										),
										'https://melapress.com/wordpress-activity-log/'
									)
								);
								?>
								" target="_blank"><?php \esc_html_e( 'LEARN MORE', 'wp-2fa' ); ?></a>
							</div>
						</div>
					</div>
				</li>
				<li>
					<div class="plugin-box">
						<div class="plugin-img">
							<img src="<?php echo WP_2FA_URL; // phpcs:ignore ?>dist/images/login-security.jpeg" alt="">
						</div>
						<div class="plugin-desc">
							<p><?php \esc_html_e( 'Enforce strong password policies on WordPress.', 'wp-2fa' ); ?></p>
							<div class="cta-btn">
								<a href="
								<?php
								echo \esc_url(
									add_query_arg(
										array(
											'utm_source'   => 'plugin',
											'utm_medium'   => 'referral',
											'utm_campaign' => 'WSAL',
											'utm_content'  => 'WP2FA+banner',
										),
										'https://melapress.com/wordpress-login-security/?&utm_source=plugin&utm_medium=link&utm_campaign=wp2fa'
									)
								);
								?>
								" target="_blank"><?php \esc_html_e( 'LEARN MORE', 'wp-2fa' ); ?></a>
							</div>
						</div>
					</div>
				</li>
				<li>
					<div class="plugin-box">
						<div class="plugin-img">
							<img src="<?php echo WP_2FA_URL; // phpcs:ignore ?>dist/images/website-file-changes-monitor.jpg" alt="">
						</div>
						<div class="plugin-desc">
							<p><?php \esc_html_e( 'Automatically identify unauthorized file changes on your WordPress site.', 'wp-2fa' ); ?></p>
							<div class="cta-btn">
								<a href="
								<?php
								echo \esc_url(
									add_query_arg(
										array(
											'utm_source'   => 'plugin',
											'utm_medium'   => 'referral',
											'utm_campaign' => 'WSAL',
											'utm_content'  => 'WP2FA+banner',
										),
										'https://melapress.com/wordpress-plugins/website-file-changes-monitor/'
									)
								);
								?>
								" target="_blank"><?php \esc_html_e( 'LEARN MORE', 'wp-2fa' ); ?></a>
							</div>
						</div>
					</div>
				</li>
				<li>
					<div class="plugin-box">
						<div class="plugin-img">
							<img src="<?php echo WP_2FA_URL; // phpcs:ignore ?>dist/images/c4wp.jpg" alt="">
						</div>
						<div class="plugin-desc">
							<p><?php \esc_html_e( 'Protect website forms & login pages from spam bots & automated attacks.', 'wp-2fa' ); ?></p>
							<div class="cta-btn">
								<a href="
								<?php
								echo \esc_url(
									add_query_arg(
										array(
											'utm_source'   => 'plugin',
											'utm_medium'   => 'referral',
											'utm_campaign' => 'WSAL',
											'utm_content'  => 'WP2FA+banner',
										),
										'https://melapress.com/wordpress-captcha/?&utm_source=plugin&utm_medium=link&utm_campaign=wp2fa'
									)
								);
								?>
								" target="_blank"><?php \esc_html_e( 'LEARN MORE', 'wp-2fa' ); ?></a>
							</div>
						</div>
					</div>
				</li>
			</ul>
		</div>
			<?php
		}

		/**
		 * Gather basic settings and system information for use in the system info tab. Left untranslated (as is the case in all plugins)
		 * as its for our use only.
		 *
		 * @return string
		 */
		public static function get_sysinfo() {
			// System info.
			global $wpdb;

			$sysinfo = '### System Info → Begin ###' . "\n\n";

			// Start with the basics...
			$sysinfo .= '-- Site Info --' . "\n\n";
			$sysinfo .= 'Site URL (WP Address):    ' . site_url() . "\n";
			$sysinfo .= 'Home URL (Site Address):  ' . home_url() . "\n";
			$sysinfo .= 'Multisite:                ' . ( WP_Helper::is_multisite() ? 'Yes' : 'No' ) . "\n";

			// Get theme info.
			$theme_data   = wp_get_theme();
			$theme        = $theme_data->name . ' ' . $theme_data->version;
			$parent_theme = $theme_data->template;
			if ( ! empty( $parent_theme ) ) {
				$parent_theme_data = wp_get_theme( $parent_theme );
				$parent_theme      = $parent_theme_data->name . ' ' . $parent_theme_data->version;
			}

			// Language information.
			$locale = get_locale();

			// WordPress configuration.
			$sysinfo .= "\n" . '-- WordPress Configuration --' . "\n\n";
			$sysinfo .= 'Version:                  ' . get_bloginfo( 'version' ) . "\n";
			$sysinfo .= 'Language:                 ' . ( ! empty( $locale ) ? $locale : 'en_US' ) . "\n";
			$sysinfo .= 'Permalink Structure:      ' . ( get_option( 'permalink_structure' ) ? get_option( 'permalink_structure' ) : 'Default' ) . "\n";
			$sysinfo .= 'Active Theme:             ' . $theme . "\n";
			if ( $parent_theme !== $theme ) {
				$sysinfo .= 'Parent Theme:             ' . $parent_theme . "\n";
			}
			$sysinfo .= 'Show On Front:            ' . get_option( 'show_on_front' ) . "\n";

			// Only show page specs if frontpage is set to 'page'.
			if ( 'page' === get_option( 'show_on_front' ) ) {
				$front_page_id = (int) get_option( 'page_on_front' );
				$blog_page_id  = (int) get_option( 'page_for_posts' );

				$sysinfo .= 'Page On Front:            ' . ( 0 !== $front_page_id ? get_the_title( $front_page_id ) . ' (#' . $front_page_id . ')' : 'Unset' ) . "\n";
				$sysinfo .= 'Page For Posts:           ' . ( 0 !== $blog_page_id ? get_the_title( $blog_page_id ) . ' (#' . $blog_page_id . ')' : 'Unset' ) . "\n";
			}

			$sysinfo .= 'ABSPATH:                  ' . ABSPATH . "\n";
			$sysinfo .= 'WP_DEBUG:                 ' . ( defined( 'WP_DEBUG' ) ? ( WP_DEBUG ? 'Enabled' : 'Disabled' ) : 'Not set' ) . "\n";
			$sysinfo .= 'WP Memory Limit:          ' . WP_MEMORY_LIMIT . "\n";

			// Get plugins that have an update.
			$updates = get_plugin_updates();

			// Must-use plugins.
			// NOTE: MU plugins can't show updates!
			$muplugins = get_mu_plugins();
			if ( count( $muplugins ) > 0 ) {
				$sysinfo .= "\n" . '-- Must-Use Plugins --' . "\n\n";

				foreach ( $muplugins as $plugin => $plugin_data ) {
					$sysinfo .= $plugin_data['Name'] . ': ' . $plugin_data['Version'] . "\n";
				}
			}

			// WordPress active plugins.
			$sysinfo .= "\n" . '-- WordPress Active Plugins --' . "\n\n";

			$plugins        = get_plugins();
			$active_plugins = get_option( 'active_plugins', array() );

			foreach ( $plugins as $plugin_path => $plugin ) {
				if ( ! in_array( $plugin_path, $active_plugins, true ) ) {
					continue;
				}

				$update   = ( array_key_exists( $plugin_path, $updates ) ) ? ' (needs update - ' . $updates[ $plugin_path ]->update->new_version . ')' : '';
				$sysinfo .= $plugin['Name'] . ': ' . $plugin['Version'] . $update . "\n";
			}

			// WordPress inactive plugins.
			$sysinfo .= "\n" . '-- WordPress Inactive Plugins --' . "\n\n";

			foreach ( $plugins as $plugin_path => $plugin ) {
				if ( in_array( $plugin_path, $active_plugins, true ) ) {
					continue;
				}

				$update   = ( array_key_exists( $plugin_path, $updates ) ) ? ' (needs update - ' . $updates[ $plugin_path ]->update->new_version . ')' : '';
				$sysinfo .= $plugin['Name'] . ': ' . $plugin['Version'] . $update . "\n";
			}

			if ( WP_Helper::is_multisite() ) {
				// WordPress Multisite active plugins.
				$sysinfo .= "\n" . '-- Network Active Plugins --' . "\n\n";

				$plugins        = wp_get_active_network_plugins();
				$active_plugins = get_site_option( 'active_sitewide_plugins', array() );

				foreach ( $plugins as $plugin_path ) {
					$plugin_base = plugin_basename( $plugin_path );

					if ( ! array_key_exists( $plugin_base, $active_plugins ) ) {
						continue;
					}

					$update   = ( array_key_exists( $plugin_path, $updates ) ) ? ' (needs update - ' . $updates[ $plugin_path ]->update->new_version . ')' : '';
					$plugin   = get_plugin_data( $plugin_path );
					$sysinfo .= $plugin['Name'] . ': ' . $plugin['Version'] . $update . "\n";
				}
			}

			// Server configuration.
			$server_software = ( isset( $_SERVER['SERVER_SOFTWARE'] ) ) ? \sanitize_text_field( \wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) ) : '';
			$sysinfo        .= "\n" . '-- Webserver Configuration --' . "\n\n";
			$sysinfo        .= 'PHP Version:              ' . PHP_VERSION . "\n";
			$sysinfo        .= 'MySQL Version:            ' . $wpdb->db_version() . "\n";

			if ( isset( $server_software ) ) {
				$sysinfo .= 'Webserver Info:           ' . $server_software . "\n";
			} else {
				$sysinfo .= 'Webserver Info:           Global $_SERVER array is not set.' . "\n";
			}

			// PHP configs.
			$sysinfo .= "\n" . '-- PHP Configuration --' . "\n\n";
			$sysinfo .= 'Memory Limit:             ' . ini_get( 'memory_limit' ) . "\n";
			$sysinfo .= 'Upload Max Size:          ' . ini_get( 'upload_max_filesize' ) . "\n";
			$sysinfo .= 'Post Max Size:            ' . ini_get( 'post_max_size' ) . "\n";
			$sysinfo .= 'Upload Max Filesize:      ' . ini_get( 'upload_max_filesize' ) . "\n";
			$sysinfo .= 'Time Limit:               ' . ini_get( 'max_execution_time' ) . "\n";
			$sysinfo .= 'Max Input Vars:           ' . ini_get( 'max_input_vars' ) . "\n";
			$sysinfo .= 'Display Errors:           ' . ( ini_get( 'display_errors' ) ? 'On (' . ini_get( 'display_errors' ) . ')' : 'N/A' ) . "\n";

			$sysinfo .= "\n" . '-- WP 2FA Settings  --' . "\n\n";

			global $wpdb;

			$wp2fa_options = $wpdb->get_results( "SELECT option_name, option_value FROM $wpdb->options WHERE option_name LIKE 'wp_2fa_%'", ARRAY_A ); // phpcs:ignore

			if ( ! empty( $wp2fa_options ) ) {
				foreach ( $wp2fa_options as $option => $value ) {
					$sysinfo .= 'Option: ' . $value['option_name'] . "\n";
					$sysinfo .= 'Value: ' . print_r( $value['option_value'], true ) . "\n\n"; // phpcs:ignore
				}
			}

			$sysinfo .= "\n" . '### System Info → End ###' . "\n\n";

			return $sysinfo;
		}
	}
}
includes/classes/Admin/class-setup-wizard.php000064400000057060150755130600015323 0ustar00<?php
/**
 * Setup wizard rendering class.
 *
 * @package    wp2fa
 * @subpackage setup
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 * @link       https://wordpress.org/plugins/wp-2fa/
 */

namespace WP2FA\Admin;

use WP2FA\Core;
use WP2FA\WP2FA;
use WP2FA\Methods\TOTP;
use WP2FA\Methods\Email;
use WP2FA\Utils\User_Utils;
use WP2FA\Admin\Settings_Page;
use WP2FA\Methods\Backup_Codes;
use WP2FA\Utils\Generate_Modal;
use WP2FA\Utils\Settings_Utils;
use WP2FA\Admin\Helpers\WP_Helper;
use WP2FA\Admin\Views\Re_Login_2FA;
use WP2FA\Admin\Views\Wizard_Steps;
use WP2FA\Admin\Helpers\User_Helper;
use WP2FA\Admin\Controllers\Settings;
use WP2FA\Authenticator\Authentication;
use WP2FA\Admin\Views\First_Time_Wizard_Steps;
use WP2FA\Admin\SettingsPages\Settings_Page_Policies;

/**
 * Setup_Wizard class for the wizard steps setup
 *
 * @since 2.4.0
 */
if ( ! class_exists( '\WP2FA\Admin\Setup_Wizard' ) ) {
	/**
	 * Our class for creating a step by step wizard for easy configuration.
	 */
	class Setup_Wizard {

		/**
		 * Wizard Steps
		 *
		 * @var array
		 *
		 * @since 2.8.0
		 */
		private static $wizard_steps;

		/**
		 * Current Step
		 *
		 * @var string
		 *
		 * @since 2.8.0
		 */
		private static $current_step;

		/**
		 * Add setup admin page. This is empty on purpose.
		 *
		 * @since 2.8.0
		 */
		public static function admin_menus() {
			\add_dashboard_page( '', '', 'read', 'wp-2fa-setup', '' );
		}

		/**
		 * Adding menus for multisite install
		 *
		 * @return void
		 *
		 * @since 2.2.0
		 */
		public static function network_admin_menus() {
			\add_dashboard_page( 'index.php', '', 'read', 'wp-2fa-setup', '' );
		}

		/**
		 * Setup Page Start.
		 *
		 * @since 2.8.0
		 */
		public static function setup_page() {

			// Get page argument from $_GET array.
			$page = ( isset( $_GET['page'] ) ) ? \sanitize_text_field( \wp_unslash( $_GET['page'] ) ) : ''; // phpcs:ignore
			if ( empty( $page ) || 'wp-2fa-setup' !== $page ) {
				return;
			}

			// Clear out any old notices.
			$user = \wp_get_current_user();

			// First lets check if any options have been saved.
			$settings_saved = true;
			$settings       = WP2FA::get_wp2fa_setting();
			if ( empty( $settings ) || ! isset( $settings ) ) {
				$settings_saved = false;
			}

			if ( Settings_Utils::get_option( 'wizard_not_finished' ) ) {
				$settings_saved = false;
			}

			/**
			 * Wizard Steps.
			 */
			$get_array = filter_input_array( INPUT_GET );
			if ( isset( $get_array['wizard_type'] ) ) {
				$wizard_type = \sanitize_text_field( $get_array['wizard_type'] );
			} else {
				$wizard_type = 'default';
			}

			$is_user_forced_to_setup = User_Helper::get_user_enforced_instantly( $user );
			if ( ! empty( $is_user_forced_to_setup ) ) {
				\add_filter( 'wp_2fa_wizard_default_steps', array( __CLASS__, 'wp_2fa_add_intro_step' ) );
			}

			$user_type = User_Utils::determine_user_2fa_status( $user );

			$wizard_steps = array(
				'welcome'                => array(
					'name'        => \esc_html__( 'Welcome', 'wp-2fa' ),
					'content'     => array( __CLASS__, 'wp_2fa_step_welcome' ),
					'wizard_type' => 'welcome_wizard',
				),
				'settings_configuration' => array(
					'name'        => \esc_html__( 'Configure 2FA methods & Policies', 'wp-2fa' ),
					'content'     => array( __CLASS__, 'wp_2fa_step_global_2fa_methods' ),
					'save'        => array( __CLASS__, 'wp_2fa_step_global_2fa_methods_save' ),
					'wizard_type' => 'welcome_wizard',
				),
				'finish'                 => array(
					'name'        => \esc_html__( 'Setup Finish', 'wp-2fa' ),
					'content'     => array( __CLASS__, 'wp_2fa_step_finish' ),
					'save'        => array( __CLASS__, 'wp_2fa_step_finish_save' ),
					'wizard_type' => 'welcome_wizard',
				),
			);

			// Admin user setting up fresh install of 2FA plugin.
			if ( in_array( 'can_manage_options', $user_type, true ) && ! $settings_saved ) {
				unset( $wizard_steps['user_choose_2fa_method'] );
				unset( $wizard_steps['reconfigure_method'] );
			}

			// We will use this setting to determine if defaults have already been saved to the DB.
			$have_defaults_been_applied = Settings_Utils::get_option( 'default_settings_applied', false );
			// If we have settings, but they are the defaults, then we want to consider the settings to be unsaved at this point.
			if ( in_array( 'can_manage_options', $user_type, true ) && $settings_saved && $have_defaults_been_applied ) {
				$settings_saved = false;
			}

			// Ensure user has minimum capabilities needed to be here.
			if ( in_array( 'can_read', $user_type, true ) && $settings_saved ) {

				switch ( $wizard_type ) {
					case 'user_2fa_config':
						$wizard_steps = array_intersect_key( $wizard_steps, array_flip( array( 'user_choose_2fa_method', 'setup_method', 'finish', 'backup_codes' ) ) );
						break;

					case 'backup_codes_config':
						$wizard_steps = array_intersect_key( $wizard_steps, array_flip( array( 'backup_codes' ) ) );
						break;

					case 'user_reconfigure_config':
						$wizard_steps = array_intersect_key( $wizard_steps, array_flip( array( 'reconfigure_method' ) ) );
						break;

					default:
						$wizard_steps = array_intersect_key( $wizard_steps, array_flip( array( 'choose_2fa_method', 'setup_method', 'finish', 'backup_codes', 'reconfigure_method' ) ) );
				}

				// Remove 1st step if only one method is available.
				if ( empty( WP2FA::get_wp2fa_setting( TOTP::POLICY_SETTINGS_NAME ) ) || empty( WP2FA::get_wp2fa_setting( Email::POLICY_SETTINGS_NAME ) ) ) {
					unset( $wizard_steps['choose_2fa_method'] );
				}

				// If the user has codes setup already, no need to add the slide.
				if ( ! in_array( 'user_needs_to_setup_backup_codes', $user_type, true ) && 'backup_codes_config' !== $wizard_type ) {
					unset( $wizard_steps['backup_codes'] );
				}
			}

			/**
			 * Filter: `Wizard Default Steps`
			 *
			 * Filter to filter wizard steps before they are displayed.
			 *
			 * @param array $wizard_steps – Wizard Steps.
			 *
			 * @since 2.8.0
			 */
			self::$wizard_steps = apply_filters( WP_2FA_PREFIX . 'wizard_default_steps', $wizard_steps );

			// Set current step.
			$current_step       = ( isset( $_GET['current-step'] ) ) ? \sanitize_text_field( \wp_unslash( $_GET['current-step'] ) ) : ''; // phpcs:ignore
			self::$current_step = ! empty( $current_step ) ? $current_step : current( array_keys( self::$wizard_steps ) );

			if ( Backup_Codes::METHOD_NAME === self::$current_step && ! Backup_Codes::are_backup_codes_enabled_for_role( User_Helper::get_user_role( $user ) ) ) {

				$redirect_to_finish = add_query_arg(
					array(
						'current-step' => 'finish',
						'all-set'      => 1,
					)
				);
				\wp_safe_redirect( \esc_url_raw( $redirect_to_finish ) );
			}

			/**
			 * Enqueue Scripts.
			 */
			\wp_enqueue_style(
				'wp_2fa_setup_wizard',
				Core\style_url( 'setup-wizard', 'admin' ),
				array( 'select2' ),
				WP_2FA_VERSION
			);

			\wp_enqueue_style(
				'wp_2fa_admin-style',
				Core\style_url( 'admin-style', 'admin' ),
				array(),
				WP_2FA_VERSION
			);

			\WP2FA\Core\enqueue_select2_scripts();

			\wp_enqueue_script(
				'wp_2fa_admin',
				Core\script_url( 'admin', 'admin' ),
				array( 'jquery-ui-widget', 'jquery-ui-core', 'jquery-ui-autocomplete', 'select2' ),
				WP_2FA_VERSION,
				true
			);

			\wp_enqueue_script(
				'wp_2fa_micromodal',
				Core\script_url( 'micromodal', 'admin', 'select2' ),
				array(),
				WP_2FA_VERSION,
				true
			);

			// Data array.
			$data_array = array(
				'ajaxURL'         => \admin_url( 'admin-ajax.php' ),
				'roles'           => WP_Helper::get_roles_wp(),
				'nonce'           => \wp_create_nonce( 'wp-2fa-settings-nonce' ),
				'invalidEmail   ' => \esc_html__( 'Please use a valid email address', 'wp-2fa' ),
				'backupCodesSent' => \esc_html__( 'Backup codes sent', 'wp-2fa' ),
			);
			\wp_localize_script( 'wp_2fa_admin', 'wp2faData', $data_array );

			$re_login = Settings::get_role_or_default_setting( Re_Login_2FA::RE_LOGIN_SETTINGS_NAME, 'current', User_Helper::get_user_role() );

			// Data array.
			$data_array = array(
				'ajaxURL'        => \admin_url( 'admin-ajax.php' ),
				'nonce'          => \wp_create_nonce( 'wp2fa-verify-wizard-page' ),
				'codesPreamble'  => \esc_html__( 'These are the 2FA backup codes for the user', 'wp-2fa' ),
				'readyText'      => \esc_html__( 'I\'m ready', 'wp-2fa' ),
				'codeReSentText' => \esc_html__( 'New code sent', 'wp-2fa' ),
				'reLogin'        => $re_login,
				'reLoginEnabled' => Re_Login_2FA::ENABLED_SETTING_VALUE,
			);

			/**
			 * Gives the ability to change the default JS wizard settings.
			 *
			 * @param int $data_array - The array with all the JS wizard settings.
			 *
			 * @since 2.2.0
			 */
			$data_array = apply_filters( WP_2FA_PREFIX . 'js_wizard_settings', $data_array );
			\wp_localize_script( 'wp_2fa_admin', 'wp2faWizardData', $data_array );

			/**
			 * Save Wizard Settings.
			 */
			$save_step = ( isset( $_POST['save_step'] ) ) ? \sanitize_text_field( \wp_unslash( $_POST['save_step'] ) ) : ''; // phpcs:ignore
			if ( ! empty( $save_step ) && ! empty( self::$wizard_steps[ self::$current_step ]['save'] ) ) {
				call_user_func( self::$wizard_steps[ self::$current_step ]['save'] );
			}

			self::setup_page_header();
			self::setup_page_steps();
			self::setup_page_content();
			self::setup_page_footer();

			exit();
		}

		/**
		 * Setup Page Header.
		 *
		 * @since 2.8.0
		 */
		private static function setup_page_header() {
			?>
				<!DOCTYPE html>
				<html <?php language_attributes(); ?>>
				<head>
					<meta name="viewport" content="width=device-width" />
					<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
					<title><?php \esc_html_e( 'WP 2FA &rsaquo; Setup Wizard', 'wp-2fa' ); ?></title>
					<?php \wp_print_scripts( 'jquery' ); ?>
					<?php \wp_print_scripts( 'jquery-ui-core' ); ?>
					<?php \wp_print_scripts( 'wp_2fa_setup_wizard' ); ?>
					<?php \wp_print_scripts( 'wp_2fa_micromodal' ); ?>
					<?php \wp_print_scripts( 'wp_2fa_admin' ); ?>
					<?php
						/**
						 * Gives the ability for 3rd party scripts to add their own JS to the plugin setup page.
						 *
						 * @since 2.2.0
						 */
						\do_action( WP_2FA_PREFIX . 'setup_page_scripts' );
					?>
					<?php \wp_print_styles( 'common' ); ?>
					<?php \wp_print_styles( 'forms' ); ?>
					<?php \wp_print_styles( 'buttons' ); ?>
					<?php \wp_print_styles( 'wp-jquery-ui-dialog' ); ?>
					<?php \wp_print_styles( 'wp_2fa_admin' ); ?>
					<?php \do_action( 'admin_print_styles' ); ?>
				</head>
				<body class="wp2fa-setup wp-core-ui">
					<div class="setup-wizard-wrapper wp-2fa-settings-wrapper wp2fa-form-styles">
						<h1 id="wp2fa-logo"><a href="https://melapress.com/wordpress-2fa/?&utm_source=plugin&utm_medium=link&utm_campaign=wp2fa" target="_blank"><img style="max-width: 80px;" src="<?php echo \esc_url( WP_2FA_URL . 'dist/images/wp-2fa-color_opt.png' ); ?>"></a></h1>
			<?php
		}

		/**
		 * Setup Page Footer.
		 *
		 * @since 2.8.0
		 */
		private static function setup_page_footer() {
			$user = \wp_get_current_user();

			$redirect = Settings::get_settings_page_link();
			?>
				<div class="wp2fa-setup-footer">
					<?php if ( 'welcome' !== self::$current_step && 'finish' !== self::$current_step ) { // Don't show the link on the first & last step. ?>
						<?php if ( ! User_Helper::get_user_enforced_instantly( $user ) ) { ?>
							<a class="close-wizard-link" href="<?php echo \esc_url( $redirect ); ?>"><?php \esc_html_e( 'Close Wizard', 'wp-2fa' ); ?></a>
							<?php
						}
					}
					?>
				</div>
			</div>
			</body>
			</html>
				<?php
				// phpcs:ignore
				echo Generate_Modal::generate_modal(
					'notify-admin-settings-page',
					'',
					__( 'If you cancel this wizard, the default plugin settings will be applied. You can always configure the plugin settings and two-factor authentication policies at a later stage from the ', 'wp-2fa' ) . ' <b>' . __( 'WP 2FA', 'wp-2fa' ) . '</b>' . __( ' entry in your WordPress dashboard menu.', 'wp-2fa' ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
					array(
						'<a href="#" id="close-settings" class="button button-primary wp-2fa-button-primary" data-redirect-url="' . \esc_url( $redirect ) . '">' . __( 'OK, close wizard', 'wp-2fa' ) . '</a>', // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
						'<a href="#" class="button button-secondary wp-2fa-button-secondary wp-2fa-button-secondary" data-close-2fa-modal>' . __( 'Continue with wizard', 'wp-2fa' ) . '</a>', // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
					),
					'',
					'580px'
				);
		}

		/**
		 * Setup Page Steps.
		 *
		 * @since 2.8.0
		 */
		private static function setup_page_steps() {
			?>
			<ul class="steps">
				<?php
				foreach ( self::$wizard_steps as $key => $step ) {
					if ( 'welcome_wizard' === $step['wizard_type'] || is_array( $step['wizard_type'] ) && in_array( 'welcome_wizard', $step['wizard_type'], true ) ) {
						if ( $key === self::$current_step ) {
							?>
							<li class="is-active"><?php echo \esc_html( $step['name'] ); ?></li>
							<?php
						} else {
							?>
							<li><?php echo \esc_html( $step['name'] ); ?></li>
							<?php
						}
					}
				}
				?>
			</ul>
			<?php
		}

		/**
		 * Get Next Step URL.
		 *
		 * @return string
		 *
		 * @since 2.8.0
		 */
		private static function get_next_step() {
			// Get current step.
			$current_step = self::$current_step;

			// Array of step keys.
			$keys = array_keys( self::$wizard_steps );
			if ( end( $keys ) === $current_step ) { // If last step is active then return WP Admin URL.
				return admin_url();
			}

			// Search for step index in step keys.
			$step_index = array_search( $current_step, $keys, true );
			if ( false === $step_index ) { // If index is not found then return empty string.
				return '';
			}

			// Return next step.
			return add_query_arg( 'current-step', $keys[ $step_index + 1 ] );
		}

		/**
		 * Setup Page Content.
		 *
		 * @since 2.8.0
		 */
		private static function setup_page_content() {
			?>
			<div class="wp2fa-setup-content">
				<?php
				if ( ! empty( self::$wizard_steps[ self::$current_step ]['content'] ) ) {
					call_user_func( self::$wizard_steps[ self::$current_step ]['content'] );
				}
				?>
			</div>
			<?php
		}

		/**
		 * Step View: `Welcome`
		 *
		 * @since 2.8.0
		 */
		private static function wp_2fa_step_welcome() {
			Wizard_Steps::welcome_step( self::get_next_step() );
		}

		/**
		 * Step View: `Finish`
		 *
		 * @since 2.8.0
		 */
		private static function wp_2fa_step_finish() {
			User_Helper::remove_user_needs_to_reconfigure_2fa( User_Helper::get_user_object() );
			Wizard_Steps::congratulations_step( true );
		}

		/**
		 * Step Save: `Finish`
		 *
		 * @since 2.8.0
		 */
		private static function wp_2fa_step_finish_save() {
			// Verify nonce.
			\check_admin_referer( 'wp2fa-step-finish' );
			\wp_safe_redirect( \esc_url_raw( self::get_next_step() ) );

			exit();
		}

		/**
		 * Step View: `Choose Methods`
		 *
		 * @since 2.8.0
		 */
		private static function wp_2fa_step_global_2fa_methods() {
			?>
				<form method="post" class="wp2fa-setup-form wp2fa-form-styles wp2fa-first-time-wizard" autocomplete="off">
					<?php wp_nonce_field( 'wp2fa-step-choose-method' ); ?>
					<div class="step-setting-wrapper active" data-step-title="<?php \esc_html_e( '2FA methods', 'wp-2fa' ); ?>">
						<?php First_Time_Wizard_Steps::select_method( true ); ?>
						<div class="wp2fa-setup-actions">
							<a class="button button-primary" name="next_step_setting" value="<?php \esc_attr_e( 'Continue Setup', 'wp-2fa' ); ?>"><?php \esc_html_e( 'Continue Setup', 'wp-2fa' ); ?></a>
						</div>
					</div>
					<div class="step-setting-wrapper" data-step-title="<?php \esc_html_e( 'Alternative methods', 'wp-2fa' ); ?>">
						<?php First_Time_Wizard_Steps::backup_method( true ); ?>
						<div class="wp2fa-setup-actions">
							<a class="button button-primary" name="next_step_setting" value="<?php \esc_attr_e( 'Continue Setup', 'wp-2fa' ); ?>"><?php \esc_html_e( 'Continue Setup', 'wp-2fa' ); ?></a>
						</div>
					</div>
					<div class="step-setting-wrapper" data-step-title="<?php \esc_html_e( '2FA policy', 'wp-2fa' ); ?>">
						<?php First_Time_Wizard_Steps::enforcement_policy( true ); ?>
						<div class="wp2fa-setup-actions">
							<a class="button button-primary continue-wizard hidden" name="next_step_setting" value="<?php \esc_attr_e( 'Continue Setup', 'wp-2fa' ); ?>"><?php \esc_html_e( 'Continue Setup', 'wp-2fa' ); ?></a>
							<button class="button button-primary save-wizard" type="submit" name="save_step" value="<?php \esc_attr_e( 'All done', 'wp-2fa' ); ?>"><?php \esc_html_e( 'All done', 'wp-2fa' ); ?></button>
						</div>
					</div>
					<div class="step-setting-wrapper hidden" data-step-title="<?php \esc_html_e( 'Exclude users', 'wp-2fa' ); ?>">
					<?php First_Time_Wizard_Steps::exclude_users( true ); ?>
						<div class="wp2fa-setup-actions">
							<a class="button button-primary" name="next_step_setting" value="<?php \esc_attr_e( 'Continue Setup', 'wp-2fa' ); ?>"><?php \esc_html_e( 'Continue Setup', 'wp-2fa' ); ?></a>
						</div>
					</div>

					<?php if ( WP_Helper::is_multisite() ) : ?>
						<div class="step-setting-wrapper" data-step-title="<?php \esc_html_e( 'Exclude sites', 'wp-2fa' ); ?>">
						<?php First_Time_Wizard_Steps::excluded_network_sites( true ); ?>
							<div class="wp2fa-setup-actions">
								<a class="button button-primary" name="next_step_setting" value="<?php \esc_attr_e( 'Continue Setup', 'wp-2fa' ); ?>"><?php \esc_html_e( 'Continue Setup', 'wp-2fa' ); ?></a>
							</div>
						</div>
					<?php endif; ?>

					<div class="step-setting-wrapper hidden" data-step-title="<?php \esc_html_e( 'Grace period', 'wp-2fa' ); ?>">
						<h3><?php \esc_html_e( 'How long should the grace period for your users be?', 'wp-2fa' ); ?></h3>
						<p class="description"><?php \esc_html_e( 'When you configure the 2FA policies and require users to configure 2FA, they can either have a grace period to configure 2FA, or can be required to configure 2FA before the next time they login. Choose which method you\'d like to use:', 'wp-2fa' ); ?></p>
						<?php First_Time_Wizard_Steps::grace_period( true ); ?>
						<div class="wp2fa-setup-actions">
							<button class="button button-primary save-wizard" type="submit" name="save_step" value="<?php \esc_attr_e( 'All done', 'wp-2fa' ); ?>"><?php \esc_html_e( 'All done', 'wp-2fa' ); ?></button>
						</div>
					</div>

				</form>
			<?php
		}

		/**
		 * Step Save: `Choose Method`
		 *
		 * @since 2.8.0
		 */
		private static function wp_2fa_step_global_2fa_methods_save() {
			// Check nonce.
			\check_admin_referer( 'wp2fa-step-choose-method' );

			$input = ( isset( $_POST[ WP_2FA_POLICY_SETTINGS_NAME ] ) && ! empty( $_POST[ WP_2FA_POLICY_SETTINGS_NAME ] ) && \is_array( $_POST[ WP_2FA_POLICY_SETTINGS_NAME ] ) ) ? $_POST[ WP_2FA_POLICY_SETTINGS_NAME ] : array();

			$input = \map_deep( $input, 'wp_unslash' );
			$input = \map_deep( $input, 'sanitize_text_field' );

			if ( ! WP_Helper::is_multisite() ) {
				\unregister_setting(
					WP_2FA_POLICY_SETTINGS_NAME,
					WP_2FA_POLICY_SETTINGS_NAME
				);
			}

			$sanitized_settings = Settings_Page_Policies::validate_and_sanitize( $input, 'setup_wizard' );
			WP2FA::update_plugin_settings( $sanitized_settings );

			\wp_safe_redirect( \esc_url_raw( self::get_next_step() ) );
			exit();
		}

		/**
		 * Send email with fresh code, or to setup email 2fa.
		 *
		 * @param int    $user_id - User id we want to send the message to.
		 * @param string $nominated_email_address - The user custom address to use (name of the meta key to check for).
		 * @param bool   $is_reset_protection - That call is for reset code.
		 *
		 * @return bool
		 *
		 * @since 2.8.0
		 */
		public static function send_authentication_setup_email( $user_id, $nominated_email_address = 'nominated_email_address', $is_reset_protection = false ) {

			// If we have a nonce posted, check it.
			if ( \wp_doing_ajax() && isset( $_POST['nonce'] ) ) {
				$nonce_check = \wp_verify_nonce( \sanitize_text_field( \wp_unslash( $_POST['nonce'] ) ), 'wp-2fa-send-setup-email' );
				if ( ! $nonce_check ) {
					\wp_send_json_error( new \WP_Error( 400, \esc_html__( 'Nonce checking failed', 'wp-2fa' ) ), 400 );
					return false;
				}
			}

			if ( isset( $_POST['user_id'] ) ) {
				$user = get_userdata( intval( $_POST['user_id'] ) );
			} else {
				$user = get_userdata( $user_id );
			}

			// Grab email address is its provided.
			if ( isset( $_POST['email_address'] ) ) {
				$email = sanitize_email( \wp_unslash( $_POST['email_address'] ) );
			} else {
				$email = sanitize_email( $user->user_email );
			}

			if ( wp_doing_ajax() && isset( $_POST['nonce'] ) ) {
				User_Helper::set_nominated_email_for_user( $email, $user );
			}

			$email_address = '';
			if ( ! empty( $nominated_email_address ) ) {
				if ( 'nominated_email_address' === $nominated_email_address ) {
					$email_address = User_Helper::get_nominated_email_for_user( $user );
				} elseif ( 'backup_email_address' === $nominated_email_address ) {
					$email_address = User_Helper::get_backup_email_for_user( $user );
				}
			} else {
				$email_address = $user->user_email;
			}

			// Generate a token and setup email.
			$token = Authentication::generate_token( $user->ID );


			if ( $is_reset_protection ) {
				$subject = wp_strip_all_tags( WP2FA::replace_email_strings( WP2FA::get_wp2fa_email_templates( 'reset_password_code_email_subject' ), $user->ID ) );
				$message = wpautop( WP2FA::replace_email_strings( WP2FA::get_wp2fa_email_templates( 'reset_password_code_email_body' ), $user->ID, $token ) );
			} elseif ( wp_doing_ajax() && isset( $_POST['nonce'] ) ) {
				$subject = wp_strip_all_tags( WP2FA::replace_email_strings( WP2FA::get_wp2fa_email_templates( 'login_code_setup_email_subject' ), $user->ID ) );
				$message = wpautop( WP2FA::replace_email_strings( WP2FA::get_wp2fa_email_templates( 'login_code_setup_email_body' ), $user->ID, $token ) );
			} else {
				$subject = wp_strip_all_tags( WP2FA::replace_email_strings( WP2FA::get_wp2fa_email_templates( 'login_code_email_subject' ), $user->ID ) );
				$message = wpautop( WP2FA::replace_email_strings( WP2FA::get_wp2fa_email_templates( 'login_code_email_body' ), $user->ID, $token ) );
			}

			// If we have a nonce posted, check it.
			if ( \wp_doing_ajax() && isset( $_POST['nonce'] ) ) {
				$mail_sent = Settings_Page::send_email( $email_address, $subject, $message );
				if ( ! $mail_sent ) {
					\wp_send_json_error( new \WP_Error( 500, \esc_html__( 'Email sending failed', 'wp-2fa' ) ), 400 );
					return false;
				}

				return $mail_sent;
			}

			return Settings_Page::send_email( $email_address, $subject, $message );
		}

		/**
		 * 3rd Party plugins
		 *
		 * @param array $wizard_steps - Array with the current wizard steps.
		 *
		 * @return array
		 *
		 * @since 2.8.0
		 */
		public static function wp_2fa_add_intro_step( $wizard_steps ) {
			$new_wizard_steps = array(
				'test' => array(
					'name'        => __( 'Welcome to WP 2FA', 'wp-2fa' ),
					'content'     => array( __CLASS__, 'introduction_step' ),
					'save'        => array( __CLASS__, 'introduction_step_save' ),
					'wizard_type' => 'welcome_wizard',
				),
			);

			// combine the two arrays.
			$wizard_steps = $new_wizard_steps + $wizard_steps;

			return $wizard_steps;
		}

		/**
		 * Shows introduction step of the wizard
		 *
		 * @return void
		 *
		 * @since 2.8.0
		 */
		private static function introduction_step() {
			Wizard_Steps::introduction_step();
		}

		/**
		 * Step Save: `Addons`
		 *
		 * @since 2.8.0
		 */
		private static function introduction_step_save() {
			// Check nonce.
			check_admin_referer( 'wp2fa-step-addon' );

			wp_safe_redirect( \esc_url_raw( self::get_next_step() ) );
			exit();
		}
	}
}
includes/classes/class-wp2fa.php000064400000164715150755130600012662 0ustar00<?php
/**
 * Main plugin class.
 *
 * @package    wp2fa
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 * @link       https://wordpress.org/plugins/wp-2fa/
 */

namespace WP2FA;

use WP2FA\Admin\User_Listing;
use WP2FA\Admin\User_Notices;
use WP2FA\Admin\FlyOut\FlyOut;
use WP2FA\Admin\Settings_Page;
use WP2FA\Utils\Request_Utils;
use WP2FA\Utils\Settings_Utils;
use WP2FA\Shortcodes\Shortcodes;
use WP2FA\Utils\Date_Time_Utils;
use WP2FA\Authenticator\Open_SSL;
use WP2FA\Admin\Helpers\WP_Helper;
use WP2FA\Freemius\User_Licensing;
use WP2FA\Admin\Views\Re_Login_2FA;
use WP2FA\Admin\Controllers\Methods;
use WP2FA\Admin\Helpers\File_Writer;
use WP2FA\Admin\Helpers\User_Helper;
use WP2FA\Admin\Controllers\Settings;
use WP2FA\Admin\Plugin_Updated_Notice;
use WP2FA\Admin\Helpers\Classes_Helper;
use WP2FA\Admin\Helpers\Methods_Helper;
use WP2FA\Admin\Views\Password_Reset_2FA;
use WP2FA\Admin\Views\Grace_Period_Notifications;

if ( ! class_exists( '\WP2FA\WP2FA' ) ) {
	/**
	 * Main WP2FA Class.
	 */
	class WP2FA {

		/**
		 * Holds the global plugin secret key
		 *
		 * @var string
		 *
		 * @since 2.0.0
		 */
		private static $secret_key = null;

		/**
		 * Local static cache for plugins settings.
		 *
		 * @var array
		 *
		 * @since 2.0.0
		 */
		private static $plugin_settings = array();

		/**
		 * Local static cache for plugins settings.
		 *
		 * @var array
		 *
		 * @since 2.8.0
		 */
		private static $default_settings = array();

		/**
		 * Local static cache for email template settings.
		 *
		 * @var array
		 */
		protected static $wp_2fa_email_templates;

		/**
		 * Array with all the plugin default settings.
		 *
		 * @return array
		 *
		 * @since 2.2.0
		 */
		public static function get_default_settings() {
			if ( empty( self::$default_settings ) ) {
				self::$default_settings = array(
					'enforcement-policy'                   => 'do-not-enforce',
					'excluded_users'                       => array(),
					'excluded_roles'                       => array(),
					'enforced_users'                       => array(),
					'enforced_roles'                       => array(),
					'grace-period'                         => 3,
					'grace-period-denominator'             => 'days',
					'enable_destroy_session'               => '',
					'limit_access'                         => '',
					'brute_force_disable'                  => '',
					'2fa_settings_last_updated_by'         => '',
					'2fa_main_user'                        => '',
					'grace-period-expiry-time'             => '',
					'plugin_version'                       => WP_2FA_VERSION,
					'delete_data_upon_uninstall'           => '',
					'excluded_sites'                       => array(),
					'included_sites'                       => array(),
					'create-custom-user-page'              => 'no',
					'redirect-user-custom-page'            => '',
					'redirect-user-custom-page-global'     => '',
					'custom-user-page-url'                 => '',
					'custom-user-page-id'                  => '',
					'hide_remove_button'                   => '',
					'separate-multisite-page-url'          => '',
					'grace-policy'                         => 'use-grace-period',
					'superadmins-role-add'                 => 'no',
					'superadmins-role-exclude'             => 'no',
					'default-text-code-page'               => '<p>' . __( 'Please enter the two-factor authentication (2FA) verification code below to login. Depending on your 2FA setup, you can get the code from the 2FA app or it was sent to you by email.', 'wp-2fa' ) . '</p><p><strong>' . __( 'Note: if you are supposed to receive an email but did not receive any, please click the Resend Code button to request another code.', 'wp-2fa' ) . '</strong></p>',
					'default-text-pw-reset-code-page'      => '<p>' . __( 'You have been sent a one-time code via email. Please enter the code below and then click Get New Password to proceed with the password reset.', 'wp-2fa' ) . '</p><br><p><strong>' . __( 'Note: If you have not received the code please click the button Resend Code. If you still do not get the code after pressing the button, please contact the website\'s administrator.', 'wp-2fa' ) . '</strong></p>',
					'default-2fa-required-notice'          => '<p>' . __( 'This website\'s administrator requires you to enable two-factor authentication (2FA) {grace_period_remaining}.', 'wp-2fa' ) . '</p><br><p>' . __( 'Failing to configure 2FA within this time period will result in a locked account. For more information, please contact your website administrator.', 'wp-2fa' ) . '</p>',
					'default-2fa-resetup-required-notice'  => '<p>' . __( 'This website\'s administrator requires you to enable two-factor authentication (2FA) {grace_period_remaining}.', 'wp-2fa' ) . '</p><br><p>' . __( 'Failing to configure 2FA within this time period will result in a locked account. For more information, please contact your website administrator.', 'wp-2fa' ) . '</p>',
					'custom-text-authy-code-page-intro'    => __( 'If you are using the Authy app approve the OneTouch request to log in.', 'wp-2fa' ),
					'custom-text-authy-code-page-awaiting' => __( 'Waiting for approval from application...', 'wp-2fa' ),
					'custom-text-authy-code-page'          => __( 'Manually enter the code from the mobile app.', 'wp-2fa' ),
					'custom-text-twilio-code-page'         => __( 'Enter the 2FA code you have received over SMS.', 'wp-2fa' ),
					'custom-text-clickatell-code-page'     => __( 'Enter the 2FA code you have received over SMS.', 'wp-2fa' ),
					'custom-text-yubico-code-page'         => __( 'Please insert the YubiKey in a USB port and touch / click the button on the YubiKey to generate the OTP required to log in.', 'wp-2fa' ),
					'custom-text-app-code-page'            => '<p>' . __( 'Please enter the two-factor authentication (2FA) verification code below to login. Depending on your 2FA setup, you can get the code from the 2FA app or it was sent to you by email.', 'wp-2fa' ) . '</p><p><strong>' . __( 'Note: if you are supposed to receive an email but did not receive any, please click the Resend Code button to request another code.', 'wp-2fa' ) . '</strong></p>',
					'custom-text-email-code-page'          => '<p>' . __( 'Please enter the two-factor authentication (2FA) verification code below to login. Depending on your 2FA setup, you can get the code from the 2FA app or it was sent to you by email.', 'wp-2fa' ) . '</p><p><strong>' . __( 'Note: if you are supposed to receive an email but did not receive any, please click the Resend Code button to request another code.', 'wp-2fa' ) . '</strong></p>',

					'default-backup-code-page'             => __( 'Enter a backup verification code.', 'wp-2fa' ),
					'method_invalid_setting'               => 'login_block',
					'enable_wizard_styling'                => 'enable_wizard_styling',
					'show_help_text'                       => 'show_help_text',
					'enable_wizard_logo'                   => '',
					'enable_welcome'                       => '',
					'welcome'                              => '',
					'method_selection'                     => '<h3>' . __( 'Choose the 2FA method', 'wp-2fa' ) . '</h3>' . Methods::get_number_of_methods_text(),
					'method_selection_single'              => '<h3>' . __( 'Choose the 2FA method', 'wp-2fa' ) . '</h3><p>' . __( 'Only the below 2FA method is allowed on this website:', 'wp-2fa' ) . '</p>',
					'method_help_authy_intro'              => '<h3>' . __( 'Setting up Push notifications', 'wp-2fa' ) . '</h3><p>' . __( 'To enable push notifications enter the country and cellphone number in order to use it with this account.', 'wp-2fa' ) . '</p>',
					'method_help_twilio_intro'             => '<h3>' . __( 'Setting up 2FA over SMS', 'wp-2fa' ) . '</h3><p>' . __( 'When you use 2FA over SMS to log in to this website you will receive your one-time code via an SMS on your cellphone. Therefore please enter the cellphone number of where you would like to receive the SMS below.', 'wp-2fa' ) . '</p>',
					'method_help_clickatell_intro'         => '<h3>' . __( 'Setting up 2FA over SMS', 'wp-2fa' ) . '</h3><p>' . __( 'When you use 2FA over SMS to log in to this website you will receive your one-time code via an SMS on your cellphone. Therefore please enter the cellphone number of where you would like to receive the SMS below.', 'wp-2fa' ) . '</p>',
					'method_help_oob_intro'                => '<h3>' . __( 'Setting up Link over email 2FA', 'wp-2fa' ) . '</h3><p>' . __( 'Please select the email address to where the out-of-band link should be sent:', 'wp-2fa' ) . '</p>',
					'method_help_yubico_intro'             => '<h3>' . __( 'Setting up 2FA with YubiKey', 'wp-2fa' ) . '</h3><p>' . __( '1 - Insert your YubiKey into the computer\'s / mobile\'s USB port', 'wp-2fa' ) . '</p><p>' . __( '2 - Touch / press the button on your YubiKey to generate the OTP code, which is automatically populated below', 'wp-2fa' ) . '</p>',
					'method_verification_oob_pre'          => '<h3>' . __( 'Almost there…', 'wp-2fa' ) . '</h3><p>' . __( 'Please type in the one-time code sent to your email address to finalize the setup. Once the code is confirmed and 2FA is set up, you only have to verify a login by clicking on a link sent to you via email.', 'wp-2fa' ) . '</p>',
					'method_verification_authy_pre'        => '<h3>' . __( 'Almost there…', 'wp-2fa' ) . '</h3><p>' . __( 'Please type in the code from your Authy application with name {authy_name}', 'wp-2fa' ) . '</p>',
					'method_verification_twilio_pre'       => '<h3>' . __( 'Almost there…', 'wp-2fa' ) . '</h3><p>' . __( 'Please type in the one-time code sent via SMS to your phone to confirm your phone number.', 'wp-2fa' ) . '</p>',
					'method_verification_clickatell_pre'   => '<h3>' . __( 'Almost there…', 'wp-2fa' ) . '</h3><p>' . __( 'Please type in the one-time code sent via SMS to your phone to confirm your phone number.', 'wp-2fa' ) . '</p>',
					'method_verification_yubico_pre'       => '<h3>' . __( 'Almost there…', 'wp-2fa' ) . '</h3><p>' . __( 'Touch the YubiKey again to generate the OTP code to confirm the setup. Once the code is populated below, it should be automatically saved and verified. If that does not happen by any reason, once the secret key was pasted, click "Validate & save" button below to manually save and complete the configuration.', 'wp-2fa' ) . '</p>',
					'backup_codes_intro_multi'             => '<h3>' . __( 'Your login just got more secure', 'wp-2fa' ) . '</h3><p>' . __( 'It is recommended to configure a backup 2FA method in case you do not have access to the primary 2FA method to generate a code to log in. You can configure any of the below. You can always configure any or both from your user profile page later.', 'wp-2fa' ) . '</p>',
					'backup_codes_intro'                   => '<h3>' . __( 'Your login just got more secure', 'wp-2fa' ) . '</h3><p>' . __( 'Congratulations! You have enabled two-factor authentication for your user. You’ve just helped towards making this website more secure!', 'wp-2fa' ) . '</p>',
					'backup_codes_intro_continue'          => '<h3>' . __( 'Your login just got more secure', 'wp-2fa' ) . '</h3><p>' . __( 'Congratulations! You have enabled two-factor authentication for your user. You’ve just helped towards making this website more secure!', 'wp-2fa' ) . '</p><p>' . __( 'You should now generate the list of backup method. Although this is optional, it is highly recommended to have a secondary 2FA method. This can be used as a backup should the primary 2FA method fail. This can happen if, for example, you forget your smartphone, the smartphone runs out of battery, or there are email deliverability problems.', 'wp-2fa' ) . '</p>',
					'backup_codes_generate_intro'          => '<h3>' . __( 'Generate list of backup codes', 'wp-2fa' ) . '</h3><p>' . __( 'It is recommended to generate and print some backup codes in case you lose access to your primary 2FA method.', 'wp-2fa' ) . '</p>',
					'backup_codes_generated'               => '<h3>' . __( 'Backup codes generated', 'wp-2fa' ) . '</h3><p>' . __( 'Here are your backup codes:', 'wp-2fa' ) . '</p>',
					'no_further_action'                    => '<h3>' . __( 'Congratulations! You are all set.', 'wp-2fa' ),
					'2fa_required_intro'                   => '<h3>' . __( 'You are required to configure 2FA.', 'wp-2fa' ) . '</h3><p>' . __( 'In order to keep this site - and your details secure, this website’s administrator requires you to enable 2FA authentication to continue.', 'wp-2fa' ) . '</p><p>' . __( 'Two factor authentication ensures only you have access to your account by creating an added layer of security when logging in -', 'wp-2fa' ) . ' <a href="https://melapress.com/wordpress-2fa/?&utm_source=plugin&utm_medium=link&utm_campaign=wp2fa" target="_blank" rel="noopener">' . __( 'Learn more', 'wp-2fa' ) . '</a></p>',
					'authy_reconfigure_intro'              => '<h3>' . __( '{reconfigure_or_configure_capitalized} push notification method', 'wp-2fa' ) . '</h3><p>' . __( 'Click the below button to {reconfigure_or_configure} the push notifications configuration.', 'wp-2fa' ) . '</p>',
					'authy_reconfigure_intro_unavailable'  => '<h3>' . __( '{reconfigure_or_configure_capitalized} push notification method', 'wp-2fa' ) . '</h3><p>' . __( 'The 2FA service you want to use is currently unavailable. Please try again later or restart the wizard to choose another method.', 'wp-2fa' ) . '</p>',
					'twilio_reconfigure_intro'             => '<h3>' . __( '{reconfigure_or_configure_capitalized} SMS method (Twilio)', 'wp-2fa' ) . '</h3><p>' . __( 'Click the below button to {reconfigure_or_configure} the mobile phone number where the one-time code should be sent.', 'wp-2fa' ) . '</p>',
					'clickatell_reconfigure_intro'         => '<h3>' . __( '{reconfigure_or_configure_capitalized} SMS method (Clickatell)', 'wp-2fa' ) . '</h3><p>' . __( 'Please select the phone where code should be send:', 'wp-2fa' ) . '</p>',
					'yubico_reconfigure_intro'             => '<h3>' . __( '{reconfigure_or_configure_capitalized} 2FA over YubiKey', 'wp-2fa' ) . '</h3><p>' . __( 'Click the below button to {reconfigure_or_configure} the YubiKey associated with your user.', 'wp-2fa' ) . '</p>',
					'twilio_reconfigure_intro_unavailable' => '<h3>' . __( '{reconfigure_or_configure_capitalized} SMS method', 'wp-2fa' ) . '</h3><p>' . __( 'The 2FA over SMS service you want to use is currently unavailable. Please try again later or restart the wizard to choose another method.', 'wp-2fa' ) . '</p>',
					'clickatell_reconfigure_intro_unavailable' => '<h3>' . __( '{reconfigure_or_configure_capitalized} SMS method', 'wp-2fa' ) . '</h3><p>' . __( 'The 2FA over SMS service you want to use is currently unavailable. Please try again later or restart the wizard to choose another method.', 'wp-2fa' ) . '</p>',
					'yubico_reconfigure_intro_unavailable' => '<h3>' . __( ' {reconfigure_or_configure_capitalized} 2FA over YubiKey', 'wp-2fa' ) . '</h3><p>' . __( 'The Yubico service you want to use is currently unavailable. Please try again later or restart the wizard to choose another method.', 'wp-2fa' ) . '</p>',
					'oob_reconfigure_intro'                => '<h3>' . __( '{reconfigure_or_configure_capitalized} link over email method', 'wp-2fa' ) . '</h3><p>' . __( 'Click the below button to {reconfigure_or_configure} the email address where the link should be sent.', 'wp-2fa' ) . '</p>',
					'custom_css'                           => '',
					'login_custom_css'                     => '',
					'logo-code-page'                       => '',
					'disable_login_css'                    => '',
					'login-to-view-area'                   => '<p>' . __( 'You must be logged in to view this page. {login_url}', 'wp-2fa' ) . '</p>',
					'backup_email_intro'                   => '<h3>' . __( 'Your login just got more secure', 'wp-2fa' ) . '</h3><p>' . __( 'Well done on configuring 2FA, your login has just got more secure. To make sure you never get locked out you are required to confirm your email address and use email as an alternative and backup 2FA method in case your primary method is unavailable. Please confirm your email address below', 'wp-2fa' ) . '</p>',
					'user-profile-form-preamble-title'     => __( 'Two-factor authentication settings', 'wp-2fa' ),
					'user-profile-form-preamble-desc'      => __( 'Add two-factor authentication to strengthen the security of your user account.', 'wp-2fa' ),
					'use_custom_2fa_message'               => 'use-defaults',

				);
				/**
				 * Gives the ability to filter the default settings array of the plugin
				 *
				 * @param array $settings - The array with all the default settings.
				 *
				 * @since 2.0.0
				 */
				self::$default_settings = \apply_filters( WP_2FA_PREFIX . 'default_settings', self::$default_settings );
			}

			return self::$default_settings;
		}

		/**
		 * Inits the plugin related classes and settings
		 *
		 * @return void
		 *
		 * @since 2.6.0
		 */
		public static function init() {

			Methods_Helper::init();

			self::$plugin_settings[ WP_2FA_POLICY_SETTINGS_NAME ]      = Settings_Utils::get_option( WP_2FA_POLICY_SETTINGS_NAME, array() );
			self::$plugin_settings[ WP_2FA_SETTINGS_NAME ]             = Settings_Utils::get_option( WP_2FA_SETTINGS_NAME, array() );
			self::$plugin_settings[ WP_2FA_WHITE_LABEL_SETTINGS_NAME ] = Settings_Utils::get_option( WP_2FA_WHITE_LABEL_SETTINGS_NAME, array() );

			self::$wp_2fa_email_templates = Settings_Utils::get_option( WP_2FA_EMAIL_SETTINGS_NAME );

			/** We need to exclude all the possible ways, that logic to be executed by some WP request which could come from cron job or AJAX call, which will break the wizard (by storing the settings for the plugin) before it is completed by the user. We also have to check if the user is still processing first time wizard ($_GET parameter), and if the wizard has been finished already (wp_2fa_wizard_not_finished)  */
			if ( Settings_Utils::get_option( 'wizard_not_finished' ) && ! isset( $_GET['is_initial_setup'] ) && ! wp_doing_ajax() && ! defined( 'DOING_CRON' ) ) {

				if ( ! Settings_Utils::get_option( WP_2FA_POLICY_SETTINGS_NAME ) ) {
					self::update_plugin_settings( self::get_default_settings() );
				}

				// Set a flag so we know we have default values present, not custom.
				Settings_Utils::update_option( 'default_settings_applied', true );
				Settings_Utils::delete_option( 'wizard_not_finished' );
			}


			WP_Helper::init();

			// Bootstrap.
			Core\setup();

			if ( is_admin() ) {
				User_Listing::init();
				// Hide all unrelated to the plugin notices on the plugin admin pages.
				\add_action( 'admin_print_scripts', array( '\WP2FA\Admin\Helpers\WP_Helper', 'hide_unrelated_notices' ) );

				// FlyOut::init();
			}

			Grace_Period_Notifications::init();
			Password_Reset_2FA::init();
			Re_Login_2FA::init();

			Shortcodes::init();
			User_Notices::init();
			Plugin_Updated_Notice::init();

			self::add_actions();

			// Inits all the additional free app extensions.
			$free_extensions = Classes_Helper::get_classes_by_namespace( 'WP2FA\\App\\' );

			foreach ( $free_extensions as $extension ) {
				if ( method_exists( $extension, 'init' ) ) {
					call_user_func_array( array( $extension, 'init' ), array() );
				}
			}
		}

		/**
		 * Inits all the plugin hooks
		 *
		 * @return void
		 *
		 * @since 2.6.0
		 */
		public static function add_actions() {
			// Plugin redirect on activation, only if we have no settings currently saved.
			if ( ( ! isset( self::$plugin_settings[ WP_2FA_POLICY_SETTINGS_NAME ] ) || empty( self::$plugin_settings[ WP_2FA_POLICY_SETTINGS_NAME ] ) ) && Settings_Utils::get_option( 'redirect_on_activate', false ) ) {
				\add_action( 'admin_init', array( __CLASS__, 'setup_redirect' ), 10 );
			} elseif ( ! \is_array( Settings_Utils::get_option( WP_2FA_POLICY_SETTINGS_NAME ) ) ) {
					Settings_Utils::delete_option( WP_2FA_POLICY_SETTINGS_NAME );
					self::update_plugin_settings( self::get_default_settings() );
			}

			// SettingsPage.
			if ( WP_Helper::is_multisite() ) {
				\add_action( 'network_admin_menu', array( '\WP2FA\Admin\Settings_Page', 'create_settings_admin_menu_multisite' ) );
				\add_action( 'network_admin_edit_update_wp2fa_network_options', array( '\WP2FA\Admin\Settings_Page', 'update_wp2fa_network_options' ) );
				\add_action( 'network_admin_edit_update_wp2fa_network_email_options', array( '\WP2FA\Admin\Settings_Page', 'update_wp2fa_network_email_options' ) );
				\add_action( 'network_admin_notices', array( '\WP2FA\Admin\Settings_Page', 'settings_saved_network_admin_notice' ) );
				\add_action( 'network_admin_notices', array( __CLASS__, 'wp_not_writable' ) );
			} else {
				\add_action( 'admin_menu', array( '\WP2FA\Admin\Settings_Page', 'create_settings_admin_menu' ) );
				\add_action( 'admin_notices', array( '\WP2FA\Admin\Settings_Page', 'settings_saved_admin_notice' ) );
				\add_action( 'admin_notices', array( __CLASS__, 'wp_not_writable' ) );
			}
			\add_action( 'wp_ajax_wp2fa_dismiss_notice_mail_domain', array( '\WP2FA\Admin\Settings_Page', 'dismiss_notice_mail_domain' ) );
			\add_action( 'wp_ajax_nopriv_set_salt_key', array( '\WP2FA\Admin\Helpers\Ajax_Helper', 'set_salt_key' ) );
			\add_action( 'wp_ajax_set_salt_key', array( '\WP2FA\Admin\Helpers\Ajax_Helper', 'set_salt_key' ) );

			\add_action( 'wp_ajax_wp_2fa_get_all_users', array( '\WP2FA\Admin\Helpers\Ajax_Helper', 'get_all_users' ) );
			\add_action( 'wp_ajax_wp_2fa_get_all_roles', array( '\WP2FA\Admin\Helpers\Ajax_Helper', 'get_ajax_user_roles' ) );
			\add_action( 'wp_ajax_wp_2fa_get_all_network_sites', array( '\WP2FA\Admin\Helpers\Ajax_Helper', 'get_all_network_sites' ) );
			\add_action( 'wp_ajax_unlock_account', array( '\WP2FA\Admin\Helpers\Ajax_Helper', 'unlock_account' ), 10, 1 );
			\add_action( 'admin_action_unlock_account', array( '\WP2FA\Admin\Helpers\Ajax_Helper', 'unlock_account' ), 10, 1 );
			\add_action( 'admin_action_remove_user_2fa', array( '\WP2FA\Admin\Helpers\Ajax_Helper', 'remove_user_2fa' ), 10, 1 );
			\add_action( 'wp_ajax_remove_user_2fa', array( '\WP2FA\Admin\Helpers\Ajax_Helper', 'remove_user_2fa' ), 10, 1 );
			\add_action( 'admin_menu', array( '\WP2FA\Admin\Settings_Page', 'hide_settings' ), 999 );
			\add_action( 'plugin_action_links_' . WP_2FA_BASE, array( '\WP2FA\Admin\Settings_Page', 'add_plugin_action_links' ) );
			\add_filter( 'display_post_states', array( '\WP2FA\Admin\Settings_Page', 'add_display_post_states' ), 10, 2 );
			\add_action( 'wp_ajax_send_authentication_setup_email', array( '\WP2FA\Admin\Setup_Wizard', 'send_authentication_setup_email' ) );
			\add_action( 'wp_ajax_send_backup_codes_email', array( '\WP2FA\Methods\Backup_Codes', 'send_backup_codes_email' ) );
			\add_action( 'wp_ajax_regenerate_authentication_key', array( '\WP2FA\Methods\TOTP', 'regenerate_authentication_key' ) );

			// User_Notices.
			\add_action( 'wp_ajax_dismiss_nag', array( '\WP2FA\Admin\User_Notices', 'dismiss_nag' ) );
			\add_action( 'wp_ajax_wp2fa_dismiss_reconfigure_nag', array( '\WP2FA\Admin\User_Notices', 'dismiss_nag' ) );
			\add_action( 'wp_logout', array( '\WP2FA\Admin\User_Notices', 'reset_nag' ), 10, 1 );

			// User_Profile.
			global $pagenow;
			if ( 'profile.php' !== $pagenow || 'user-edit.php' !== $pagenow ) {
				\add_action( 'show_user_profile', array( '\WP2FA\Admin\User_Profile', 'inline_2fa_profile_form' ) );
				\add_action( 'edit_user_profile', array( '\WP2FA\Admin\User_Profile', 'inline_2fa_profile_form' ) );
				if ( WP_Helper::is_multisite() ) {
					\add_action( 'personal_options_update', array( '\WP2FA\Admin\User_Profile', 'save_user_2fa_options' ) );
				}
			}
			\add_filter( 'user_row_actions', array( '\WP2FA\Admin\User_Profile', 'user_2fa_row_actions' ), 10, 2 );
			if ( WP_Helper::is_multisite() ) {
				\add_filter( 'ms_user_row_actions', array( '\WP2FA\Admin\User_Profile', 'user_2fa_row_actions' ), 10, 2 );
			}
			\add_action( 'wp_ajax_validate_authcode_via_ajax', array( '\WP2FA\Admin\User_Profile', 'validate_authcode_via_ajax' ) );
			\add_action( 'wp_ajax_wp2fa_test_email', array( '\WP2FA\Admin\Helpers\Ajax_Helper', 'handle_send_test_email_ajax' ) );

			// Login.
			\add_action( 'wp_login', array( '\WP2FA\Authenticator\Login', 'wp_login' ), 20, 2 );
			\add_action( 'wp_loaded', array( '\WP2FA\Authenticator\Login', 'login_form_validate_2fa' ) );
			\add_action( 'login_form_validate_2fa', array( '\WP2FA\Authenticator\Login', 'login_form_validate_2fa' ) );
			\add_action( 'login_form_backup_2fa', array( '\WP2FA\Authenticator\Login', 'backup_2fa' ) );
			\add_action( 'login_enqueue_scripts', array( '\WP2FA\Authenticator\Login', 'dequeue_style' ), PHP_INT_MAX );

			// Reset password.
			\add_action( 'lostpassword_post', array( '\WP2FA\Authenticator\Reset_Password', 'lostpassword_post' ), 20, 2 );
			\add_action( 'login_form_lostpassword', array( '\WP2FA\Authenticator\Reset_Password', 'login_form_validate_2fa' ), 20 );
			// \add_action( 'wp_loaded', array( '\WP2FA\Authenticator\Reset_Password', 'login_form_validate_2fa' ) );.

			/**
			 * Keep track of all the user sessions for which we need to invalidate the
			 * authentication cookies set during the initial password check.
			 */
			\add_action( 'set_auth_cookie', array( '\WP2FA\Authenticator\Login', 'collect_auth_cookie_tokens' ) );
			\add_action( 'set_logged_in_cookie', array( '\WP2FA\Authenticator\Login', 'collect_auth_cookie_tokens' ) );

			// Run only after the core wp_authenticate_username_password() check.
			\add_filter( 'authenticate', array( '\WP2FA\Authenticator\Login', 'filter_authenticate' ), 50 );
			\add_filter( 'wp_authenticate_user', array( '\WP2FA\Authenticator\Login', 'run_authentication_check' ), 10, 2 );

			// User Register.
			\add_action( 'set_user_role', array( '\WP2FA\Admin\User_Registered', 'check_user_upon_role_change' ), 10, 3 );

			// Block users from admin if needed.
			$user_block_hook = is_admin() || is_network_admin() ? 'init' : 'wp';
			\add_action( $user_block_hook, array( __CLASS__, 'block_unconfigured_users_from_admin' ), 10 );

			// Help & Contact Us.
			\add_action( WP_2FA_PREFIX . 'after_admin_menu_created', array( '\WP2FA\Admin\Help_Contact_Us', 'add_extra_menu_item' ) );

			// phpcs:disable
			/* @free:start */
			// phpcs:enable
			// Premium Features.
			\add_action( WP_2FA_PREFIX . 'after_admin_menu_created', array( 'WP2FA\Admin\Premium_Features', 'add_extra_menu_item' ) );
			\add_action( WP_2FA_PREFIX . 'before_plugin_settings', array( 'WP2FA\Admin\Premium_Features', 'add_settings_banner' ) );
			\add_action( 'admin_footer', array( 'WP2FA\Admin\Premium_Features', 'pricing_new_tab_js' ) );
			// phpcs:disable
			/* @free:end */
			// phpcs:enable

			\add_action( 'admin_footer', array( '\WP2FA\Admin\User_Profile', 'dismiss_nag_notice' ) );

			\add_action( WP_2FA_PREFIX . 'user_authenticated', array( __CLASS__, 'clear_user_after_login' ), 10, 1 );

			\add_filter( 'mepr-auto-login', array( '\WP2FA\Authenticator\Login', 'mepr_login' ) );
		}

		/**
		 * Add actions specific to the wizard.
		 *
		 * @since 2.0.0
		 */
		public static function add_wizard_actions() {
			if ( function_exists( 'wp_get_current_user' ) && \current_user_can( 'read' ) ) {
				\add_action( 'admin_init', array( '\WP2FA\Admin\Setup_Wizard', 'setup_page' ), 10 );
			}
		}

		/**
		 * Redirect user to 1st time setup.
		 *
		 * @since 2.0.0
		 */
		public static function setup_redirect() {

			// Bail early before the redirect if the user can't manage options.
			if ( ! \current_user_can( 'manage_options' ) ) {
				return;
			}

			$registered_and_active = 'yes';
			if ( function_exists( 'wp2fa_freemius' ) ) {
				$registered_and_active = wp2fa_freemius()->is_registered() && wp2fa_freemius()->has_active_valid_license() ? 'yes' : 'no';
			}

			if ( Settings_Utils::get_option( 'redirect_on_activate', false ) && 'yes' === $registered_and_active ) {
				// Delete redirect option.
				Settings_Utils::delete_option( 'redirect_on_activate' );

				Settings_Utils::update_option( 'wizard_not_finished', true );

				$redirect = \add_query_arg(
					array(
						'page'             => 'wp-2fa-setup',
						'is_initial_setup' => 'true',
					),
					\network_admin_url( 'user-edit.php' )
				);

				\wp_safe_redirect( $redirect );
				exit();
			}
		}

		/**
		 * Util function to grab settings or apply defaults if no settings are saved into the db.
		 *
		 * @param string  $setting_name Settings to grab value of.
		 * @param boolean $get_default_on_empty return default setting value if current one is empty.
		 * @param boolean $get_default_value return default value setting (ignore the stored ones).
		 * @param string  $role - The name of the user role.
		 *
		 * @return mixed               Settings value or default value.
		 *
		 * @since 2.0.0
		 */
		public static function get_wp2fa_setting( $setting_name = '', $get_default_on_empty = false, $get_default_value = false, $role = 'global' ) {
			$role = ( is_null( $role ) || empty( $role ) ) ? 'global' : $role;
			return self::get_wp2fa_setting_generic( WP_2FA_POLICY_SETTINGS_NAME, $setting_name, $get_default_on_empty, $get_default_value, $role );
		}

		/**
		 * Util function to grab settings or apply defaults if no settings are saved into the db.
		 *
		 * @param string  $setting_name Settings to grab value of.
		 * @param boolean $get_default_on_empty return default setting value if current one is empty.
		 * @param boolean $get_default_value return default value setting (ignore the stored ones).
		 *
		 * @return mixed               Settings value or default value.
		 */
		public static function get_wp2fa_general_setting( $setting_name = '', $get_default_on_empty = false, $get_default_value = false ) {

			return self::get_wp2fa_setting_generic( WP_2FA_SETTINGS_NAME, $setting_name, $get_default_on_empty, $get_default_value );
		}

		/**
		 * Util function to grab white label settings or apply defaults if no settings are saved into the db.
		 *
		 * @param  string  $setting_name Settings to grab value of.
		 * @param boolean $get_default_on_empty return default setting value if current one is empty.
		 * @param boolean $get_default_value return default value setting (ignore the stored ones).
		 *
		 * @return string               Settings value or default value.
		 *
		 * @since 2.0.0
		 */
		public static function get_wp2fa_white_label_setting( $setting_name = '', $get_default_on_empty = false, $get_default_value = false ) {

			return (string) self::get_wp2fa_setting_generic( WP_2FA_WHITE_LABEL_SETTINGS_NAME, $setting_name, $get_default_on_empty, $get_default_value );
		}

		/**
		 * Generic method for extracting settings from the plugin
		 *
		 * @param string  $wp_2fa_setting - The name of the settings type.
		 * @param string  $setting_name - The name of the setting to extract.
		 * @param boolean $get_default_on_empty - Should we use default value on empty.
		 * @param boolean $get_default_value - Extract default value.
		 * @param string  $role - The name of the user role.
		 *
		 * @return mixed
		 *
		 * @since 2.0.0
		 */
		private static function get_wp2fa_setting_generic( $wp_2fa_setting = WP_2FA_POLICY_SETTINGS_NAME, $setting_name = '', $get_default_on_empty = false, $get_default_value = false, $role = 'global' ) {
			$default_settings = self::get_default_settings();
			$role             = ( is_null( $role ) || empty( $role ) ) ? 'global' : $role;

			if ( true === $get_default_value ) {
				if ( isset( $default_settings[ $setting_name ] ) ) {
					return $default_settings[ $setting_name ];
				}

				return false;
			}

			$apply_defaults = false;

			$wp2fa_setting = self::$plugin_settings[ $wp_2fa_setting ];

			// If we have no setting name, return them all.
			if ( empty( $setting_name ) ) {
				return $wp2fa_setting;
			}

			// First lets check if any options have been saved.
			if ( empty( $wp2fa_setting ) || ! isset( $wp2fa_setting ) ) {
				$apply_defaults = true;
			}

			if ( $apply_defaults ) {
				return isset( $default_settings[ $setting_name ] ) ? $default_settings[ $setting_name ] : false;
			} elseif ( ! isset( $wp2fa_setting[ $setting_name ] ) ) {
				if ( true === $get_default_on_empty ) {
					if ( isset( $default_settings[ $setting_name ] ) ) {
						return $default_settings[ $setting_name ];
					}
				}
				return false;
			} elseif ( WP_2FA_POLICY_SETTINGS_NAME === $wp_2fa_setting ) {

				/**
				 * Extensions could change the extracted value, based on custom / different / specific for role settings.
				 *
				 * @param mixed - Value of the setting.
				 * @param string - The name of the setting.
				 * @param string - The role name.
				 *
				 * @since 2.0.0
				 */
				return \apply_filters( WP_2FA_PREFIX . 'setting_generic', $wp2fa_setting[ $setting_name ], $setting_name, $role );
			} else {
				return $wp2fa_setting[ $setting_name ];
			}
		}

		/**
		 * Util function to grab EMAIL settings or apply defaults if no settings are saved into the db.
		 *
		 * @param  string $setting_name Settings to grab value of.
		 *
		 * @since 2.0.0
		 */
		public static function get_wp2fa_email_templates( $setting_name = '' ) {

			// If we have no setting name, return what ever is saved.
			if ( empty( $setting_name ) ) {
				return self::$wp_2fa_email_templates;
			}

			// If we have a saved setting, return it.
			if ( $setting_name && isset( self::$wp_2fa_email_templates[ $setting_name ] ) ) {
				return self::$wp_2fa_email_templates[ $setting_name ];
			}

			// Create Login Code Message.
			$login_code_subject = __( 'Your login confirmation code for {site_name}', 'wp-2fa' );

			$login_code_body = '<p>' . sprintf(
			// translators: The login code provided from the plugin.
				\esc_html__( 'Enter %1$1s to log in.', 'wp-2fa' ),
				'<strong>{login_code}</strong>'
			);
			$login_code_body .= '</p>';
			$login_code_body .= '<p>' . \esc_html__( 'Thank you.', 'wp-2fa' ) . '</p>';
			$login_code_body .= '<p>' . \esc_html__( 'Email sent by', 'wp-2fa' );
			$login_code_body .= ' <a href="https://melapress.com/wordpress-2fa/?&utm_source=plugin&utm_medium=link&utm_campaign=wp2fa" target="_blank">' . \esc_html__( 'WP 2FA plugin.', 'wp-2fa' ) . '</a>';
			$login_code_body .= '</p>';

			// Create Reset PW Code Message.
			$reset_password_code_subject = __( '2FA code for password reset', 'wp-2fa' );

			$reset_password_code_body = '<p>' . \esc_html__( 'Hello,', 'wp-2fa' ) . '</p>';

			$reset_password_code_body = '<p>' . sprintf(
			// translators: The login code provided from the plugin.
				\esc_html__( 'Someone from the IP address %1$1s has requested a password reset for the user %2$2s on the website %3$3s. If this was you please use the below code to proceed with the password reset:', 'wp-2fa' ),
				'{user_ip_address}',
				'{user_login_name}',
				'{site_url}'
			);

			$reset_password_code_body .= '<p><strong>{login_code}</strong></p>';

			$reset_password_code_body .= '</p>';
			$reset_password_code_body .= '<p>' . \esc_html__( 'If this was not you, ignore this email and contact your website administrator.', 'wp-2fa' ) . '</p>';

			$login_code_setup_body = '<p>' . sprintf(
			// translators: The login code provided from the plugin.
				\esc_html__( 'Please enter this code to confirm 2FA setup: %1$1s', 'wp-2fa' ),
				'<strong>{login_code}</strong>'
			);
			$login_code_setup_body .= '</p>';
			$login_code_setup_body .= '<p>' . \esc_html__( 'Thank you.', 'wp-2fa' ) . '</p>';
			$login_code_setup_body .= '<p>' . \esc_html__( 'Email sent by', 'wp-2fa' );
			$login_code_setup_body .= ' <a href="hhttps://melapress.com/wordpress-2fa/?&utm_source=plugin&utm_medium=link&utm_campaign=wp2fa" target="_blank">' . \esc_html__( 'WP 2FA plugin.', 'wp-2fa' ) . '</a>';
			$login_code_setup_body .= '</p>';

			// Create User Locked Message.
			$user_locked_subject = __( 'Your user on {site_name} has been locked', 'wp-2fa' );

			$user_locked_body  = '<p>' . \esc_html__( 'Hello.', 'wp-2fa' ) . '</p>';
			$user_locked_body .= '<p>' . sprintf(
			// translators: %1s - the name of the user
			// translators: %2s - the name of the site.
				\esc_html__( 'Since you have not enabled two-factor authentication for the user %1$1s on the website %2$2s within the grace period, your account has been locked.', 'wp-2fa' ),
				'{user_login_name}',
				'{site_name}'
			);
			$user_locked_body .= '</p>';
			$user_locked_body .= '<p>' . \esc_html__( 'Contact your website administrator to unlock your account.', 'wp-2fa' ) . '</p>';
			$user_locked_body .= '<p>' . \esc_html__( 'Thank you.', 'wp-2fa' ) . '</p>';
			$user_locked_body .= '<p>' . \esc_html__( 'Email sent by', 'wp-2fa' );
			$user_locked_body .= ' <a href="https://melapress.com/wordpress-2fa/?&utm_source=plugin&utm_medium=link&utm_campaign=wp2fa" target="_blank">' . \esc_html__( 'WP 2FA plugin.', 'wp-2fa' ) . '</a>';
			$user_locked_body .= '</p>';

			// Create User unlocked Message.
			$user_unlocked_subject = __( 'Your user on {site_name} has been unlocked', 'wp-2fa' );
			$user_unlocked_body    = '';

			$user_unlocked_body .= '<p>' . __( 'Hello,', 'wp-2fa' ) . '</p><p>' . \esc_html__( 'Your user', 'wp-2fa' ) . ' <strong>{user_login_name}</strong> ' . \esc_html__( 'on the website', 'wp-2fa' ) . ' {site_url} ' . __( 'has been unlocked. Please configure two-factor authentication within the grace period, otherwise your account will be locked again.', 'wp-2fa' ) . '</p>';

			if ( ! empty( self::get_wp2fa_setting( 'custom-user-page-id' ) ) ) {
				$user_unlocked_body .= '<p>' . __( 'You can configure 2FA from this page:', 'wp-2fa' ) . ' <a href="{2fa_settings_page_url}" target="_blank">{2fa_settings_page_url}.</a></p>';
			}

			$user_unlocked_body .= '<p>' . __( 'Thank you.', 'wp-2fa' ) . '</p><p>' . __( 'Email sent by', 'wp-2fa' ) . ' <a href="https://melapress.com/wordpress-2fa/?&utm_source=plugin&utm_medium=link&utm_campaign=wp2fa" target="_blank">' . __( 'WP 2FA plugin', 'wp-2fa' ) . '</a></p>';

			// Create User backup codes Message.
			$user_backup_codes_subject = __( '2FA backup codes for user {user_login_name} on {site_name}', 'wp-2fa' );
			$user_backup_codes_body    = '';

			$user_backup_codes_body .= '<p>' . __( 'Hello,', 'wp-2fa' ) . '</p><p>' . \esc_html__( 'Below please find the 2FA backup codes for your user', 'wp-2fa' ) . ' <strong>{user_login_name}</strong> ' . \esc_html__( 'on the website', 'wp-2fa' ) . ' <strong>{site_name}</strong>. ' . __( 'The website\'s URL is', 'wp-2fa' ) . ' {site_url} </p>';

			$user_backup_codes_body .= '{backup_codes}';

			$user_backup_codes_body .= '<p>' . __( 'Thank you for enabling 2FA on your account and helping us keeping the website secure.', 'wp-2fa' ) . '</p><p>' . __( 'Email sent by', 'wp-2fa' ) . ' <a href="https://melapress.com/wordpress-2fa/?&utm_source=plugin&utm_medium=link&utm_campaign=wp2fa" target="_blank">' . __( 'WP 2FA plugin', 'wp-2fa' ) . '</a></p>';

			// Array of defaults, now we have things setup above.
			$default_settings = array(
				'email_from_setting'                  => 'use-defaults',
				'custom_from_email_address'           => '',
				'custom_from_display_name'            => '',
				'login_code_email_subject'            => $login_code_subject,
				'login_code_email_body'               => $login_code_body,
				'reset_password_code_email_subject'   => $reset_password_code_subject,
				'reset_password_code_email_body'      => $reset_password_code_body,
				'login_code_setup_email_subject'      => $login_code_subject,
				'login_code_setup_email_body'         => $login_code_setup_body,
				'user_account_locked_email_subject'   => $user_locked_subject,
				'user_account_locked_email_body'      => $user_locked_body,
				'user_account_unlocked_email_subject' => $user_unlocked_subject,
				'user_account_unlocked_email_body'    => $user_unlocked_body,
				'user_backup_codes_email_subject'     => $user_backup_codes_subject,
				'user_backup_codes_email_body'        => $user_backup_codes_body,
				'send_account_locked_email'           => 'enable_account_locked_email',
				'send_account_unlocked_email'         => 'enable_account_unlocked_email',
				'send_login_code_email'               => 'enable_send_login_code_email',
				'send_reset_password_code_email'      => 'enable_send_reset_password_code_email',
			);

			/**
			 * Allows 3rd party providers to their own settings for the mail templates.
			 *
			 * @param array $default_settings - Array with the default settings.
			 *
			 * @since 2.0.0
			 */
			$default_settings = \apply_filters( WP_2FA_PREFIX . 'mail_default_settings', $default_settings );

			return $default_settings[ $setting_name ];
		}

		/**
		 * Util which we use to replace our {strings} with actual, useful stuff.
		 *
		 * @param string     $input   Text we are working on.
		 * @param int|string $user_id User id, if its needed.
		 * @param string     $token   Login code, if its needed..
		 * @param string     $override_grace_period - Value to override grace period with.
		 *
		 * @return string          The output, with all the {strings} swapped out.
		 *
		 * @since 2.0.0
		 */
		public static function replace_email_strings( $input = '', $user_id = '', $token = '', $override_grace_period = '' ) {

			$token = trim( (string) $token );

			// Gather grace period.
			$grace_period_string = '';
			if ( isset( $override_grace_period ) && ! empty( $override_grace_period ) ) {
				$grace_period_string = $override_grace_period;
			} else {
				$grace_policy        = self::get_wp2fa_setting( 'grace-policy' );
				$grace_period_string = Date_Time_Utils::format_grace_period_expiration_string( $grace_policy );
			}

			// Setup user data.
			if ( isset( $user_id ) && ! empty( $user_id ) ) {
				$user = get_userdata( $user_id );
			} else {
				$user = wp_get_current_user();
			}

			// Setup token.
			if ( isset( $token ) && ! empty( $token ) ) {
				$login_code = $token;
			} else {
				$login_code = '';
			}

			$new_page_id = Settings::get_role_or_default_setting( 'custom-user-page-id', $user );
			if ( ! empty( $new_page_id ) ) {
				$new_page_permalink = \get_permalink( $new_page_id );
			} else {
				$new_page_id = Settings::get_custom_settings_page_id( '', $user );
				if ( ! empty( $new_page_id ) ) {
					$new_page_permalink = \get_permalink( $new_page_id );
				} else {
					$new_page_permalink = '';
				}
			}

			// These are the strings we are going to search for, as well as there respective replacements.
			$replacements = array(
				'{site_url}'              => \esc_url( \get_bloginfo( 'url' ) ),
				'{site_name}'             => \sanitize_text_field( \get_bloginfo( 'name' ) ),
				'{grace_period}'          => \sanitize_text_field( $grace_period_string ),
				'{user_login_name}'       => \sanitize_text_field( $user->user_login ),
				'{user_first_name}'       => \sanitize_text_field( $user->user_firstname ),
				'{user_last_name}'        => \sanitize_text_field( $user->user_lastname ),
				'{user_display_name}'     => \sanitize_text_field( $user->display_name ),
				'{login_code}'            => $login_code,
				'{2fa_settings_page_url}' => \esc_url( $new_page_permalink ),
				'{user_ip_address}'       => Request_Utils::get_ip(),
			);

			/**
			 * 3rd party plugins could change the mail strings, or provide their own.
			 *
			 * @param array $replacements - The array with all the currently supported strings.
			 */
			$replacements = \apply_filters(
				WP_2FA_PREFIX . 'replacement_email_strings',
				$replacements
			);

			$final_output = str_replace( array_keys( $replacements ), array_values( $replacements ), $input );
			return $final_output;
		}

		/**
		 * Util which contextualizes the wording 'reconfigure'/'configure' as needed.
		 *
		 * @param string     $input   - Text we are working on.
		 * @param int|string $user_id - User id, if its needed.
		 * @param string     $method_to_check - Name of the method to check for.
		 *
		 * @return string The output, with all the {strings} swapped out.
		 *
		 * @since 2.5.0
		 */
		public static function contextual_reconfigure_text( $input = '', $user_id = '', $method_to_check = '' ) {

			if ( empty( trim( (string) $input ) ) ) {
				return $input;
			}

			$enabled_method = User_Helper::get_enabled_method_for_user( $user_id );

			$text = ( $enabled_method === $method_to_check ) ? \esc_html__( 'Reconfigure', 'wp-2fa' ) : \esc_html__( 'Configure', 'wp-2fa' );

			$replacements = array(
				'{reconfigure_or_configure_capitalized}' => $text,
				'{reconfigure_or_configure}'             => strtolower( $text ),
			);

			/**
			 * 3rd party plugins could change this to their own.
			 *
			 * @param array $replacements - The array with all the currently supported strings.
			 *
			 * @since 2.5.0
			 */
			$replacements = \apply_filters(
				WP_2FA_PREFIX . 'replacement_reconfigure_strings',
				$replacements
			);

			return str_replace( array_keys( $replacements ), array_values( $replacements ), $input );
		}

		/**
		 * Util replace replace a placeholder with the actual remaining grace period for a user..
		 *
		 * @param string $input  -  Text we are working on.
		 * @param int    $grace_expiry  -  Expiration time.
		 *
		 * @return string The output, with all the {strings} swapped out.
		 *
		 * @since 2.5.0
		 */
		public static function replace_remaining_grace_period( $input = '', $grace_expiry = -1 ) {
			if ( empty( trim( (string) $input ) ) || empty( trim( (string) $grace_expiry ) ) ) {
				return $input;
			}

			$replacements = array(
				'{grace_period_remaining}' => \esc_attr( Date_Time_Utils::format_grace_period_expiration_string( null, $grace_expiry ) ),
			);

			return str_replace( array_keys( $replacements ), array_values( $replacements ), $input );
		}

		/**
		 * Util which we use to replace our {strings} with actual, useful stuff.
		 *
		 * @param string  $input   Text we are working on.
		 * @param WP_User $user   The WP User.
		 *
		 * @return string          The output, with all the {strings} swapped out.
		 *
		 * @since 2.0.0
		 */
		public static function replace_wizard_strings( $input = '', $user = false ) {

			if ( ! $user ) {
				return $input;
			}

			$available_methods = Methods::get_enabled_methods( User_Helper::get_user_role( $user ) );

			// These are the strings we are going to search for, as well as there respective replacements.
			$replacements = array(
				'{available_methods_count}' => count( $available_methods[ User_Helper::get_user_role( $user ) ] ),
			);

			/**
			 * 3rd party plugins could change the mail strings, or provide their own.
			 *
			 * @param array $replacements - The array with all the currently supported strings.
			 */
			$replacements = \apply_filters(
				WP_2FA_PREFIX . 'replacement_wizard_strings',
				$replacements
			);

			$final_output = str_replace( array_keys( $replacements ), array_values( $replacements ), $input );
			return $final_output;
		}

		/**
		 * If a user is trying to access anywhere other than the 2FA config area, this blocks them.
		 *
		 * @return void
		 *
		 * @since 2.0.0
		 */
		public static function block_unconfigured_users_from_admin() {
			global $pagenow;

			$user = User_Helper::get_user();
			if ( 0 === $user->ID ) {
				return;
			}

			$redirect = true;

			if ( class_exists( '\WP2FA\Freemius\User_Licensing' ) ) {
				if ( Extensions_Loader::use_proxytron() ) {
					$redirect = User_Licensing::enable_2fa_user_setting( true );
				}
			}


			if ( $redirect ) {
				$is_user_instantly_enforced = User_Helper::get_user_enforced_instantly();
				$grace_period_expiry_time   = (int) User_Helper::get_user_expiry_date();
				$time_now                   = time();
				if ( $is_user_instantly_enforced && ! empty( $grace_period_expiry_time ) && $grace_period_expiry_time < $time_now && ! User_Helper::is_excluded( $user->ID ) ) {

					$has_cap = true;
					if ( class_exists( 'WooCommerce', false ) ) {

						// Lets check if the user has the required capabilities to view the 2FA settings page (or profile page in the Admin section - dashboard).
						$has_cap = false;

						$access_caps = array( 'edit_posts', 'manage_woocommerce', 'view_admin_dashboard' );

						foreach ( $access_caps as $access_cap ) {
							if ( \current_user_can( $access_cap ) ) {
								$has_cap = true;
								break;
							}
						}
					}

					/**
					 * We should only allow:
					 * - 2FA setup wizard in the administration
					 * - custom 2FA page if enabled and created
					 * - AJAX requests originating from these 2FA setup UIs
					 */
					if ( wp_doing_ajax() && isset( $_REQUEST['action'] ) && self::action_check() ) { // phpcs:ignore
						return;
					}

					if ( is_admin() || is_network_admin() ) {
						$allowed_admin_page = 'profile.php';
						if ( $pagenow === $allowed_admin_page && ( isset( $_GET['show'] ) && 'wp-2fa-setup' === $_GET['show'] ) ) { // phpcs:ignore
							return;
						}
					}

					if ( is_page() ) {
						$custom_user_page_id = Settings::get_role_or_default_setting( 'custom-user-page-id', $user );
						if ( ! empty( $custom_user_page_id ) && \get_the_ID() === (int) $custom_user_page_id ) {
							return;
						} else {
							$custom_user_page_id = Settings::get_custom_settings_page_id( '', $user );
							if ( ! empty( $custom_user_page_id ) && \get_the_ID() === (int) $custom_user_page_id ) {
								return;
							}
						}
					}

					// force a redirect to the 2FA set-up page if it exists.
					$custom_user_page_id = Settings::get_role_or_default_setting( 'custom-user-page-id', $user );
					if ( ! empty( $custom_user_page_id ) ) {
						\wp_redirect( Settings::get_custom_page_link( $user ) );
						exit;
					} else {
						$custom_user_page_id = Settings::get_custom_settings_page_id( '', $user );
						if ( ! empty( $custom_user_page_id ) && \get_the_ID() === (int) $custom_user_page_id ) {
							\wp_redirect( \get_permalink( $custom_user_page_id ) );
							exit;
						}
					}

					// There is nowhere to redirect, so we have to fall back to the default which is the dashboard. If the user does not have the required capabilities to view the dashboard - lets stop the redirection.
					if ( ! $has_cap ) {

						// Is there WOO installed? If so, then lets try to extract the redirection rules from there.
						if ( class_exists( 'WooCommerce', false ) ) {

							// Lets check if there is a 2FA implemented within the WOOCommerce myaccount page.
							$items = \wc_get_account_menu_items();

							if ( isset( $items['wp-2fa'] ) ) {

								if ( ! isset( $_GET['wp-2fa'] ) ) {
									$url = \add_query_arg(
										array(
											'wp-2fa' => '',
										),
										\get_permalink( \get_option( 'woocommerce_myaccount_page_id' ) )
									);

									\wp_redirect( $url );

									exit;
								}
							}
						}

						// Nothing suitable found - notify the admin and bail.
						$transient_name = WP_2FA_PREFIX . '_notified_admin_mail_nowhere_to_redirect_' . $user->ID;
						if ( false === \get_transient( $transient_name ) ) {
							$subject = sprintf(
							// translators: The username.
								\esc_html__(
									'User %1$s logged in without 2FA',
									'wp-2fa'
								),
								$user->user_login,
							);

							$text = sprintf(
							// translators: The username.
							// translators: the site name.
								\esc_html__(
									'2FA is enforced on the user %1$s on the website %2$s. However, since the WP 2FA plugin has not been configured properly it cannot enforce the user to configure 2FA, so the user logged in without 2FA.',
									'wp-2fa'
								),
								$user->user_login,
								\get_bloginfo( 'name' )
							);
							$text .= '<p>' . sprintf(
							// translators: the settings page.
							// translators: the support e-mail.
								\esc_html__(
									'To enforce 2FA on users logging in from non default WordPress login pages please configure the %1$s. If you need assistance, please contact us at %2$s.',
									'wp-2fa'
								),
								'<a href="' . \esc_url(
									\add_query_arg(
										array(
											'page' => 'wp-2fa-settings',
											'tab'  => 'integrations',
										),
										\network_admin_url( 'admin.php' )
									)
								) . '">front-end 2FA page</a>',
								'<a href="mailto:support@melapress.com">support@melapress.com</a>'
							) . '</p>';

							Settings_Page::send_email(
								\get_option( 'admin_email' ),
								$subject,
								$text
							);

							\set_transient( $transient_name, 'sent', DAY_IN_SECONDS * 2 );
						}

						return;
					}

					// custom 2FA page is not set-up, force redirect to the wizard in administration.
					\wp_redirect( Settings::get_setup_page_link() );
					exit;
				}
			}
		}

		/**
		 * Returns currently stored settings
		 *
		 * @return array
		 *
		 * @since 2.0.0
		 */
		public static function get_policy_settings() {
			/**
			 * Extensions could change the stored settings value, based on custom / different / specific for role settings.
			 *
			 * @param array - Value of the settings.
			 *
			 * @since 2.0.0
			 */
			$settings = \apply_filters( WP_2FA_PREFIX . 'policy_settings', self::$plugin_settings[ WP_2FA_POLICY_SETTINGS_NAME ] );

			return $settings;
		}

		/**
		 * Checks the action parameter against given list of actions
		 *
		 * @return bool
		 *
		 * @since 2.0.0
		 */
		private static function action_check() {
			if ( ! isset( $_REQUEST['action'] ) ) { //phpcs:ignore -- No nonce - that is not needed here
				return false;
			}
			$actions_array = array(
				'send_authentication_setup_email',
				'validate_authcode_via_ajax',
				'heartbeat',
				'regenerate_authentication_key',
				'send_backup_codes_email',
				'register_user_twilio',
				'register_user_clickatell',
			);

			/**
			 * Allows 3rd party providers to their own settings for the mail templates.
			 *
			 * @param array $actions_array - Array with the default settings.
			 *
			 * @since 2.0.0
			 */
			$actions_array = \apply_filters( WP_2FA_PREFIX . 'actions_check', $actions_array );

			return in_array( $_REQUEST['action'], $actions_array, true );
		}

		/**
		 * Updates the plugin settings, the settings hash in the database as well as a local (cached) copy of the settings.
		 *
		 * @param array  $settings - The settings values.
		 * @param bool   $skip_option_save If true, the settings themselves are not saved. This is needed when saving settings from settings page as WordPress options API takes care of that.
		 * @param string $settings_name - The name of the settings to extract.
		 *
		 * @since 2.0.0
		 */
		public static function update_plugin_settings( $settings, $skip_option_save = false, $settings_name = WP_2FA_POLICY_SETTINGS_NAME ) {
			// update local copy of settings.
			self::$plugin_settings[ $settings_name ] = $settings;

			if ( ! $skip_option_save ) {
				// update the database option itself.
				Settings_Utils::update_option( $settings_name, $settings );
			}

			if ( WP_2FA_POLICY_SETTINGS_NAME === $settings_name ) {
				// Create a hash for comparison when we interact with a use.
				$settings_hash = Settings_Utils::create_settings_hash( self::get_policy_settings() );
				Settings_Utils::update_option( WP_2FA_PREFIX . 'settings_hash', $settings_hash );
			}
		}

		/**
		 * Getter for the secret key of the plugin for the current instance
		 *
		 * Note: that is legacy code and will be removed.
		 *
		 * @return string
		 *
		 * @since 2.0.0
		 */
		public static function get_secret_key() {
			if ( null === self::$secret_key ) {
				if ( ! defined( File_Writer::SECRET_NAME ) ) {
					self::check_for_key();
				} else {
					self::$secret_key = constant( File_Writer::SECRET_NAME );
				}
			}

			return self::$secret_key;
		}

		/**
		 * Checks if the wp-config.php file is writable, show notice to the admin if it is not
		 *
		 * @return void
		 *
		 * @since 2.4.0
		 */
		public static function wp_not_writable() {

			if ( ! \defined( 'WP2FA_SECRET_IS_IN_DB' ) || true !== WP2FA_SECRET_IS_IN_DB ) {
				return;
			}

			if ( ! File_Writer::can_write_to_file( File_Writer::get_wp_config_file_path() ) ) {
				$whitelist_admin_pages = array(
					'wp-2fa_page_wp-2fa-settings',
					'wp-2fa_page_wp-2fa-settings-network',
					'toplevel_page_wp-2fa-policies',
					'toplevel_page_wp-2fa-policies-network',
					'wp-2fa_page_wp-2fa-help-contact-us',
					'wp-2fa_page_wp-2fa-help-contact-us-network',
					'wp-2fa_page_wp-2fa-policies-account',
					'wp-2fa_page_wp-2fa-policies-account-network',
					'wp-2fa_page_wp-2fa-reports',
					'wp-2fa_page_wp-2fa-reports-network',
				);
				$admin_page            = \get_current_screen();
				if ( in_array( $admin_page->base, $whitelist_admin_pages ) ) {
					?>
				<div class="notice notice-warning" id="config-update-notice">
						<?php
						$message = sprintf(
							'<p>%1$s <a href="https://melapress.com/support/kb/wp-2fa-add-2fa-plugin-encryption-key-wp-config/?&utm_source=plugin&utm_medium=link&utm_campaign=wp2fa" noopener target="_blank">%2$s</a><br>%3$s</p>',
							\esc_html__( 'For security reasons WP 2FA needs to store the private key in the wp-config.php file. However, it is unable to. This can happen because of restrictive permissions, or the file is not in the default location. To fix this you can:', 'wp-2fa' ) . '<br><br>' .

							\esc_html__( 'Option A) allow the plugin to write to the wp-config.php file temporarily by changing the wp-config.php permissions to 755. Once ready, click the button to proceed.', 'wp-2fa' ) . '<br>' .

							\esc_html__( 'Option B) Add the encryption key to the wp-config.php file yourself by ', 'wp-2fa' ),
							\esc_html__( 'following these instructions.', 'wp-2fa' ) . '<br>',
							\esc_html__(
								'Once you complete any of the above, please click the button below.
						',
								'wp-2fa'
							),
						)
						?>
						<?php echo $message; // phpcs:ignore ?>
					<p><button id="salt-update" type="button">
						<span><?php \esc_html_e( 'Write key to file now / Check for the key in file', 'wp-2fa' ); ?></span>
					</button></p>
				</div>
				<script>
				jQuery(document).ready(function($) {
					$(document).on('click', '#salt-update', function( event ) {
						const ajaxURL = (typeof wp2faWizardData != "undefined") ? wp2faWizardData.ajaxURL : ajaxurl;
						const nonceValue = '<?php echo \esc_attr( \wp_create_nonce( 'wp-2fa-set-salt-nonce' ) ); ?>';
						jQuery.ajax({
							url: ajaxURL,
							data: {
								action: 'set_salt_key',
								_wpnonce: nonceValue
							},
							success: function (data) {
								if (data.success) {
									jQuery('#config-update-notice .notice-dismiss').click();
								} else {
									alert(data.data);
								}
							},
							error: function (data) {
								alert(data.responseJSON.data[0].message);
							}
						});
					});
				});
				</script>
					<?php
				}
			}
		}

		/**
		 * Remove the user meta related with the code has been sent to the user.
		 * That is so we can lower the security by giving the option not to resend codes, so eventual brute force could succeed.
		 * The setting name - brute_force_disable
		 *
		 * @return void
		 *
		 * @since 2.5.0
		 */
		public static function clear_user_after_login() {
			User_Helper::remove_meta( WP_2FA_PREFIX . 'code_sent' );
		}

		/**
		 * Checks and sets the global wp2fa salt
		 *
		 * @return void
		 *
		 * @since 2.4.0
		 */
		private static function check_for_key() {
			self::$secret_key = Settings_Utils::get_option( 'secret_key' );
			if ( empty( self::$secret_key ) ) {
				self::$secret_key = base64_encode( Open_SSL::secure_random() ); // phpcs:ignore
				if ( ! File_Writer::save_secret_key( self::$secret_key ) ) {
					Settings_Utils::update_option( 'secret_key', self::$secret_key );
				}
			}
		}
	}
}
includes/classes/class-email-template.php000064400000005135150755130600014531 0ustar00<?php
/**
 * Responsible for email templates generation.
 *
 * @package    wp2fa
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 * @link       https://wordpress.org/plugins/wp-2fa/
 */

namespace WP2FA;

if ( ! class_exists( '\WP2FA\Email_Template' ) ) {

	/**
	 * Plain old PHP object to hold data for an email template.
	 *
	 * @package WP2FA
	 */
	class Email_Template {

		/**
		 * Template ID used for most settings form fields and setting keys.
		 *
		 * @var string
		 */
		private $id;

		/**
		 * The title of the email
		 *
		 * @var string
		 */
		private $title;

		/**
		 * Email template description
		 *
		 * @var string
		 */
		private $description;

		/**
		 * ID used for identifying the subject and body of the email. Defaults to $id.
		 *
		 * @var string ID used for identifying the subject and body of the email. Defaults to $id.
		 */
		private $email_content_id;

		/**
		 * True if the email can be turned on or off in the plugin settings.
		 *
		 * @var bool
		 */
		private $can_be_toggled = true;

		/**
		 * Email_Template constructor.
		 *
		 * @param string $id - The template ID.
		 * @param string $title - The title.
		 * @param string $description - The description.
		 */
		public function __construct( string $id, string $title, string $description ) {
			$this->id               = $id;
			$this->title            = $title;
			$this->description      = $description;
			$this->email_content_id = $id;
		}

		/**
		 * Can it be toggled
		 *
		 * @return bool
		 */
		public function can_be_toggled(): bool {
			return $this->can_be_toggled;
		}

		/**
		 * Sets the toggled flag for the template
		 *
		 * @param bool $can_be_toggled - Can it be toggled.
		 */
		public function set_can_be_toggled( $can_be_toggled ) {
			$this->can_be_toggled = $can_be_toggled;
		}

		/**
		 * Returns the template ID
		 *
		 * @return string
		 */
		public function get_id(): string {
			return $this->id;
		}

		/**
		 * Returns the title
		 *
		 * @return string
		 */
		public function get_title(): string {
			return $this->title;
		}

		/**
		 * Returns the description
		 *
		 * @return string
		 */
		public function get_description(): string {
			return $this->description;
		}

		/**
		 * Returns the mail content
		 *
		 * @return string
		 */
		public function get_email_content_id(): string {
			return $this->email_content_id;
		}

		/**
		 * Set content ID
		 *
		 * @param string $email_content_id - the ID of the content.
		 */
		public function set_email_content_id( string $email_content_id ) {
			$this->email_content_id = $email_content_id;
		}
	}
}
includes/classes/Authenticator/class-login.php000064400000122541150755130600015554 0ustar00<?php
/**
 * Responsible for WP2FA user's login forms.
 *
 * @package    wp2fa
 * @subpackage login
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 * @link       https://wordpress.org/plugins/wp-2fa/
 */

declare(strict_types=1);

namespace WP2FA\Authenticator;

use WP2FA\WP2FA;
use WP2FA\Methods\TOTP;
use WP2FA\Methods\Email;
use WP2FA\Admin\Setup_Wizard;
use WP2FA\Methods\Backup_Codes;
use WP2FA\Admin\Helpers\WP_Helper;
use WP2FA\Admin\Controllers\Methods;
use WP2FA\Admin\Helpers\User_Helper;
use WP2FA\Admin\Controllers\Settings;
use WP2FA\Authenticator\Authentication;
use WP2FA\Methods\Wizards\TOTP_Wizard_Steps;
use WP2FA\Admin\Views\Grace_Period_Notifications;
use WP2FA\Admin\SettingsPages\Settings_Page_Policies;

/**
 * Responsible for user login process.
 *
 * @since 2.0.0
 */
if ( ! class_exists( '\WP2FA\Authenticator\Login' ) ) {
	/**
	 * Class for handling logins.
	 */
	class Login {

		/**
		 * Keys used for backup codes
		 *
		 * @var string
		 */
		const USER_META_NONCE_KEY    = 'wp_2fa_nonce';
		const INPUT_NAME_RESEND_CODE = 'wp-2fa-email-code-resend';

		/**
		 * Keep track of all the password-based authentication sessions that
		 * need to invalidated before the second factor authentication.
		 *
		 * @var array
		 */
		private static $password_auth_tokens = array();

		/**
		 * Keep track of all the authentication cookies that need to be
		 * invalidated before the second factor authentication.
		 *
		 * @param string $cookie Cookie string.
		 *
		 * @return void
		 */
		public static function collect_auth_cookie_tokens( $cookie ) {
			$parsed = wp_parse_auth_cookie( $cookie );

			if ( ! empty( $parsed['token'] ) ) {
				self::$password_auth_tokens[] = $parsed['token'];
			}
		}

		/**
		 * Leave the memberpress alone
		 *
		 * @return bool
		 *
		 * @since 2.6.0
		 */
		public static function mepr_login(): bool {
			\remove_action( 'wp_login', array( '\WP2FA\Authenticator\Login', 'wp_login' ), 20, 2 );

			return true;
		}
		/**
		 * Handle the browser-based login.
		 *
		 * Note: All user meta data is in sync with the current version of plugin settings. This is taken care of in filter
		 * wp_authenticate_user.
		 *
		 * @since 0.1-dev
		 *
		 * @param string   $user_login Username.
		 * @param \WP_User $user \WP_User object of the logged-in user.
		 *
		 * @SuppressWarnings(PHPMD.ExitExpression)
		 */
		public static function wp_login( $user_login, $user ) {

			if ( class_exists( '\wpengine\sign_on_plugin\WPESignOnPlugin' ) && isset( $_REQUEST['nonce'] ) && isset( $_REQUEST['install_name'] ) ) {
				$user_nonce   = new \wpengine\sign_on_plugin\UserNonceHelper();
				$nonce        = \wp_unslash( $_REQUEST['nonce'] );
				$install_name = \wp_unslash( $_REQUEST['install_name'] );
				$nonce_data   = $user_nonce->get_nonce_data( $user->ID );

				// At this stage we are pretty sure that it is wp engine and everything is OK. $nonce_data must be empty because they are using user_meta and it is deleted - so there is no way to do a second validation, but that is enough.
				if ( empty( $nonce_data ) ) {
					return;
				}
			}

			// Flywheel auto login part starts here.
			if ( defined( 'FW_DIRECT_LOGIN_SHARED_KEY' ) && isset( $_REQUEST['payload'] ) && isset( $_REQUEST['nonce'] ) && function_exists( 'sodium_crypto_secretbox_open' ) ) {

				$playload = base64_decode( \wp_unslash( $_REQUEST['payload'] ) );
				$nonce    = base64_decode( \wp_unslash( $_REQUEST['nonce'] ) );
				$key      = file_get_contents( FW_DIRECT_LOGIN_SHARED_KEY );

				$playload = sodium_crypto_secretbox_open( $playload, $nonce, $key );
				if ( false !== $playload ) {
					return;
				}
			}
			// Flywheel auto login end.

			global $wp_current_filter;

			if ( isset( $wp_current_filter ) && ! empty( $wp_current_filter ) && \is_array( $wp_current_filter ) ) {
				foreach ( $wp_current_filter as $filter ) {
					if ( 'wp_ajax_nopriv_mepr_stripe_confirm_payment' === $filter ) {
						// That request comes from unprivileged user (maybe new), lets skip our checks in that case.
						return;
					}
				}
			}

			$user_status = User_Helper::get_2fa_status( $user );

			if ( User_Helper::USER_UNDETERMINED_STATUS === $user_status ) {
				User_Helper::remove_global_settings_hash_for_user( $user->ID );
			}
			User_Helper::set_login_date_for_user( time(), $user );

			WP2FA::clear_user_after_login();

			/**
			 * User is not required to use the 2FA
			 */
			if ( 'no_required_not_enabled' === $user_status ) {
				return;
			}

			$global_methods       = Methods::get_available_2fa_methods();
			$users_method         = User_Helper::get_enabled_method_for_user( $user );
			$users_method_removed = false;

			if ( User_Helper::is_enforced( $user ) && ! empty( $users_method ) && empty( \array_intersect( array( $users_method ), $global_methods ) ) ) {
				$users_method_removed = true;
			}

			// leave if the user has already got 2FA authentication configured.
			if ( ! $users_method_removed && User_Helper::is_user_using_two_factor( $user->ID ) ) {
				// phpcs:disable
				// phpcs:enable				
				try {
					Settings::is_provider_enabled_for_role( User_Helper::get_user_role(), User_Helper::get_enabled_method_for_user( $user ) );
					self::clear_session_and_show_2fa_form( $user );
					return;
				} catch ( \Exception $e ) {
					return;
				}
			}

			// Method is no longer available, but the user is using it - bail.
			if ( $users_method_removed && User_Helper::is_user_using_two_factor( $user->ID ) ) {
				return;
			}

			// leave if 2FA is not enforced, but optional.
			$enforcement_policy = WP2FA::get_wp2fa_setting( 'enforcement-policy' );
			if ( 'do-not-enforce' === $enforcement_policy ) {
				return;
			}

			// leave if the user is not required to have 2FA enabled due to and exclusion rule.
			if ( User_Helper::is_excluded( $user->ID ) ) {
				return;
			}

			// redirect to 2FA setup page if the 2FA configuration is enforced to happen instantly.
			$is_user_instantly_enforced = User_Helper::get_user_enforced_instantly( $user );
			if ( true === (bool) $is_user_instantly_enforced ) {
				wp_safe_redirect(
				self::get_2fa_setup_url( $user ) . ( ( isset( $_REQUEST['_wp_http_referer'] ) && ! empty( $_REQUEST['_wp_http_referer'] ) ) ? '?return=' . urlencode( \esc_url_raw( \wp_unslash( $_REQUEST['_wp_http_referer'] ) ) ) : '' ) // phpcs:ignore
				);
				exit();
			}

			// if there is some grace period configured, and it is not instant, we can let the users in (if they needed to
			// be blocked, this would have already happened in wp_authenticate).
			$grace_policy = WP2FA::get_wp2fa_setting( 'grace-policy' );
			if ( 'use-grace-period' === $grace_policy ) {

				if ( ! Grace_Period_Notifications::notify_using_dashboard( $user ) ) {
					$global_methods   = Methods::get_available_2fa_methods();
					$users_method     = User_Helper::get_enabled_method_for_user( $user );
					$is_nag_dismissed = User_Helper::get_nag_status();
					$is_nag_needed    = User_Helper::is_enforced( User_Helper::get_user_object()->ID );


					if ( ! $is_nag_dismissed && $is_nag_needed ) {

						$login_nonce = self::create_login_nonce( $user->ID );
						if ( ! $login_nonce ) {
							wp_die( \esc_html__( 'Failed to create a login nonce.', 'wp-2fa' ) );
						}

						$redirect_to = isset( $_REQUEST['redirect_to'] ) ? \esc_url_raw( wp_unslash( $_REQUEST['redirect_to'] ) ) : admin_url(); //phpcs:ignore

						self::show_2fa_form_grace_form( $user, $login_nonce['key'], $redirect_to );
					} else {
						return;
					}
				} else {
					return;
				}
			}

			$provider = User_Helper::get_enabled_method_for_user( $user );
			if ( '' === trim( (string) $provider ) ) {
				return;
			}

			self::clear_session_and_show_2fa_form( $user );
		}

		/**
		 * Generates the html form for the second step of the authentication process.
		 *
		 * @since 2.5.0
		 *
		 * @param \WP_User $user \WP_User object of the logged-in user.
		 * @param string   $login_nonce A string nonce stored in usermeta.
		 * @param string   $redirect_to The URL to which the user would like to be redirected.
		 * @param string   $error_msg Optional. Login error message.
		 */
		public static function show_2fa_form_grace_form( $user, $login_nonce, $redirect_to, $error_msg = '' ) {
			$interim_login   = isset( $_REQUEST['interim-login'] ) ? filter_var( wp_unslash( $_REQUEST['interim-login'] ), FILTER_VALIDATE_BOOLEAN ) : false; //phpcs:ignore
			$rememberme     = intval( self::rememberme() );
			$global_methods = Methods::get_available_2fa_methods();
			$users_method   = User_Helper::get_enabled_method_for_user( $user );

			if ( ! function_exists( 'login_header' ) ) {
				// We really should migrate login_header() out of `wp-login.php` so it can be called from an includes file.
				include_once WP_2FA_PATH . 'includes/functions/login-header.php';
			}

			login_header();

			if ( ! empty( $error_msg ) ) {
				echo '<div id="login_error"><strong>' . apply_filters( 'login_errors', \esc_html( $error_msg ) ) . '</strong><br /></div>';
			}
			?>
			<form name="grace_2fa_form" id="lgraceform" action="<?php echo \esc_url( self::login_url( array( 'action' => 'grace_2fa' ), 'login_post' ) ); ?>" method="post" autocomplete="off">
				<input type="hidden" name="wp-auth-id"    id="wp-auth-id"    value="<?php echo \esc_attr( $user->ID ); ?>" />
				<input type="hidden" name="wp-auth-nonce" id="wp-auth-nonce" value="<?php echo \esc_attr( $login_nonce ); ?>" />
				<?php if ( $interim_login ) : ?>
					<input type="hidden" name="interim-login" value="1" />
				<?php else : ?>
					<input type="hidden" name="redirect_to" value="<?php echo \esc_attr( $redirect_to ); ?>" />
				<?php endif; ?>
				<input type="hidden" name="rememberme" id="rememberme" value="<?php echo \esc_attr( $rememberme ); ?>"/>

				<?php
				$class = 'wp-2fa-nag';

				if ( User_Helper::get_user_needs_to_reconfigure_2fa( User_Helper::get_user_object() ) ) {
					$message = WP2FA::get_wp2fa_white_label_setting( 'default-2fa-resetup-required-notice', true );
				} else {
					$message = WP2FA::get_wp2fa_white_label_setting( 'default-2fa-required-notice', true );
				}


				$grace_expiry = (int) User_Helper::get_user_expiry_date( User_Helper::get_user_object() );

				$setup_url = Settings::get_setup_page_link();

				echo '<div class="' . \esc_attr( $class ) . '">';
				echo \wpautop( \wp_kses_post( WP2FA::replace_remaining_grace_period( $message, $grace_expiry ) ) );
				echo '<p>&nbsp;</p><div> <a href="' . \esc_url( $setup_url ) . '" class="button button-primary">' . \esc_html__( 'Configure 2FA now', 'wp-2fa' ) . '</a>';
				echo ' <a href="#" class="button button-secondary dismiss-user-configure-nag">' . \esc_html__( 'I\'ll do it later', 'wp-2fa' ) . '</a></div>';
				echo '</div>';

				/**
				 * Allows 3rd parties to render something at the end of the existing grace form.
				 *
				 * @param \WP_User $user - User for which the login form is shown.
				 * @param string $provider - The name of the provider.
				 *
				 * @since 2.0.0
				 */
				do_action( WP_2FA_PREFIX . 'grace_html_before_end', $user );
				?>
			</form>

			<?php
			/** This action is documented in wp-login.php */
			do_action( 'login_footer' );
			?>

		</div>
		<div class="clear"></div>
			<?php wp_print_scripts( 'jquery' ); ?>
		<script>
			jQuery( document ).on( 'click', '.dismiss-user-configure-nag', function(e) {
				e.preventDefault();
				const thisNotice = jQuery( this ).closest( '.notice' );
				jQuery.ajax( {
					url: '<?php echo admin_url( 'admin-ajax.php' ); ?>',
					data: {
						action: 'dismiss_nag'
					},
					complete: function() {
						window.location.replace( jQuery( '[name="redirect_to"]' ).val() );
					},
				} );
			} );
		</script>
		<style>
			#login form p:empty + p {
				margin-top: 15px;
			}
		</style>
		</body>
		</html>
			<?php

			exit();
		}

		/**
		 * Clears current user session and displays a "clone" of login screen with form to capture 2FA code.
		 *
		 * It also terminates current web request.
		 *
		 * @param \WP_User $user WordPress user object.
		 *
		 * @since 2.0.0
		 *
		 * @SuppressWarnings(PHPMD.ExitExpression)
		 */
		private static function clear_session_and_show_2fa_form( $user ) {
			/**
			 * The filter can be user to skip the 2FA "login" form in some cases. For example if the user has set their
			 * device as trusted.
			 *
			 * @param bool $skip
			 * @param \WP_User $user
			 *
			 * @return bool
			 */
			$should_form_be_skipped = apply_filters( WP_2FA_PREFIX . 'skip_2fa_login_form', false, $user );
			if ( $should_form_be_skipped ) {
				return;
			}

			// Invalidate the current login session to prevent from being re-used.
			self::destroy_current_session_for_user( $user );

			// Also clear the cookies which are no longer valid.
			wp_clear_auth_cookie();

			self::show_two_factor_login( $user );
			exit;
		}

		/**
		 * Retrieves the correct URL to the 2FA setup page. It handles configurable custom page as well as multisite.
		 *
		 * @param \WP_User $user \WP_User object of the logged-in user.
		 *
		 * @return string 2FA setup page URL.
		 *
		 * @since 2.0.0
		 * @since 2.5.0 $user parameter is added
		 */
		private static function get_2fa_setup_url( $user ) {

			$page_slug = Settings::get_role_or_default_setting( 'custom-user-page-url', $user );

			// Lets check for multisite first and if that is the case - lets search for that page on the user's default blog.
			if ( WP_Helper::is_multisite() && false !== Settings::get_role_or_default_setting( 'separate-multisite-page-url', $user ) && ! empty( $page_slug ) ) {
				$blog_id = User_Helper::get_user_default_blog( $user );
				if ( 0 === $blog_id ) {
					$new_page_permalink = '';
				} else {
					// Switch to the blog context.
					\switch_to_blog( $blog_id );

					$page_exists = Settings_Page_Policies::get_post_by_post_name( $page_slug, 'page' );

					// Restore global context.
					\restore_current_blog();

					if ( false === $page_exists ) {
						// Switch to the blog context.
						switch_to_blog( $blog_id );

						$result = Settings_Page_Policies::generate_custom_user_profile_page( $page_slug, User_Helper::get_user_role( $user ) );

						// Restore global context.
						restore_current_blog();

						if ( $result && ! is_wp_error( $result ) ) {
							$new_page_permalink = get_permalink( $result );
						}
					} else {
						$new_page_permalink = get_permalink( $page_exists->ID );
					}
				}
			} else {
				$page_exists = Settings_Page_Policies::get_post_by_post_name( $page_slug, 'page' );

				if ( $page_exists instanceof \WP_Post ) {
					$new_page_permalink = get_permalink( $page_exists->ID );
				}
			}

			if ( ! empty( $new_page_permalink ) ) {
				return $new_page_permalink;
			}

			// If multisite - redirect the user properly in the admin.
			if ( WP_Helper::is_multisite() ) {
				return Settings::get_setup_page_link();
			}

			return admin_url( 'profile.php' );
		}

		/**
		 * Destroy the known password-based authentication sessions for the current user.
		 *
		 * Is there a better way of finding the current session token without
		 * having access to the authentication cookies which are just being set
		 * on the first password-based authentication request.
		 *
		 * @param \WP_User $user User object.
		 *
		 * @return void
		 */
		public static function destroy_current_session_for_user( $user ) {
			$session_manager = \WP_Session_Tokens::get_instance( $user->ID );

			foreach ( self::$password_auth_tokens as $auth_token ) {
				$session_manager->destroy( $auth_token );
			}
		}

		/**
		 * Prevent login through XML-RPC and REST API for users with at least one
		 * 2FA method enabled.
		 *
		 * @param  \WP_User|\WP_Error $user Valid \WP_User only if the previous filters
		 *                                have verified and confirmed the
		 *                                authentication credentials.
		 *
		 * @return \WP_User|\WP_Error
		 */
		public static function filter_authenticate( $user ) {
			if ( $user instanceof \WP_User && self::is_api_request() && User_Helper::is_user_using_two_factor( $user->ID ) && ! self::is_user_api_login_enabled( $user->ID ) ) {
				return new \WP_Error(
					'invalid_application_credentials',
					\esc_html__( 'Error: API login for user disabled.', 'wp-2fa' )
				);
			}

			return $user;
		}

		/**
		 * Checks if the user should be locked and return WordPress error if that's the case. It doesn't check the account
		 * if it receives an error object as an input.
		 *
		 * @param \WP_User|\WP_Error $user User data.
		 * @param string             $password Password.
		 *
		 * @return \WP_User|\WP_Error
		 */
		public static function run_authentication_check( $user, $password ) {
			// we don't need to do anything if we already received an error.
			if ( is_a( $user, '\WP_Error' ) ) {
				return $user;
			}

			if ( User_Helper::is_user_locked( $user->ID ) && ! User_Helper::is_excluded( $user->ID ) ) {
				return self::get_user_locked_error();
			}

			return $user;
		}

		/**
		 * Generates an error object representing locked user account.
		 *
		 * @return \WP_Error User account locked error.
		 * @since 2.0.0
		 */
		public static function get_user_locked_error() {
			return new \WP_Error(
				'account_locked',
				\esc_html__( 'Your user account has been locked because you have not configured 2FA within the grace period. Please contact the website administrator to unlock your user and you can configure 2FA.', 'wp-2fa' )
			);
		}

		/**
		 * If the current user can login via API requests such as XML-RPC and REST.
		 *
		 * @param  integer $user_id User ID.
		 *
		 * @return boolean
		 */
		public static function is_user_api_login_enabled( $user_id ) {
			return (bool) apply_filters( 'two_factor_user_api_login_enable', false, $user_id );
		}

		/**
		 * Is the current request an XML-RPC or REST request.
		 *
		 * @return boolean
		 */
		public static function is_api_request() {
			if ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST ) {
				return true;
			}

			if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
				return true;
			}

			return false;
		}

		/**
		 * Display the login form.
		 *
		 * @since 0.1-dev
		 *
		 * @param \WP_User $user \WP_User object of the logged-in user.
		 */
		public static function show_two_factor_login( $user ) {
			if ( ! $user ) {
				$user = wp_get_current_user();
			}

			$login_nonce = self::create_login_nonce( $user->ID );
			if ( ! $login_nonce ) {
				wp_die( \esc_html__( 'Failed to create a login nonce.', 'wp-2fa' ) );
			}

			$redirect_to = isset( $_REQUEST['redirect_to'] ) ? \esc_url_raw( wp_unslash( $_REQUEST['redirect_to'] ) ) : admin_url(); //phpcs:ignore

			if ( self::is_woocommerce_activated() ) {
				$redirect_to = isset( $_REQUEST['redirect'] ) ? \esc_url_raw( wp_unslash( $_REQUEST['redirect'] ) ) : admin_url();
			}

			self::login_html( $user, $login_nonce['key'], $redirect_to );
		}

		/**
		 * Checks if woocommerce is enabled.
		 *
		 * @return boolean
		 *
		 * @since 2.2.2
		 */
		public static function is_woocommerce_activated(): bool {
			if ( class_exists( 'woocommerce' ) ) {
				return true;
			} else {
				return false;
			}
		}

		/**
		 * Display the Backup code 2fa screen.
		 *
		 * @since 0.1-dev
		 *
		 * @SuppressWarnings(PHPMD.ExitExpression)
		 */
		public static function backup_2fa() {
			if ( ! isset( $_GET['wp-auth-id'], $_GET['wp-auth-nonce'], $_GET['provider'] ) ) { //phpcs:ignore
				return;
			}

			// Filter $_GET array for security.
			$get_array = filter_input_array( INPUT_GET );
			$auth_id   = (int) $get_array['wp-auth-id'];
			$user      = \get_userdata( $auth_id );
			if ( ! $user ) {
				return;
			}

			$nonce = \sanitize_text_field( $get_array['wp-auth-nonce'] );
			if ( true !== self::verify_login_nonce( $user->ID, $nonce ) ) {
				wp_safe_redirect( get_bloginfo( 'url' ) );
				exit;
			}

			if ( ! isset( $get_array['provider'] ) ) {
				\wp_die( \esc_html__( 'Cheatin&#8217; uh?', 'wp-2fa' ), 403 );
			} else {
				$provider = \sanitize_textarea_field( \wp_unslash( $_GET['provider'] ) ); //phpcs:ignore
			}

			\delete_transient( 'wp_2fa_code_login_' . $user->ID );

			self::login_html( $user, $nonce, \esc_url_raw( \wp_unslash( $get_array['redirect_to'] ) ), '', $provider );

			exit;
		}

		/**
		 * Generates the html form for the second step of the authentication process.
		 *
		 * @since 0.1-dev
		 *
		 * @param \WP_User      $user \WP_User object of the logged-in user.
		 * @param string        $login_nonce A string nonce stored in usermeta.
		 * @param string        $redirect_to The URL to which the user would like to be redirected.
		 * @param string        $error_msg Optional. Login error message.
		 * @param string|object $provider An override to the provider.
		 */
		public static function login_html( $user, $login_nonce, $redirect_to, $error_msg = '', $provider = null ) {
			if ( ! $provider || ( Backup_Codes::METHOD_NAME === $provider && ! Backup_Codes::are_backup_codes_enabled_for_role( User_Helper::get_user_role( $user ) ) ) ) {
				$provider = User_Helper::get_enabled_method_for_user( $user );
			}

			$codes_remaining = Backup_Codes::codes_remaining_for_user( $user );
			$interim_login   = isset( $_REQUEST['interim-login'] ) ? filter_var( wp_unslash( $_REQUEST['interim-login'] ), FILTER_VALIDATE_BOOLEAN ) : false; //phpcs:ignore
			$rememberme      = intval( self::rememberme() );

			if ( ! function_exists( 'login_header' ) ) {
				// We really should migrate login_header() out of `wp-login.php` so it can be called from an includes file.
				include_once WP_2FA_PATH . 'includes/functions/login-header.php';
			}

			login_header();

			if ( ! empty( $error_msg ) ) {
				echo '<div id="login_error"><strong>' . apply_filters( 'login_errors', \esc_html( $error_msg ) ) . '</strong><br /></div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
			}
			?>
			<form name="validate_2fa_form" id="loginform" action="<?php echo \esc_url( self::login_url( array( 'action' => 'validate_2fa' ), 'login_post' ) ); ?>" method="post" autocomplete="off">
				<input type="hidden" name="provider"      id="provider"      value="<?php echo \esc_attr( $provider ); ?>" />
				<input type="hidden" name="wp-auth-id"    id="wp-auth-id"    value="<?php echo \esc_attr( $user->ID ); ?>" />
				<input type="hidden" name="wp-auth-nonce" id="wp-auth-nonce" value="<?php echo \esc_attr( $login_nonce ); ?>" />
				<?php if ( $interim_login ) : ?>
					<input type="hidden" name="interim-login" value="1" />
				<?php else : ?>
					<input type="hidden" name="redirect_to" value="<?php echo \esc_attr( $redirect_to ); ?>" />
				<?php endif; ?>
				<input type="hidden" name="rememberme" id="rememberme" value="<?php echo \esc_attr( $rememberme ); ?>"/>

				<?php
				// Check to see what provider is set and give the relevant authentication page.
				if ( TOTP::METHOD_NAME === $provider ) {
					TOTP_Wizard_Steps::totp_authentication_page( $user );
				} elseif ( Email::METHOD_NAME === $provider ) {
					self::email_authentication_page( $user );
				} elseif ( Backup_Codes::METHOD_NAME === $provider ) {
					self::backup_codes_authentication_page( $user );
				} else {

					/**
					 * Allows 3rd parties to render their own 2FA "login" form.
					 *
					 * @param \WP_User $user - User for which the login form is shown.
					 * @param string $provider - The name of the provider.
					 *
					 * @since 2.0.0
					 */
					do_action( WP_2FA_PREFIX . 'login_form', $user, $provider );
				}

				/**
				 * Gives the ability to remove the submit button from the plugin forms
				 *
				 * @param bool - Default at this point is true - no method is selected.
				 * @param array $input - The input array with all the data.
				 *
				 * @since 2.0.0
				 */
				$submit_button_disabled = apply_filters( WP_2FA_PREFIX . 'login_disable_submit_button', false, $user, $provider );
				if ( ! $submit_button_disabled ) {

					/**
					 * Allows 3rd parties to render something before the login button on the 2FA "login" form.
					 *
					 * @param \WP_User $user - User for which the login form is shown.
					 * @param string $provider - The name of the provider.
					 *
					 * @since 2.0.0
					 */
					do_action( WP_2FA_PREFIX . 'login_before_submit_button', $user, $provider );
					?>
					<p>
					<?php
					if ( function_exists( 'submit_button' ) ) {

						/**
						 * Using that filter, the default text of the login button could be changed
						 *
						 * @param callback - Callback function which is responsible for text manipulation.
						 *
						 * @since 2.0.0
						 */
						$button_text = apply_filters( WP_2FA_PREFIX . 'login_button_text', \esc_html__( 'Log In', 'wp-2fa' ) );

						submit_button( $button_text );
						?>
						<script type="text/javascript">
							setTimeout(function () {
								var d
								try {
									d = document.getElementById('authcode')
									d.value = ''
									d.focus()
								} catch (e) {}
							}, 200)
						</script>
					<?php } ?>
					</p>
					<?php
					if ( Email::METHOD_NAME === $provider ) {
						?>
						<p class="2fa-email-resend">
							<input type="submit" class="button"
							name="<?php echo \esc_attr( self::INPUT_NAME_RESEND_CODE ); ?>"
							value="<?php \esc_attr_e( 'Resend Code', 'wp-2fa' ); ?>"/>
						</p>
						<?php
					}
				} // submit button not disabled

				/**
				 * Allows 3rd parties to render something at the end of the existing login form.
				 *
				 * @param \WP_User $user - User for which the login form is shown.
				 * @param string $provider - The name of the provider.
				 *
				 * @since 2.0.0
				 */
				do_action( WP_2FA_PREFIX . 'login_html_before_end', $user, $provider );
				?>
			</form>

			<?php
			if ( Backup_Codes::METHOD_NAME !== $provider && Backup_Codes::are_backup_codes_enabled_for_role( User_Helper::get_user_role( $user ) ) && isset( $codes_remaining ) && $codes_remaining > 0 ) {
				$login_url = self::login_url(
					array(
						'action'        => 'backup_2fa',
						'provider'      => Backup_Codes::METHOD_NAME,
						'wp-auth-id'    => $user->ID,
						'wp-auth-nonce' => $login_nonce,
						'redirect_to'   => $redirect_to,
						'rememberme'    => $rememberme,
					)
				);
				?>
				<div class="backup-methods-wrap">
					<p class="backup-methods">
						<a href="<?php echo \esc_url( $login_url ); ?>">
							<?php \esc_html_e( 'Or, use a backup code.', 'wp-2fa' ); ?>
						</a>
					</p>
				</div>
				<?php
			}

			/**
			 * Allows 3rd parties to render something after the backup methods.
			 *
			 * @param \WP_User $user - User for which the login form is shown.
			 * @param string $provider - The name of the provider.
			 * @param string $login_nonce - The login nonce created.
			 * @param string $redirect_to - Where to redirect the user after successful login.
			 * @param bool $rememberme - Remember me status.
			 *
			 * @since 2.0.0
			 */
			do_action( WP_2FA_PREFIX . 'login_html_after_backup_providers', $user, $provider, $login_nonce, $redirect_to, $rememberme );

			?>

		<p id="backtoblog">
			<a href="<?php echo \esc_url( home_url( '/' ) ); ?>" title="<?php \esc_attr_e( 'Are you lost?', 'wp-2fa' ); ?>">
				<?php
				echo \esc_html(
					sprintf(
						// translators: %s: site name.
						__( '&larr; Back to %s', 'wp-2fa' ),
						get_bloginfo( 'title', 'display' )
					)
				);
				?>
			</a>
		</p>
		</div>
		<style>
		/* @todo: migrate to an external stylesheet. */
		.backup-methods-wrap {
			margin-top: 16px;
			padding: 0 24px;
		}
		.backup-methods-wrap a {
			color: #50575e;
			text-decoration: none;
		}
		ul.backup-methods {
			display: none;
			padding-left: 1.5em;
		}
		/* Prevent Jetpack from hiding our controls, see https://github.com/Automattic/jetpack/issues/3747 */
		.jetpack-sso-form-display #loginform > p,
		.jetpack-sso-form-display #loginform > div {
			display: block;
		}
		</style>

			<?php
			/** This action is documented in wp-login.php */
			do_action( 'login_footer' );
			?>
		<div class="clear"></div>
		</body>
		</html>
			<?php
		}

		/**
		 * Generate the 2FA login form URL.
		 *
		 * @param  array  $params List of query argument pairs to add to the URL.
		 * @param  string $scheme URL scheme context.
		 *
		 * @return string
		 */
		public static function login_url( $params = array(), $scheme = 'login' ) {
			if ( ! is_array( $params ) ) {
				$params = array();
			}

			$params = urlencode_deep( $params );

			return add_query_arg( $params, site_url( 'wp-login.php', $scheme ) );
		}

		/**
		 * Create the login nonce.
		 *
		 * @since 0.1-dev
		 *
		 * @param int $user_id User ID.
		 *
		 * @return array|bool
		 */
		public static function create_login_nonce( $user_id ) {
			$login_nonce = array();
			try {
				$login_nonce['key'] = bin2hex( random_bytes( 32 ) );
			} catch ( \Exception $ex ) {
				$login_nonce['key'] = wp_hash( $user_id . mt_rand() . microtime(), 'nonce' ); //phpcs:ignore
			}
			$login_nonce['expiration'] = time() + HOUR_IN_SECONDS;

			if ( ! update_user_meta( $user_id, self::USER_META_NONCE_KEY, $login_nonce ) ) {
				return false;
			}

			return $login_nonce;
		}

		/**
		 * Delete the login nonce.
		 *
		 * @since 0.1-dev
		 *
		 * @param int $user_id User ID.
		 *
		 * @return void
		 */
		public static function delete_login_nonce( $user_id ) {
			User_Helper::remove_meta( self::USER_META_NONCE_KEY, $user_id );
		}

		/**
		 * Verify the login nonce.
		 *
		 * @since 0.1-dev
		 *
		 * @param int    $user_id User ID.
		 * @param string $nonce Login nonce.
		 * @return bool
		 */
		public static function verify_login_nonce( $user_id, $nonce ) {
			$login_nonce = get_user_meta( $user_id, self::USER_META_NONCE_KEY, true );
			if ( ! $login_nonce ) {
				return false;
			}

			if ( $nonce !== $login_nonce['key'] || time() > $login_nonce['expiration'] ) {
				self::delete_login_nonce( $user_id );
				return false;
			}

			return true;
		}

		/**
		 * Login form validation.
		 *
		 * @since 0.1-dev
		 *
		 * @SuppressWarnings(PHPMD.ExitExpression)
		 */
		public static function login_form_validate_2fa() {
			if ( ! isset( $_POST['wp-auth-id'], $_POST['wp-auth-nonce'] ) ) { // phpcs:ignore
				return;
			}

			// If form data comes from 2 factor password reset - bounce.
			if ( isset( $_POST['reset'] ) && 'reset-2fa' === $_POST['reset'] ) { // phpcs:ignore
				return;
			}

			$auth_id = (int) $_POST['wp-auth-id']; // phpcs:ignore
			$user    = get_userdata( $auth_id );
			if ( ! $user ) {
				return;
			}

			$nonce = ( isset( $_POST['wp-auth-nonce'] ) ) ? sanitize_textarea_field( wp_unslash( $_POST['wp-auth-nonce'] ) ) : ''; // phpcs:ignore
			if ( true !== self::verify_login_nonce( $user->ID, $nonce ) ) {
				wp_safe_redirect( get_bloginfo( 'url' ) );
				exit;
			}

			if ( isset( $_POST['provider'] ) ) { // phpcs:ignore
				$provider  = sanitize_textarea_field( wp_unslash( $_POST['provider'] ) ); // phpcs:ignore
			}

			if ( ! Settings::is_provider_enabled_for_role( User_Helper::get_user_role( $user ), $provider ) ) {
				wp_die( __( '<p> <strong>WP-2FA</strong>: Please contact the administrator for further assistance!</p>', 'wp-2fa' ) . \esc_html__( 'Invalid provider.', 'wp-2fa' ) ); // phpcs:ignore
			}

			// If this is an email login, or if the user failed validation previously, lets send the code to the user.
			if ( Email::METHOD_NAME === $provider && true !== self::pre_process_email_authentication( $user ) ) {
				$login_nonce = self::create_login_nonce( $user->ID );
				if ( ! $login_nonce ) {
					wp_die( \esc_html__( 'Failed to create a login nonce.', 'wp-2fa' ) );
				}
			}

			// Validate TOTP.
			if ( TOTP::METHOD_NAME === $provider && true !== TOTP::validate_totp_authentication( $user ) ) {
				do_action(
					'wp_login_failed',
					$user->user_login,
					new \WP_Error(
						'authentication_failed',
						__( '<strong>Error</strong>: User can not be authenticated.', 'wp-2fa' )
					)
				);

				$login_nonce = self::create_login_nonce( $user->ID );
				if ( ! $login_nonce ) {
					wp_die( \esc_html__( 'Failed to create a login nonce.', 'wp-2fa' ) );
				}

				if ( Authentication::check_number_of_attempts( $user ) ) {
					self::login_html( $user, $login_nonce['key'], \esc_url_raw( wp_unslash( $_REQUEST['redirect_to'] ) ), \esc_html__( 'ERROR: Invalid verification code.', 'wp-2fa' ), $provider ); // phpcs:ignore
				} else {
					// Reached the maximum number of attempts - clear the attempts and redirect the user to the login page.
					Authentication::clear_login_attempts( $user );
					\wp_redirect( \wp_login_url() );
				}
				exit;
			}

			// Backup Codes.
			if ( Backup_Codes::METHOD_NAME === $provider && true !== Backup_Codes::validate_backup_codes( $user ) ) {
				do_action(
					'wp_login_failed',
					$user->user_login,
					new \WP_Error(
						'authentication_failed',
						__( '<strong>Error</strong>: User can not be authenticated.', 'wp-2fa' )
					)
				);
				$login_nonce = self::create_login_nonce( $user->ID );
				if ( ! $login_nonce ) {
					wp_die( \esc_html__( 'Failed to create a login nonce.', 'wp-2fa' ) );
				}

				if ( Backup_Codes::check_number_of_attempts( $user ) ) {

					self::login_html( $user, $login_nonce['key'], \esc_url_raw( wp_unslash( $_REQUEST['redirect_to'] ) ), \esc_html__( 'ERROR: Invalid backup code.', 'wp-2fa' ), $provider ); // phpcs:ignore
				} else {
					Backup_Codes::clear_login_attempts( $user );
					\wp_redirect( \wp_login_url() );
				}
				exit;
			}

			// Validate Email.
			if ( Email::METHOD_NAME === $provider && true !== self::validate_email_authentication( $user ) ) {
				do_action(
					'wp_login_failed',
					$user->user_login,
					new \WP_Error(
						'authentication_failed',
						__( '<strong>Error</strong>: User can not be authenticated.', 'wp-2fa' )
					)
				);

				$login_nonce = self::create_login_nonce( $user->ID );
				if ( ! $login_nonce ) {
					wp_die( \esc_html__( 'Failed to create a login nonce.', 'wp-2fa' ) );
				}

				if ( isset( $_REQUEST['wp-2fa-email-code-resend'] ) ) { //phpcs:ignore
					self::login_html( $user, $login_nonce['key'], \esc_url_raw( wp_unslash( $_REQUEST['redirect_to'] ) ), \esc_html__( 'A new code has been sent.', 'wp-2fa' ), $provider ); // phpcs:ignore
				} elseif ( Authentication::check_number_of_attempts( $user ) ) {
					$msg = \esc_html__( 'ERROR: Invalid verification code.', 'wp-2fa' );
					if ( empty( WP2FA::get_wp2fa_general_setting( 'brute_force_disable' ) ) ) {
						$msg .= \esc_html__( ' For security reasons you have been sent a new code via email. Please use this new code to log in.', 'wp-2fa' );
					}
					self::login_html( $user, $login_nonce['key'], \esc_url_raw( wp_unslash( $_REQUEST['redirect_to'] ) ), $msg, $provider ); // phpcs:ignore
				} else {
					Authentication::clear_login_attempts( $user );
					\wp_redirect( \wp_login_url() );
				}

				exit;
			}

			/**
			 * Allows 3rd parties to validate their own 2FA "login" form.
			 *
			 * @param \WP_User $user - User for which the login form is shown.
			 * @param string $provider - The name of the provider.
			 *
			 * @since 2.0.0
			 */
			do_action( WP_2FA_PREFIX . 'validate_login_form', $user, $provider );

			self::delete_login_nonce( $user->ID );

			$rememberme = false;
			$remember   = ( isset( $_REQUEST['rememberme'] ) ) ? filter_var( $_REQUEST['rememberme'], FILTER_VALIDATE_BOOLEAN ) : ''; // phpcs:ignore
			if ( ! empty( $remember ) ) {
				$rememberme = true;
			}

			wp_set_auth_cookie( $user->ID, $rememberme );

			/**
			 * Fires when the user is authenticated.
			 *
			 * @param \WP_User - the logged in user
			 *
			 * @since 2.0.0
			 */
			\do_action( WP_2FA_PREFIX . 'user_authenticated', $user );

			// Must be global because that's how login_header() uses it.
			global $interim_login;
			$interim_login = ( isset( $_REQUEST['interim-login'] ) ) ? filter_var( $_REQUEST['interim-login'], FILTER_VALIDATE_BOOLEAN ) : false; // phpcs:ignore

			if ( $interim_login ) {
				$message       = '<p class="message">' . __( 'You have logged in successfully.', 'wp-2fa' ) . '</p>';
				$interim_login = 'success'; // phpcs:ignore

				if ( ! function_exists( 'login_header' ) ) {
					// We really should migrate login_header() out of `wp-login.php` so it can be called from an includes file.
					include_once WP_2FA_PATH . 'includes/functions/login-header.php';
				}

				login_header( '', $message );
				?>
			</div>
				<?php
				/** This action is documented in wp-login.php */
				do_action( 'login_footer' );
				?>
			</body></html>
				<?php
				exit;
			}

			// Check if user has any roles/caps set - if they dont, we know its a "network" user.
			if ( WP_Helper::is_multisite() && ! get_active_blog_for_user( $user->ID ) && empty( $user->caps ) && empty( $user->caps ) ) {
				$redirect_to = user_admin_url();
			} else {
				$redirect_to = apply_filters( 'login_redirect', \esc_url_raw( wp_unslash( $_REQUEST['redirect_to'] ) ), \esc_url_raw( wp_unslash( $_REQUEST['redirect_to'] ) ), $user ); // phpcs:ignore
			}

			Backup_Codes::clear_login_attempts( $user );

			if ( ( empty( $redirect_to ) || 'wp-admin/' === $redirect_to || admin_url() === $redirect_to ) ) {
				// If the user doesn't belong to a blog, send them to user admin. If the user can't edit posts, send them to their profile.
				if ( WP_Helper::is_multisite() && ! get_active_blog_for_user( $user->ID ) && ! is_super_admin( $user->ID ) ) {
					$redirect_to = user_admin_url();
				} elseif ( WP_Helper::is_multisite() && ! $user->has_cap( 'read' ) ) {
					$redirect_to = get_dashboard_url( $user->ID );
				} elseif ( ! $user->has_cap( 'edit_posts' ) ) {
					$redirect_to = $user->has_cap( 'read' ) ? admin_url( 'profile.php' ) : home_url();
				}

				$redirect_to = apply_filters( WP_2FA_PREFIX . 'post_login_orphan_user_redirect', $redirect_to, $user );

				wp_redirect( $redirect_to );
				exit;
			}

			$redirect_to = apply_filters( WP_2FA_PREFIX . 'post_login_user_redirect', $redirect_to, $user );

			wp_safe_redirect( $redirect_to );

			exit;
		}

		/**
		 * Should the login session persist between sessions.
		 *
		 * @return boolean
		 */
		public static function rememberme() {
			$rememberme = false;

			if ( ! empty( $_REQUEST['rememberme'] ) ) { //phpcs:ignore
				$rememberme = true;
			}

			/**
			 * Changes the remember me value.
			 *
			 * @param bool $rememberme - Current state of the remember me variable.
			 *
			 * @since 2.0.0
			 */
			return (bool) apply_filters( WP_2FA_PREFIX . 'rememberme', $rememberme );
		}

		/**
		 * Prints the form that prompts the user to authenticate.
		 *
		 * @since 0.1-dev
		 *
		 * @param \WP_User $user \WP_User object of the logged-in user.
		 */
		public static function email_authentication_page( $user, $is_reset_protection = false ) {
			if ( ! $user ) {
				return;
			}

			$code_sent       = (bool) User_Helper::get_meta( WP_2FA_PREFIX . 'code_sent' );
			$use_default     = ( 'use-custom' == WP2FA::get_wp2fa_white_label_setting( 'use_custom_2fa_message' ) ) ? 'custom-text-email-code-page' : 'default-text-code-page';
			$text_to_display = ( $is_reset_protection ) ? 'default-text-pw-reset-code-page' : $use_default;

			if ( ! $code_sent && ! isset( $_REQUEST[ self::INPUT_NAME_RESEND_CODE ] ) ) {
				Setup_Wizard::send_authentication_setup_email( $user->ID, 'nominated_email_address', $is_reset_protection );
				if ( ! empty( WP2FA::get_wp2fa_general_setting( 'brute_force_disable' ) ) ) {
					User_Helper::set_meta( WP_2FA_PREFIX . 'code_sent', true );
				}
			}

			require_once ABSPATH . '/wp-admin/includes/template.php';
			?>
	<?php echo WP2FA::get_wp2fa_white_label_setting( $text_to_display, true ); // phpcs:ignore ?>
	<p>
	</br>
		<label for="authcode"><?php \esc_html_e( 'Verification Code:', 'wp-2fa' ); ?></label>
		<input type="tel" name="wp-2fa-email-code" id="authcode" class="input" value="" size="20" pattern="[0-9]*" autocomplete="off" />
		<script>
			const email_code = document.getElementById('authcode');
			email_code.addEventListener('input', function() {
			this.value = this.value.trim();
			});
		</script>
	</p>
			<?php
		}

		/**
		 * Validates the users input token.
		 *
		 * @since 0.1-dev
		 *
		 * @param \WP_User $user \WP_User object of the logged-in user.
		 * @return boolean
		 */
		public static function validate_email_authentication( $user ) {
			if ( ! isset( $user->ID ) || ! isset( $_REQUEST['wp-2fa-email-code'] ) ) { //phpcs:ignore
				return false;
			}
			return Authentication::validate_token( $user, \sanitize_text_field( \wp_unslash( $_REQUEST['wp-2fa-email-code'] ) ) );
		}

		/**
		 * Send the email code if missing or requested. Stop the authentication
		 * validation if a new token has been generated and sent.
		 *
		 * @param  \WP_User $user \WP_User object of the logged-in user.
		 * @return boolean
		 */
		public static function pre_process_email_authentication( $user ) {
			if ( isset( $user->ID ) && isset( $_REQUEST[ self::INPUT_NAME_RESEND_CODE ] ) ) { //phpcs:ignore -- nonce
				Setup_Wizard::send_authentication_setup_email( $user->ID );
				return true;
			}
			return false;
		}

		/**
		 * Prints the form that prompts the user to authenticate.
		 *
		 * @since 0.1-dev
		 *
		 * @param \WP_User $user \WP_User object of the logged-in user.
		 */
		public static function backup_codes_authentication_page( $user ) {
			require_once ABSPATH . '/wp-admin/includes/template.php';
			?>
		<p><?php echo WP2FA::get_wp2fa_white_label_setting( 'default-backup-code-page', true ); // phpcs:ignore ?></p><br/>
		<p>
			<label for="authcode"><?php \esc_html_e( 'Verification Code:', 'wp-2fa' ); ?></label>
			<input type="tel" name="wp-2fa-backup-code" id="authcode" class="input" value="" size="20" pattern="[0-9]*" autocomplete="off" />
			<script>
				const backup_code = document.getElementById('authcode');
				input.addEventListener('input', function() {
				this.value = this.value.trim();
				});
			</script>
		</p>
			<?php
		}

		/**
		 * Removes GoDaddy style which causing the form elements to be shown
		 *
		 * @return void
		 *
		 * @since 2.2.0
		 */
		public static function dequeue_style() {
			wp_dequeue_style( 'wpaas-sso-login' );
		}
	}
}
includes/classes/Authenticator/class-reset-passord.php000064400000017554150755130600017246 0ustar00<?php
/**
 * Responsible for WP2FA user's reset password forms.
 *
 * @package    wp2fa
 * @subpackage resetpassword
 *
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 *
 * @see       https://wordpress.org/plugins/wp-2fa/
 */

declare(strict_types=1);

namespace WP2FA\Authenticator;

use WP2FA\Methods\Email;
use WP2FA\Authenticator\Login;
use WP2FA\Admin\Helpers\User_Helper;
use WP2FA\Admin\Controllers\Settings;
use WP2FA\Admin\Views\Password_Reset_2FA;
use WP2FA\Extensions\RoleSettings\Role_Settings_Controller;

/**
 * Responsible for user login process.
 *
 * @since 2.5.0
 */
if ( ! class_exists( '\WP2FA\Authenticator\Reset_Password' ) ) {
	/**
	 * Class for handling logins.
	 */
	class Reset_Password {

		/**
		 * Show 2FA on password reset request.
		 *
		 * @param \WP_Error      $errors    A WP_Error object containing any errors generated
		 *                                 by using invalid credentials.
		 * @param \WP_User|false $user_data WP_User object if found, false if the user does not exist.
		 *
		 * @return \WP_Error|void
		 *
		 * @since 2.5.0
		 */
		public static function lostpassword_post( $errors, $user_data ) {
			if ( $errors->has_errors() ) {
				return $errors;
			}
			if ( false === $user_data ) {
				return $errors;
			}
			if ( ! ( $user_data instanceof \WP_User ) ) {
				return $errors;
			}

			global $current_user;

			if ( isset( $current_user ) && 0 !== $current_user->ID && $current_user->ID !== $user_data->ID ) {
				return;
			}

			if ( class_exists( 'WP2FA\Extensions\RoleSettings\Role_Settings_Controller' ) ) {
				$expire_action = Role_Settings_Controller::get_setting( User_Helper::get_user_role( $user_data ), Password_Reset_2FA::PASSWORD_RESET_SETTINGS_NAME, true );
			} else {
				$expire_action = Settings::get_role_or_default_setting( Password_Reset_2FA::PASSWORD_RESET_SETTINGS_NAME, null, null, true );
			}

			if ( 'password-reset-2fa' !== $expire_action ) {
				return $errors;
			}

			if ( User_Helper::get_reset_password_valid_for_user() ) {
				return $errors;
			}

			$login_nonce = Login::create_login_nonce( $user_data->ID );
			if ( ! $login_nonce ) {
				wp_die( \esc_html__( 'Failed to create a login nonce.', 'wp-2fa' ) );
			}

			self::show_two_factor_login( $user_data, $login_nonce['key'] );

			exit;
		}

		/**
		 * Generates the html form for the second step of the authentication process.
		 *
		 * @param \WP_User $user \WP_User object of the logged-in user.
		 * @param string   $login_nonce - The generated nonce.
		 * @param string   $error_msg - Error message (if any) to show.
		 *
		 * @since 2.5.0
		 */
		public static function show_two_factor_login( $user, $login_nonce, $error_msg = '' ) {

			if ( ! function_exists( 'login_header' ) ) {
				// We really should migrate login_header() out of `wp-login.php` so it can be called from an includes file.
				include_once WP_2FA_PATH . 'includes/functions/login-header.php';
			}

			$lostpassword_redirect = ! empty( $_REQUEST['redirect_to'] ) ? \sanitize_text_field( \wp_unslash( $_REQUEST['redirect_to'] ) ) : '';
			/**
			 * Filters the URL redirected to after submitting the lostpassword/retrievepassword form.
			 *
			 * @since 3.0.0
			 *
			 * @param string $lostpassword_redirect The redirect destination URL.
			 */
			$redirect_to = apply_filters( 'lostpassword_redirect', $lostpassword_redirect );

			if ( ! function_exists( 'login_header' ) ) {
				// We really should migrate login_header() out of `wp-login.php` so it can be called from an includes file.
				include_once WP_2FA_PATH . 'includes/functions/login-header.php';
			}

			login_header();

			if ( ! empty( $error_msg ) ) {
				echo '<div id="login_error"><strong>' . \esc_html( apply_filters( 'login_errors', \esc_html( $error_msg ) ) ) . '</strong><br /></div>';
			}
			?>
		<form name="lostpasswordform" id="lostpasswordform" action="<?php echo \esc_url( network_site_url( 'wp-login.php?action=lostpassword', 'login_post' ) ); ?>" method="post">
			<input type="hidden" name="wp-auth-id"    id="wp-auth-id"    value="<?php echo \esc_attr( $user->ID ); ?>" />
			<input type="hidden" name="wp-auth-nonce" id="wp-auth-nonce" value="<?php echo \esc_attr( $login_nonce ); ?>" />
			<input type="hidden" name="reset"      id="reset"      value="<?php echo \esc_attr( 'reset-2fa' ); ?>" />
			<input type="hidden" name="redirect_to" value="<?php echo \esc_attr( $redirect_to ); ?>" />
			<?php
			// Check to see what provider is set and give the relevant authentication page.

			Login::email_authentication_page( $user, true );
			?>
				<p>
			<?php

				/**
				 * Using that filter, the default text of the login button could be changed
				 *
				 * @param callback - Callback function which is responsible for text manipulation.
				 *
				 * @since 2.0.0
				 */
				$button_text = apply_filters( WP_2FA_PREFIX . 'new_password_button_text', \esc_html__( 'Get New Password', 'wp-2fa' ) );
			?>

					<p class="submit">
						<input type="submit" name="wp-submit" id="wp-submit" class="button button-primary button-large" value="<?php echo \esc_attr( $button_text ); ?>" />
					</p>
				</p>

				<p class="2fa-email-resend">
					<input type="submit" class="button"
					name="<?php echo \esc_attr( Login::INPUT_NAME_RESEND_CODE ); ?>"
					value="<?php \esc_attr_e( 'Resend Code', 'wp-2fa' ); ?>"/>
				</p>

		</form>
			<?php
			if ( function_exists( 'login_footer' ) ) {
				\login_footer( 'user_login' );
			}
		}

		/**
		 * Login form validation.
		 *
		 * @since 2.5.0
		 *
		 * @SuppressWarnings(PHPMD.ExitExpression)
		 */
		public static function login_form_validate_2fa() {
			if ( ! isset( $_POST['wp-auth-id'], $_POST['wp-auth-nonce'], $_POST['reset'] ) ) { // phpcs:ignore
				return;
			}

			$auth_id = (int) $_POST['wp-auth-id']; // phpcs:ignore
			$user    = get_userdata( $auth_id );
			if ( ! $user ) {
				return;
			}

			$nonce = ( isset( $_POST['wp-auth-nonce'] ) ) ? sanitize_textarea_field( wp_unslash( $_POST['wp-auth-nonce'] ) ) : ''; // phpcs:ignore
			if ( true !== Login::verify_login_nonce( $user->ID, $nonce ) ) {
				wp_safe_redirect( get_bloginfo( 'url' ) );
				exit;
			}

			$provider = Email::METHOD_NAME;

			// If this is an email login, or if the user failed validation previously, lets send the code to the user.
			if ( Email::METHOD_NAME === $provider && true !== Login::pre_process_email_authentication( $user ) ) {
				$login_nonce = Login::create_login_nonce( $user->ID );
				if ( ! $login_nonce ) {
					wp_die( \esc_html__( 'Failed to create a login nonce.', 'wp-2fa' ) );
				}
			}

			// Validate Email.
			if ( Email::METHOD_NAME === $provider && true !== Login::validate_email_authentication( $user ) ) {
				do_action(
					'wp_login_failed',
					$user->user_login,
					new \WP_Error(
						'authentication_failed',
						__( '<strong>Error</strong>: User can not be authenticated.', 'wp-2fa' )
					)
				);

				$login_nonce = Login::create_login_nonce( $user->ID );
				if ( ! $login_nonce ) {
					wp_die( \esc_html__( 'Failed to create a login nonce.', 'wp-2fa' ) );
				}

				if ( isset( $_REQUEST['wp-2fa-email-code-resend'] ) ) { //phpcs:ignore
					self::show_two_factor_login( $user, $login_nonce['key'], \esc_html__( 'A new code has been sent.', 'wp-2fa' ), $provider ); // phpcs:ignore
				} else {
						self::show_two_factor_login( $user, $login_nonce['key'], \esc_html__( 'ERROR: Invalid verification code.', 'wp-2fa' ), $provider ); // phpcs:ignore
				}

				exit;
			}

			User_Helper::set_reset_password_valid_for_user( true );

			$errors = retrieve_password( $user->user_email );

			if ( ! is_wp_error( $errors ) ) {
				$redirect_to = ! empty( $_REQUEST['redirect_to'] ) ? \sanitize_text_field( \wp_unslash( $_REQUEST['redirect_to'] ) ) : 'wp-login.php?checkemail=confirm';
				User_Helper::remove_reset_password_valid_for_user();
				wp_safe_redirect( $redirect_to );
				exit;
			}

			\wp_redirect( site_url( 'wp-login.php?action=lostpassword' ) );
		}
	}
}
includes/classes/Authenticator/class-open-ssl.php000064400000011443150755130600016202 0ustar00<?php
/**
 * Open SSL encrypt / decrypt class.
 *
 * @package   wp2fa
 * @copyright 2024 Melapress
 * @license   https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 * @link      https://wordpress.org/plugins/wp-2fa/
 */

declare(strict_types=1);

namespace WP2FA\Authenticator;

use WP2FA\WP2FA;
use WP2FA\Utils\Debugging;

use function WP2FA\Core\wp_salt;

/**
 * Open_SSL - Class for encryption and decryption of the string using open_ssl method
 *
 * @since 2.0.0
 */
if ( ! class_exists( '\WP2FA\Authenticator\Open_SSL' ) ) {

	/**
	 * Responsible for SSL operations
	 */
	class Open_SSL {

		const CIPHER_METHOD     = 'aes-256-ctr';
		const BLOCK_BYTE_SIZE   = 16;
		const DIGEST_ALGORITHM  = 'SHA256';
		const SECRET_KEY_PREFIX = 'lsc_';

		/**
		 * Internal cache var for the PHP ssl functions availability
		 *
		 * @var mixed|boolean
		 *
		 * @since 2.0.0
		 */
		private static $ssl_enabled = null;

		/**
		 * Encrypts given text
		 *
		 * @param string $text - Text to be encrypted.
		 *
		 * @return string
		 *
		 * @since 2.0.0
		 */
		public static function encrypt( string $text ): string {
			if ( self::is_ssl_available() ) {
				$iv   = self::secure_random( self::BLOCK_BYTE_SIZE );
				$key  = \openssl_digest( \base64_decode( wp_salt() ), self::DIGEST_ALGORITHM, true ); //phpcs:ignore
				$text = \openssl_encrypt(
					$text,
					self::CIPHER_METHOD,
					$key,
					OPENSSL_RAW_DATA,
					$iv
				);

				$text = \base64_encode( $iv . $text ); //phpcs:ignore
			}

			return $text;
		}

		/**
		 * Decrypts crypt text
		 *
		 * @param string $text - Encrypted text to be decrypted.
		 *
		 * @return string
		 *
		 * @since 2.0.0
		 */
		public static function decrypt( string $text ): string {
			Debugging::log( 'Decrypting a text: ' . $text );
			Debugging::log( 'Will use the following salt: ' . wp_salt() );

			if ( self::is_ssl_available() ) {
				$decoded_base = \base64_decode( $text ); //phpcs:ignore

				$key = \openssl_digest( \base64_decode( wp_salt() ), self::DIGEST_ALGORITHM, true ); //phpcs:ignore

				$ivlen = \openssl_cipher_iv_length( self::CIPHER_METHOD );

				$iv             = \substr( $decoded_base, 0, $ivlen );
				$ciphertext_raw = \substr( $decoded_base, $ivlen );
				$text           = \openssl_decrypt( $ciphertext_raw, self::CIPHER_METHOD, $key, OPENSSL_RAW_DATA, $iv );
			}
			Debugging::log( 'Decrypted text: ' . $text );

			return $text;
		}

		/**
		 * Decrypts crypt text
		 *
		 * @param string $text - Encrypted text to be decrypted.
		 *
		 * @return string
		 *
		 * @since 2.0.0
		 */
		public static function decrypt_legacy( string $text ): string {
			Debugging::log( 'Decrypting a text: ' . $text );

			if ( self::is_ssl_available() ) {
				$decoded_base = \base64_decode( $text ); //phpcs:ignore

				$key = \openssl_digest( \base64_decode( WP2FA::get_secret_key() ), self::DIGEST_ALGORITHM, true ); //phpcs:ignore

				$ivlen = \openssl_cipher_iv_length( self::CIPHER_METHOD );

				$iv             = \substr( $decoded_base, 0, $ivlen );
				$ciphertext_raw = \substr( $decoded_base, $ivlen );
				$text           = \openssl_decrypt( $ciphertext_raw, self::CIPHER_METHOD, $key, OPENSSL_RAW_DATA, $iv );
			}
			Debugging::log( 'Decrypted text: ' . $text );

			return $text;
		}

		/**
		 * Decrypts old wps_ secret strings
		 *
		 * @param string $text - The encrypted string.
		 *
		 * @return string
		 *
		 * @since 2.3.0
		 */
		public static function decrypt_wps( string $text ): string {
			Debugging::log( 'Decrypting a text: ' . $text );
			Debugging::log( 'Will use the following salt: ' . \wp_salt() );

			if ( self::is_ssl_available() ) {
				$decoded_base = \base64_decode( $text ); //phpcs:ignore

				$key = \openssl_digest( \base64_decode( \wp_salt() ), self::DIGEST_ALGORITHM, true ); //phpcs:ignore

				$ivlen = \openssl_cipher_iv_length( self::CIPHER_METHOD );

				$iv             = \substr( $decoded_base, 0, $ivlen );
				$ciphertext_raw = \substr( $decoded_base, $ivlen );
				$text           = \openssl_decrypt( $ciphertext_raw, self::CIPHER_METHOD, $key, OPENSSL_RAW_DATA, $iv );
			}
			Debugging::log( 'Decrypted text: ' . $text );

			return $text;
		}

		/**
		 * Generates random bytes by given size
		 *
		 * @param integer $octets - Number of octets for use for random generator.
		 *
		 * @return string
		 *
		 * @since 2.0.0
		 */
		public static function secure_random( int $octets = 0 ): string {
			if ( 0 === $octets ) {
				$octets = self::BLOCK_BYTE_SIZE;
			}

			return \random_bytes( $octets );
		}

		/**
		 * Checks the open ssl methods existence
		 *
		 * @return boolean
		 *
		 * @since 2.0.0
		 */
		public static function is_ssl_available(): bool {
			if ( null === self::$ssl_enabled ) {
				self::$ssl_enabled = false;
				if ( \function_exists( 'openssl_encrypt' ) ) {
					self::$ssl_enabled = true;
				}
			}

			return self::$ssl_enabled;
		}
	}
}
includes/classes/Authenticator/class-authentication.php000064400000034056150755130600017466 0ustar00<?php
/**
 * Responsible for WP2FA user's authentication.
 *
 * @package    wp2fa
 * @subpackage authentication
 * @copyright  2024 Melapress
 * @license    https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 * @link       https://wordpress.org/plugins/wp-2fa/
 */

/**
 * Class for handling general authentication tasks.
 *
 * @since 0.1-dev
 *
 * @package WP2FA
 */

declare(strict_types=1);

namespace WP2FA\Authenticator;

use WP2FA\Authenticator\Open_SSL;
use WP2FA\Admin\Helpers\User_Helper;
use WP2FA_Vendor\Endroid\QrCode\QrCode;
use WP2FA\Admin\Methods\Traits\Login_Attempts;
use WP2FA_Vendor\Endroid\QrCode\Writer\SvgWriter;

if ( ! class_exists( '\WP2FA\Authenticator\Authentication' ) ) {
	/**
	 * Authenticator class
	 */
	class Authentication {
		use Login_Attempts;

		const DEFAULT_KEY_BIT_SIZE        = 160;
		const DEFAULT_CRYPTO              = 'sha1';
		const DEFAULT_DIGIT_COUNT         = 6;
		const DEFAULT_TIME_STEP_SEC       = 30;
		const DEFAULT_TIME_STEP_ALLOWANCE = 4;

		/**
		 * Holds the name of the meta key for the allowed login attempts
		 *
		 * @var string
		 *
		 * @since 2.0.0
		 */
		private static $logging_attempts_meta_key = WP_2FA_PREFIX . 'email-login-attempts';

		/**
		 * The login attempts class
		 *
		 * @var \WP2FA\Admin\Controllers\Login_Attempts
		 *
		 * @since 2.0.0
		 */
		private static $login_attempts = null;

		/**
		 * String with the base32 characters
		 *
		 * @var string
		 */
		private static $base_32_chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';

		/**
		 * String with the decrypted key
		 *
		 * @var string
		 */
		private static $decrypted_key = '';

		/**
		 * Generate QR code
		 *
		 * @param  string $name  Username.
		 * @param  string $key   Auth key.
		 * @param  string $title Site title.
		 * @return string        QR code URL.
		 */
		public static function get_google_qr_code( $name, $key, $title = null ) {
			// Encode to support spaces, question marks and other characters.
			$name = rawurlencode( $name );

			self::decrypt_key_if_needed( $key );

			$target_url = ( 'otpauth://totp/' . $name . '?secret=' . $key );
			if ( isset( $title ) ) {
				$target_url .= ( '&issuer=' . rawurlencode( $title ) );
			}

			$qr = new QrCode( $target_url );
			$qr->setWriterOptions( array( 'exclude_xml_declaration' => true ) );
			$writer = new SvgWriter();
			$result = $writer->writeString( $qr );

			return 'data:image/svg+xml;base64,' . base64_encode( $result ); // phpcs:ignore
		}

		/**
		 * Generates key
		 *
		 * @param int $bitsize Number of bits to use for key.
		 *
		 * @return string $bitsize long string composed of available base32 chars.
		 */
		public static function generate_key( $bitsize = self::DEFAULT_KEY_BIT_SIZE ) {
			$bytes  = ceil( $bitsize / 8 );
			$secret = wp_generate_password( $bytes, true, true );

			$secret = Open_SSL::encrypt( self::base32_encode( $secret ) );

			if ( Open_SSL::is_ssl_available() ) {
				$secret = Open_SSL::SECRET_KEY_PREFIX . $secret;
			}

			return $secret;
		}

		/**
		 * Generates salt for the site
		 *
		 * @return string
		 *
		 * @since 2.4.0
		 *
		 * @throws \RuntimeException - throw exception if the generated string has unexpected characters or not a string.
		 */
		public static function generate_salt(): string {
			$secret = \wp_generate_password( 64, true, true );

			if ( ! is_string( $secret ) || strlen( $secret ) !== 64 ) {
				throw new \RuntimeException( 'Could not generate secret key.' );
			}

			return base64_encode( $secret );
		}

		/**
		 * Returns a base32 encoded string.
		 *
		 * @param string $string String to be encoded using base32.
		 *
		 * @return string base32 encoded string without padding.
		 */
		public static function base32_encode( $string ) {
			if ( empty( $string ) ) {
				return '';
			}

			$binary_string = '';

			foreach ( str_split( $string ) as $character ) {
				$binary_string .= str_pad( base_convert( (string) ord( $character ), 10, 2 ), 8, '0', STR_PAD_LEFT );
			}

			$five_bit_sections = str_split( $binary_string, 5 );
			$base32_string     = '';

			foreach ( $five_bit_sections as $five_bit_section ) {
				$base32_string .= self::$base_32_chars[ base_convert( str_pad( $five_bit_section, 5, '0' ), 2, 10 ) ];
			}

			return $base32_string;
		}

		/**
		 * Clears the value of the decrypted key
		 *
		 * @return void
		 *
		 * @since 2.6.0
		 */
		public static function clear_decrypted_key() {
			self::$decrypted_key = '';
		}

		/**
		 * Check if the TOTP secret key has a proper format.
		 *
		 * @param  string $key TOTP secret key.
		 *
		 * @return boolean
		 */
		public static function is_valid_key( $key ) {
			self::decrypt_key_if_needed( $key );

			$check = sprintf( '/^[%s]+$/', self::$base_32_chars );

			if ( 1 === preg_match( $check, $key ) ) {
				return true;
			}

			return false;
		}

		/**
		 * Checks if a given code is valid for a given key, allowing for a certain amount of time drift
		 *
		 * @param string $key      The share secret key to use.
		 * @param string $authcode The code to test.
		 *
		 * @return bool Whether the code is valid within the time frame
		 */
		public static function is_valid_authcode( $key, $authcode ) {

			self::decrypt_key_if_needed( $key );
			/**
			 * That allows to change the amount of thick for decrypting the key.
			 *
			 * @param bool - Default at this point is true - no method is selected.
			 *
			 * @since 2.0.0
			 */
			$max_ticks = apply_filters( WP_2FA_PREFIX . 'totp_time_step_allowance', self::DEFAULT_TIME_STEP_ALLOWANCE );

			// Array of all ticks to allow, sorted using absolute value to test closest match first.
			$ticks = range( - $max_ticks, $max_ticks );
			usort( $ticks, array( __CLASS__, 'abssort' ) );

			$time = time() / self::DEFAULT_TIME_STEP_SEC;
			foreach ( $ticks as $offset ) {
				$log_time    = $time + $offset;
				$calculdated = (string) self::calc_totp( $key, $log_time );
				if ( hash_equals( $calculdated, $authcode ) ) {
					return true;
				}
			}
			return false;
		}

		/**
		 * Calculate a valid code given the shared secret key
		 *
		 * @param string $key        The shared secret key to use for calculating code.
		 * @param mixed  $step_count The time step used to calculate the code, which is the floor of time() divided by step size.
		 * @param int    $digits     The number of digits in the returned code.
		 * @param string $hash       The hash used to calculate the code.
		 * @param int    $time_step  The size of the time step.
		 *
		 * @return string The totp code
		 */
		public static function calc_totp( $key, $step_count = false, $digits = self::DEFAULT_DIGIT_COUNT, $hash = self::DEFAULT_CRYPTO, $time_step = self::DEFAULT_TIME_STEP_SEC ) {

			$secret = self::base32_decode( $key );

			if ( false === $step_count ) {
				$step_count = floor( time() / $time_step );
			}

			$timestamp = self::pack64( $step_count );

			$hash = hash_hmac( $hash, $timestamp, $secret, true );

			$offset = ord( $hash[19] ) & 0xf;

			$code = (
				( ( ord( $hash[ $offset + 0 ] ) & 0x7f ) << 24 ) |
				( ( ord( $hash[ $offset + 1 ] ) & 0xff ) << 16 ) |
				( ( ord( $hash[ $offset + 2 ] ) & 0xff ) << 8 ) |
				( ord( $hash[ $offset + 3 ] ) & 0xff )
			) % pow( 10, $digits );

			return str_pad( (string) $code, $digits, '0', STR_PAD_LEFT );
		}

		/**
		 * Decode a base32 string and return a binary representation
		 *
		 * @param string $base32_string The base 32 string to decode.
		 *
		 * @throws \Exception If string contains non-base32 characters.
		 *
		 * @return string Binary representation of decoded string
		 */
		public static function base32_decode( $base32_string ) {

			$base32_string = strtoupper( $base32_string );

			if ( ! preg_match( '/^[' . self::$base_32_chars . ']+$/', $base32_string, $match ) ) {
				throw new \Exception( 'Invalid characters in the base32 string.' );
			}

			$l      = strlen( $base32_string );
			$n      = 0;
			$j      = 0;
			$binary = '';

			for ( $i = 0; $i < $l; $i++ ) {

				$n  = $n << 5; // Move buffer left by 5 to make room.
				$n  = $n + strpos( self::$base_32_chars, $base32_string[ $i ] );    // Add value into buffer.
				$j += 5; // Keep track of number of bits in buffer.

				if ( $j >= 8 ) {
					$j      -= 8;
					$binary .= chr( ( $n & ( 0xFF << $j ) ) >> $j );
				}
			}

			return $binary;
		}

		/**
		 * Used with usort to sort an array by distance from 0
		 *
		 * @param int $a First array element.
		 * @param int $b Second array element.
		 *
		 * @return int -1, 0, or 1 as needed by usort
		 */
		private static function abssort( $a, $b ) {
			$a = abs( $a );
			$b = abs( $b );
			if ( $a === $b ) {
				return 0;
			}
			return ( $a < $b ) ? -1 : 1;
		}

		/**
		 * Pack stuff
		 *
		 * @param string $value The value to be packed.
		 *
		 * @return string Binary packed string.
		 */
		public static function pack64( $value ) {
			// 64bit mode (PHP_INT_SIZE == 8).
			if ( PHP_INT_SIZE >= 8 ) {
				// If we're on PHP 5.6.3+ we can use the new 64bit pack functionality.
				if ( version_compare( PHP_VERSION, '5.6.3', '>=' ) && PHP_INT_SIZE >= 8 ) {
					return pack( 'J', $value );
				}
				$highmap = 0xffffffff << 32;
				$higher  = ( $value & $highmap ) >> 32;
			} else {
				/*
				 * 32bit PHP can't shift 32 bits like that, so we have to assume 0 for the higher
				 * and not pack anything beyond it's limits.
				 */
				$higher = 0;
			}

			$lowmap = 0xffffffff;
			$lower  = $value & $lowmap;

			return pack( 'NN', $higher, $lower );
		}

		/**
		 * Generate a random eight-digit string to send out as an auth code.
		 *
		 * @since 0.1-dev
		 *
		 * @param int          $length The code length.
		 * @param string|array $chars Valid auth code characters.
		 * @return string
		 */
		public static function get_code( $length = 6, $chars = '1234567890' ) {
			$code = '';
			if ( is_array( $chars ) ) {
				$chars = implode( '', $chars );
			}
			for ( $i = 0; $i < $length; $i++ ) {
				$code .= substr( $chars, wp_rand( 0, strlen( $chars ) - 1 ), 1 );
			}
			return $code;
		}

		/**
		 * Generate the user token.
		 *
		 * @since 0.1-dev
		 *
		 * @param int $user_id User ID.
		 * @return string
		 */
		public static function generate_token( $user_id ) {
			$token = self::get_code();

			User_Helper::set_email_token_for_user( \wp_hash( $token ), $user_id );
			return $token;
		}

		/**
		 * Validate the user token.
		 *
		 * @since 0.1-dev
		 *
		 * @param \WP_User $user User ID.
		 * @param string   $token User token.
		 *
		 * @return boolean
		 */
		public static function validate_token( $user, $token ) {
			$user_id      = $user->ID;
			$hashed_token = self::get_user_token( $user_id );
			// Bail if token is empty or it doesn't match.
			// This code is here just because people have no idea what is the difference between preaching and real life.
			if ( empty( $hashed_token ) || ( ! hash_equals( wp_hash( $token ), $hashed_token ) ) ) {
				self::increase_login_attempts( $user );
				return false;
			}


			// Ensure that the token can't be re-used.
			self::delete_token( $user_id );
			self::clear_login_attempts( $user );

			\delete_transient( 'wp_2fa_code_login_' . $user_id );

			return true;
		}

		/**
		 * Delete the user token.
		 *
		 * @since 0.1-dev
		 *
		 * @param int $user_id User ID.
		 */
		public static function delete_token( $user_id ) {
			User_Helper::remove_email_token_for_user( $user_id );
		}

		/**
		 * Check if user has a valid token already.
		 *
		 * @param  int $user_id User ID.
		 * @return boolean      If user has a valid email token.
		 */
		public static function user_has_token( $user_id ) {
			$hashed_token = self::get_user_token( $user_id );
			if ( ! empty( $hashed_token ) ) {
				return true;
			} else {
				return false;
			}
		}

		/**
		 * Get the authentication token for the user.
		 *
		 * @param  int $user_id    User ID.
		 *
		 * @return string|boolean  User token or `false` if no token found.
		 */
		public static function get_user_token( $user_id ) {


			$hashed_token = User_Helper::get_email_token_for_user( $user_id );

			if ( ! empty( $hashed_token ) && is_string( $hashed_token ) ) {
				return $hashed_token;
			}

			return false;
		}

		/**
		 * Returns list of all the auth apps and their properties
		 *
		 * @return array
		 */
		public static function get_apps(): array {
			return array(
				'authy'     => array(
					'logo' => 'authy-logo.png',
					'hash' => 'authy',
					'name' => 'Authy',
				),
				'google'    => array(
					'logo' => 'google-logo.png',
					'hash' => 'google',
					'name' => 'Google Authenticator',
				),
				'microsoft' => array(
					'logo' => 'microsoft-logo.png',
					'hash' => 'microsoft',
					'name' => 'Microsoft Authenticator',
				),
				'duo'       => array(
					'logo' => 'duo-logo.png',
					'hash' => 'duo',
					'name' => 'Duo Security',
				),
				'lastpass'  => array(
					'logo' => 'lastpass-logo.png',
					'hash' => 'lastpass',
					'name' => 'LastPass',
				),
				'freeotp'   => array(
					'logo' => 'free-otp-logo.png',
					'hash' => 'freeotp',
					'name' => 'FreeOTP',
				),
				'okta'      => array(
					'logo' => 'okta-logo.png',
					'hash' => 'okta',
					'name' => 'Okta',
				),
			);
		}

		/**
		 * Getter for the base32 character set
		 *
		 * @return string
		 *
		 * @since 2.0.0
		 */
		public static function get_base32_characters(): string {
			return self::$base_32_chars;
		}

		/**
		 * Validates base32 encoded string
		 *
		 * @param string $text = The text to be validated.
		 *
		 * @return boolean
		 *
		 * @since 2.0.0
		 */
		public static function validate_base32_string( string $text ): bool {
			if ( ! preg_match( '/^[' . self::$base_32_chars . ']+$/', $text, $match ) ) {
				return false;
			}

			return true;
		}

		/**
		 * Checks the given key and decrypts it if necessarily
		 *
		 * @param string $key - The key to check.
		 *
		 * @return string
		 *
		 * @since 2.0.0
		 */
		public static function decrypt_key_if_needed( string &$key ): string {
			if ( '' === trim( (string) self::$decrypted_key ) ) {
				if ( Open_SSL::is_ssl_available() && false !== \strpos( $key, Open_SSL::SECRET_KEY_PREFIX ) ) {
					$key = self::$decrypted_key = Open_SSL::decrypt( substr( $key, 4 ) ); // phpcs:ignore
				} else {
					self::$decrypted_key = $key;
				}
			}

			return ( $key = self::$decrypted_key ); // phpcs:ignore
		}
	}
}
includes/classes/Authenticator/index.php000064400000000046150755130600014443 0ustar00<?php
/**
 * Nothing to see here.
 */
includes/classes/index.php000064400000000046150755130600011631 0ustar00<?php
/**
 * Nothing to see here.
 */
wp-2fa.php000064400000014172150755130600006360 0ustar00<?php
/**
 * WP 2FA - Two-factor authentication for WordPress .
 *
 * @copyright Copyright (C) 2013-2024, Melapress - support@melapress.com
 * @license   http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License, version 3 or higher
 *
 * @wordpress-plugin
 * Plugin Name: WP 2FA - Two-factor authentication for WordPress 
 * Version:     2.8.0
 * Plugin URI:  https://melapress.com/
 * Description: Easily add an additional layer of security to your WordPress login pages. Enable Two-Factor Authentication for you and all your website users with this easy to use plugin.
 * Author:      Melapress
 * Author URI:  https://melapress.com/
 * Text Domain: wp-2fa
 * Domain Path: /languages/
 * License:     GPL v3
 * Requires at least: 5.0
 * Requires PHP: 7.3
 * Network: true
 *
 * @package WP2FA
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @fs_ignore /dist/, /extensions/, /freemius/, /includes/, /languages/, /third-party/, /vendor/
 */

use WP2FA\WP2FA;
use WP2FA\Utils\Migration;
use WP2FA\Extensions_Loader;
use WP2FA\Admin\Helpers\WP_Helper;
use WP2FA\Freemius\Freemius_Helper;
use WP2FA\Admin\Helpers\File_Writer;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

if ( defined( '\DISABLE_2FA_LOGIN' ) && \DISABLE_2FA_LOGIN ) {
	return;
}

// Useful global constants.
if ( ! defined( 'WP_2FA_VERSION' ) ) {
	define( 'WP_2FA_VERSION', '2.8.0' );
	define( 'WP_2FA_BASE', plugin_basename( __FILE__ ) );
	define( 'WP_2FA_URL', plugin_dir_url( __FILE__ ) );
	define( 'WP_2FA_PATH', WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . dirname( WP_2FA_BASE ) . DIRECTORY_SEPARATOR );
	define( 'WP_2FA_INC', WP_2FA_PATH . 'includes/' );
	define( 'WP_2FA_FILE', __FILE__ );
	define( 'WP_2FA_LOGS_DIR', 'wp-2fa-logs' );

	// Prefix used in usermetas, settings and transients.
	define( 'WP_2FA_PREFIX', 'wp_2fa_' );
	define( 'WP_2FA_POLICY_SETTINGS_NAME', WP_2FA_PREFIX . 'policy' );
	define( 'WP_2FA_SETTINGS_NAME', WP_2FA_PREFIX . 'settings' );
	define( 'WP_2FA_WHITE_LABEL_SETTINGS_NAME', WP_2FA_PREFIX . 'white_label' );
	define( 'WP_2FA_EMAIL_SETTINGS_NAME', WP_2FA_PREFIX . 'email_settings' );

	define( 'WP_2FA_PREFIX_PAGE', 'wp-2fa-' );
}

// phpcs:disable
		// phpcs:enable
		// Include files.
		require_once WP_2FA_INC . 'functions/core.php';

		// Require Composer autoloader if it exists.
		if ( file_exists( WP_2FA_PATH . 'vendor/autoload.php' ) ) {
			require_once WP_2FA_PATH . 'vendor/autoload.php';
		}

		// run any required update routines.
		Migration::migrate();

		// Setup_Wizard.
		if ( WP_Helper::is_multisite() ) {
			add_action( 'network_admin_menu', array( '\WP2FA\Admin\Setup_Wizard', 'network_admin_menus' ), 10 );
			add_action( 'admin_menu', array( '\WP2FA\Admin\Setup_Wizard', 'admin_menus' ), 10 );
		} else {
			add_action( 'admin_menu', array( '\WP2FA\Admin\Setup_Wizard', 'admin_menus' ), 10 );
		}

		// Activation/Deactivation.
		register_activation_hook( WP_2FA_FILE, '\WP2FA\Core\activate' );
		register_deactivation_hook( WP_2FA_FILE, '\WP2FA\Core\deactivate' );
		// Register our uninstallation hook.
		register_uninstall_hook( WP_2FA_FILE, '\WP2FA\Core\uninstall' );

		add_filter( 'plugins_loaded', array( '\WP2FA\WP2FA', 'init' ) );
		add_action( 'plugins_loaded', array( '\WP2FA\WP2FA', 'add_wizard_actions' ), 10 );


		// phpcs:disable
// phpcs:enable

if ( ! defined( File_Writer::SECRET_NAME ) ) {
	define( File_Writer::SECRET_NAME, WP2FA::get_secret_key() );

	define( 'WP2FA_SECRET_IS_IN_DB', true );
}

// phpcs:disable
/* @free:start */
// phpcs:enable
if ( ! function_exists( 'wp2fa_free_on_plugin_activation' ) ) {
	/**
	 * Takes care of deactivation of the premium plugin when the free plugin is activated.
	 *
	 * Note: This code MUST NOT be present in the premium version an is removed automatically during the build process.
	 *
	 * @since 2.0.0
	 */
	function wp2fa_free_on_plugin_activation() {
		$premium_version_slug = 'wp-2fa-premium/wp-2fa.php';
		if ( is_plugin_active( $premium_version_slug ) ) {
			deactivate_plugins( $premium_version_slug, true );
		}
		check_ssl();
	}

	register_activation_hook( __FILE__, 'wp2fa_free_on_plugin_activation' );
}
// phpcs:disable
/* @free:end */
// phpcs:enable

/*
 * Clears the config cache from the DB
 *
 * @return void
 *
 * @since 2.2.0
 */
add_action(
	'upgrader_process_complete',
	function () {
		delete_transient( 'wp_2fa_config_file_hash' );
	},
	10,
	2
);

if ( ! function_exists( 'check_ssl' ) ) {
	/**
	 * Checks if the required library is installed and cancels the process if not.
	 *
	 * @return void
	 *
	 * @since 2.2.0
	 */
	function check_ssl() {
		if ( ! \WP2FA\Authenticator\Open_SSL::is_ssl_available() ) {
			$html = '<div class="updated notice is-dismissible">
			<p>' . \esc_html__( 'This plugin requires OpenSSL. Contact your web host or website administrator so they can enable OpenSSL. Re-activate the plugin once the library has been enabled.', 'wp-2fa' )
			. '</p>
		</div>';

			echo $html; // phpcs:ignore

			exit();
		}
	}
}

if ( \PHP_VERSION_ID < 80000 && ! \interface_exists( 'Stringable' ) ) {
	interface Stringable { // phpcs:ignore
		/**
		 * Mockup function for PHP versions lower than 8.
		 *
		 * @return string
		 */
		public function __toString();
	}
}

if ( ! function_exists( 'str_starts_with' ) ) {
	/**
	 * PHP lower than 8 is missing that function but it required in the newer versions of our plugin.
	 *
	 * @param string $haystack - The string to search in.
	 * @param string $needle - The needle to search for.
	 *
	 * @return bool
	 *
	 * @since 2.6.4
	 */
	function str_starts_with( $haystack, $needle ): bool {
		if ( '' === $needle ) {
			return true;
		}

		return 0 === strpos( $haystack, $needle );
	}
}
readme.txt000064400000026307150755130600006554 0ustar00=== WP 2FA - Two-factor authentication for WordPress ===
Contributors: Melapress, robert681
Plugin URI: https://melapress.com/wordpress-2fa/
License: GPLv3
License URI: https://www.gnu.org/licenses/gpl.html
Tags: 2FA, two-factor authentication, multi step authentication, 2-factor authentication, WordPress authentication, two step authentication
Requires at least: 5.0
Tested up to: 6.6.2
Stable tag: 2.8.0
Requires PHP: 7.3.0

Harden your website's authentication; add two-factor authentication (2FA) for all your users with this easy-to-use plugin.

== Description ==

### A free and easy-to-use two-factor authentication plugin for WordPress

Add an extra layer of security to your WordPress website login pages and protect your users. Enable [two-factor authentication (2FA)](https://melapress.com/wordpress-2fa/?utm_source=wp+repo&utm_medium=repo+link&utm_campaign=wordpress_org&utm_content=wp2fa), the best protection against users using weak passwords, automated password guessing, and brute force attacks.

[youtube https://www.youtube.com/watch?v=vRlX_NNGeFo]

[Features](https://melapress.com/wordpress-2fa/features/?utm_source=wp+repo&utm_medium=repo+link&utm_campaign=wordpress_org&utm_content=wp2fa) | [Getting Started](https://melapress.com/support/kb/wp-2fa-plugin-getting-started/?utm_source=wp+repo&utm_medium=repo+link&utm_campaign=wordpress_org&utm_content=wp2fa) | [Get the Premium!](https://melapress.com/wordpress-2fa/pricing/?utm_source=wp+repo&utm_medium=repo+link&utm_campaign=wordpress_org&utm_content=wp2fa)

Use the WP 2FA plugin to enable two-factor authentication for your WordPress administrator, and to enforce your website users, or users with a specific role to use 2FA. This plugin is very easy to use; everything can be configured via wizards with clear instructions, so even non technical users can setup 2FA without requiring technical assistance.

#### MAINTAINED & SUPPORTED BY MELAPRESS

Melapress develops high-quality WordPress management and security plugins such as [Melapress Login Security](https://melapress.com/wordpress-login-security/?utm_source=wp+repo&utm_medium=repo+link&utm_campaign=wordpress_org&utm_content=wp2fa), [CAPTCHA 4WP](https://melapress.com/wordpress-captcha/?utm_source=wp+repo&utm_medium=repo+link&utm_campaign=wordpress_org&utm_content=wp2fa), and [WP Activity Log](https://melapress.com/wordpress-activity-log/?utm_source=wp+repo&utm_medium=repo+link&utm_campaign=wordpress_org&utm_content=wp2fa), the #1 user-rated activity log plugin for WordPress.

Browse our list of [WordPress security and administration plugins](https://melapress.com/?utm_source=wp+repo&utm_medium=repo+link&utm_campaign=wordpress_org&utm_content=wp2fa) to see how our plugins can help you better manage and improve the security and administration of your WordPress websites and users.
  
### WP 2FA key plugin features and capabilities
- Free Two-factor authentication (2FA) for all users
- Supports multiple 2FA methods
- An API that allows you to integrate supplementary 2FA methods
- Universal 2FA app support – generate codes from Google Authenticator, Authy & any other 2FA app
- Supports 2FA backup methods
- Wizard-driven plugin configuration & 2FA setup – no technical knowledge required
- Use 2FA policies to enforce 2FA with a grace period or require users to instantly setup 2FA upon logging in
- No WordPress dashboard access is required for users to set up 2FA
- Fully editable email templates
- Protection against automated password & dictionary attacks
- Much more
 
### Upgrade to WP 2FA Premium and get even more

The premium version of WP 2FA comes bundled with even more features to take your WordPress website login security to the next level.

With the premium edition of WP 2FA, you get more 2FA methods, 1-click integration with WooCommerce, trusted devices feature, extensive white labeling capabilities, and much more!

### Premium features list

-   Everything in the free version
-   Full white labeling capabilities (change all the text and look and feel in wizards, emails, SMS and 2FA pages)
- 	[YubiKey hardware key support](https://melapress.com/support/kb/wp-2fa-hardware-key/?utm_source=wp+repo&utm_medium=repo+link&utm_campaign=wordpress_org&utm_content=wp2fa)
-   Several other additional 2FA methods (such as 2FA over SMS, link in email & more)
-   [Trusted devices](https://melapress.com/support/kb/wp-2fa-configure-2fa-trusted-devices/?utm_source=wp+repo&utm_medium=repo+link&utm_campaign=wordpress_org&utm_content=wp2fa) (no 2FA required for a configured period of time)
-   Require 2FA on password reset
-   One-click integration to set up [WooCommerce and two-factor authentication (2FA)](https://melapress.com/woocommerce-2fa/?utm_source=wp+repo&utm_medium=repo+link&utm_campaign=wordpress_org&utm_content=wp2fa)
-   Much more

Refer to the [WP 2FA plugin features and benefits page](https://melapress.com/wordpress-2fa/features/?utm_source=wp+repo&utm_medium=repo+link&utm_campaign=wordpress_org&utm_content=wp2fa) to learn more about the benefits of upgrading to WP 2FA Premium.
    
## Free and premium support

Premium world-class support for WP 2FA is free via email or through the WordPress support forums.

Note: paid customer support is given priority and is provided via one-to-one email. Upgrade to Premium to benefit from priority support.

For any other queries, feedback, or if you simply want to get in touch with us, please use our [contact form](https://melapress.com/contact/?utm_source=wp+repo&utm_medium=repo+link&utm_campaign=wordpress_org&utm_content=wp2fa).

## As featured on:

- [WP Beginner](https://www.wpbeginner.com/plugins/how-to-add-two-factor-authentication-for-wordpress/)
- [IsitWP](https://www.isitwp.com/best-wordpress-security-authentication-plugins/)
- [WP Astra](https://wpastra.com/two-factor-authentication-wordpress/)
- [MainWP](https://mainwp.com/how-to-use-the-wp-2fa-plugin-on-your-child-sites/)
- [FixRunner](https://www.fixrunner.com/wordpress-two-factor-authentication/)
- [Inmotion Hosting](https://www.inmotionhosting.com/support/edu/wordpress/plugins/wp-2fa/)
- [WP Marmite](https://wpmarmite.com/en/wordpress-two-factor-authentication/)

## Related links and documentation:

You can find more detailed information about 2FA and its benefits in the links below

- [The benefits of using 2FA on WordPress](https://melapress.com/benefits-2fa-wordpress/?utm_source=wp+repo&utm_medium=repo+link&utm_campaign=wordpress_org&utm_content=wp2fa)
- [Beginner’s guide to two-factor authentication](https://melapress.com/what-is-2fa-beginners-guide/?utm_source=wp+repo&utm_medium=repo+link&utm_campaign=wordpress_org&utm_content=wp2fa)
- [Setting up Google Authenticator for WordPress 2FA](https://melapress.com/google-authenticator-app-wordpress-2fa/?utm_source=wp+repo&utm_medium=repo+link&utm_campaign=wordpress_org&utm_content=wp2fa)
- [List of supported 2FA apps](https://melapress.com/support/kb/wp-2fa-configuring-2fa-apps/?utm_source=wp+repo&utm_medium=repo+link&utm_campaign=wordpress_org&utm_content=wp2fa)
- [The definitive guide to WordPress security](https://melapress.com/wordpress-security/?utm_source=wp+repo&utm_medium=repo+link&utm_campaign=wordpress_org&utm_content=wp2fa)
- [Official Melapress website](https://melapress.com/?utm_source=wp+repo&utm_medium=repo+link&utm_campaign=wordpress_org&utm_content=wp2fa)

== Installing WP 2FA ==

###From within WordPress

1.  Navigate to ‘Plugins > Add New’
2.  Search for ‘WP 2FA’
3.  Install & activate WP 2FA from your Plugins page
  
###Manually

1.  Download the plugin from the WordPress plugins repository
2.  Unzip the zip file and upload the folder to the /wp-content/plugins/ directory
3.  Activate the WP 2FA plugin through the ‘Plugins’ menu in WordPress

== Frequently Asked Questions ==

= Does the plugin send any data to Melapress? =
No, the plugin does not send any data to us whatsoever. The only data we recieve is license data from the premium edition of the plugin.

= Does the plugin receive updates? =
We update the plugin fairly regularly to ensure the plugin continues to run in tip-top shape while adding new features from time to time.

= Support and Documentation =
Please refer to our [support pages](https://melapress.com/support/?utm_source=wp+repo&utm_medium=repo+link&utm_campaign=wordpress_org&utm_content=wp2fa) for all the technical and product documentation.

= How can I report security bugs? =
You can report security bugs through the Patchstack Vulnerability Disclosure Program. Please use this [form](https://patchstack.com/database/vdp/wp-2fa). For more details please refer to our [Melapress plugins security program](https://melapress.com/plugins-security-program/).

== Screenshots ==

1. The first-time install wizard allows you to setup 2FA on your website and for your user within seconds.
2. The wizards make setting up 2FA very easy, so even non technical users can setup 2FA without requiring help.
3. You can require users to enable 2FA and also give them a grace period to do so.
4. Users can also use one-time codes via email as a two-factor authentication method.
5. You can use policies to require users to instantly set up and use 2FA, so the next time they login they will be prompted with this.
6. You can give users a grace period until they configure 2FA. You can also specify what should the plugin do once the grace period is over.
7. It is recommended for all users to also generate backup codes, in case they cannot access the primary device.
8. In the user profile users only have a few 2FA options, so it is not confusing for them and everything is self explanatory.

== Changelog ==

= 2.8.0 (2024-07-17) =

* **New features**
	 * Out of the box support for Yubico - [use any YubiKey hardware key by Yubico as a 2FA method to log in to your WordPress website](https://melapress.com/support/kb/wp-2fa-hardware-key/).

 * **Plugin & functionality improvements**
	 * Bumped up the minimum supported PHP version from 7.2 to 7.3.
	 * Updated a number of strings in the settings + improved help text.
	 * The names of debug log file in uploads directory are now randomized.
	 * Updated the default text in different sections of the wizard to simplify things and improve UX.
	 * Adjusted the order in which the 2FA methods are listed.
	 * Updated the features' page in the plugin - added the new features etc.
	 * Updated all UTM parameters in the plugin's URLs and links.

 * **Bug fixes**
	 * Fixed: PHP fatal error in class-email-wizard-steps.php in some edge cases.
	 * Fixed: Apostrophe character shows up as ASCII in email subject.
	 * Fixed: Error with importing plugin's settings from one website to another in some edge cases.
	 * Fixed: The grace period expiration setting did not have a default value / setting.
	 * Removed reference to Premium backup methods in the free edition's wizard.
	 * Fixed: Redirecting to frontend 2FA page without permalinks set up does not work.
	 * Fixed: Some user profile 2FA buttons were not functioning properly when used on mobile.
	 * Fixed: Data was not always / all deleted when the setting "Delete data upon uninstall" was enabled.
	
Refer to the complete [plugin changelog](https://melapress.com/support/kb/wp-2fa-plugin-changelog/?utm_source=wordpress.org&utm_medium=referral&utm_campaign=WP2FA&utm_content=plugin+repos+description) for more detailed information about what was new, improved and fixed in previous version updates of WP 2FA.
index.php000064400000000046150755130600006366 0ustar00<?php
/**
 * Nothing to see here.
 */
languages/wp-2fa-de_DE.mo000064400000227146150755130600011117 0ustar00��.���"�.(�.E�.4(/]/o/q/~/
�/
�/&�/�/�/00(0,@0 m0�0
�0�0�0�031N;1�1�1	�1	�1,�1�112V32��2I3c3=s3-�3��3!�4�4
5f5
{5*�5�5�5`�5!>6`6u6�6Z�6	7H 7i7�7�7�7��7u8�8=�8�8��8g�9K;;�;�;�;�;�;�;�;'<24<g<|<�<��<u=$�=9�=n�=�V>$?1?H?"U?%x?
�?�?�?)�?�?@92@3l@��@
dAoA/�A�A!�A��A'xB�B
�B:�B�BCC+CBCNCiC/yC$�C�C
�C�CZ
D�hDG�D7DE9|E4�EZ�EFF�OF:�FG!0G�RG	�GHHH,HGH
WHeH
|H�H�H
�H
�H�H�H�H.I/IEI!`I�I�I�I#�I �I
JJ2JNJ
jJxJ�J�J	�J6�J1�J�$K`�K9L@LEL_L�qL�LM0MHM^MmM�M�M�M�M�M�N*O+O(8O5aO��OKPXP_PfPkP}P��PmQ'tQ�Q0�Q3�QCR`R	qR	{R�R
�R��R�=S��S)�TM�U�
V��VW<W"QW'tW-�W�W�W�WnX�oXVY5qY�Y
�Y#�Y
�Y�YZZ6Z5KZ�Z�Z�Z"�Z�Z[[ ['[<[M[Y[Dr[��[Q\^\Or\�\<�\]�]
�]�]	�]�]�]�]�]�]^^6^B^`^f^�m^X�^cO`d�`aa*,aWaJka$�a#�a�a!b@b\b!nb5�b�b4�bc-cMcdczc�c�c�cF�c0�c�,d4�d�eJ�e:fNYf�fB�fGgJSg2�gC�gZhRph��h �i�i�i�i�i�ijjj*1jG\j��j-k"Fk+ik�k�k!�k]�kwLl �l�l
�lmm/m7mLmYmum�m	�m�m�mJ�m�mn!/nQn(mn�n�n�n�n
�n&�noo,o<oLo"Uoxo�o:�oP�o�&p�p�p#�pB�p:q�Uq�2r�r�r�r
�r��r�s�s��s
�t��t�+u~�u�/v&�v�v'�vtw6�w�w��x�sy��yi�z�%{��{8?|Jx}+�}i�}Y~Di~Z�~R	G\:�H�?(�Kh�R��H��P�+�;�HX�\��k��j�	s�}�(��}��*���"Ʉ� ���&%�L�`�#l�I��څ���#�*�I�#c��������:K���������n]��̊Nz�$ɋ�9�B�^�1s�����Ì�ˌ*����ݍ'��%�:�wX�	Ўڎ�0��
/�=�M��^�
���
��()�R�Z�Tl�V��T�>m�����R����ڔ�j��S�<�-+�;Y���+��Иv�g�y��}�#�">�%a�i��O�@A� ��(��-̜!��*�cG�$��Н!��,�,E�r� ��	�����(t�*��	ȟҟ$ן�������'��̠ ޠ���
��,�=�N�	j�t����+T�[��7ܣ�)�+�8�W�
h�/s���$��ݤ�
�3'� [�|�����'å!�J
�]X���Ŧ	̦
֦'��8$��]��ߧs��'�>6�6u�"��4Ϫ���3�ë4ԫ	��t-�.��Ѭ �#�e+���]���+�@�_��l�N�c�Lu�¯ί�ҰR��	ز����-�>�W�/l�1��γ����2�IN������*.�Y�1m�(��ȷ�"�.%�$T� y�S��M�<�L�%Y�>���˺��%j�
����?������,�L�"[�~�7��3ɼ���%�{C����QU�D��;�8(�ja�
̿�ڿT|�!� �������
�#�B�R�_�x�&��	������'�����9!�'[�&��������%��1$�3V�����������(�*8�c�
j�Gu�1�����b���	�'�@��[���#�0�P�i�!��&��2��1��0�9�JQ�@����:��L*��w�
#�1�8�?�E�W�u�~�7��!��2��*�T<���������
���������9�`��W`�����R�*��)�B�^�t����� ������j� h�W������*�
/�$=�b�)q���@����+�&7�$^���������������(��M��f���`,���d��	�����������
����
�+�)I�s�
��&����������`�v��Uf�����)���F�5`�'��!��&���'�47�Ll�"��a��>�/W�#������������t�;w����K}�3��I��G�S^���S��V�[v�H��J�if�r��C�/J�z���������������Z�bc����'d�+������(��/�dH����#5�$Y�
~���������$��$�%�8�X�q�w�L���� ��+�:�3X�����
������6���!(�J�b�
y�)������N��`3����!5�W�`�Gz�$������������
����������������e������0R���,����HT�&��������:��8����hO��mG��UJg=�N�/?Pog�R(E{Y�>�Z]*F�>�hw��		7	�I	��	�
/�
�
,�
#77o�1�a�.]G�
+�
"�
2/!b#���Hi���F�����-U�4+!`L�%�#�N"h����'�#��,�%/=�m
&%-3S
�����
N
\jw+�
��o�gE`�e�t8N�TBD ��!B>"=�"L�"1#H>#4�#��#O$f$�i$|,%I�&>�&o2'X�'W�'&S(5z(D�(9�(?/)�o)'�)*$1*V*-i*,�* �*&�*+�++�+,),V,^,0c,#�,�,��,J-4R-�- �-�-�-�-�-�-..4.%@.�������'��������PH[<c=�q�p������%s��SCQ���#d�� �ma���AEs�V���0��O�k*G����i,+2��v��&&��K9�]i��"hVD{I��+�� Ren���1��I.��������W���%�	�~��a���(�-�Z���� �`Z�?��?�3,!9��������6f5�*�j�
�)v6t��y~���x���#m�+�������>��pN�'��L&u�u��{n@/Yr��g�3�[	5�fr|'���z��
���T��4o:�F��7"����
����*M�����-�z��S48`<��c�k�.$���w�^UXB��L��O��t;-��%�����J��,��l��}��
g.��1�M�D������}�$�$��N!���W��UF�x�0	b�Y�(#JR�j�\CT��=
!w]QKP�)AH�X\y@�;">h����b�|�)G��(^��q�el������_��8��2��:o�7E�_�dB�
�������/� entry in your WordPress dashboard menu.%s You need to renew your license to continue using premium features.%s unused code remaining.%s unused codes remaining.&larr; Back to %s)2FA Policies2FA Settings Updated2FA Setup:2FA Status2FA apps article on our knowledge base2FA background color2FA button background color2FA code over email2FA code page text2FA code via mobile app2FA login with SMS, WhatsApp & incoming call2FA login with push notification2FA methods and backup codes2FA policy2FA users setup attributeA new code has been sent.Activate the license key nowAdd trusted devices ('Remember this device' option)Add two-factor authentication to strengthen the security of your user account.All 2FA statusesAll doneAll done.All usersAllow the "Remember this device" user optionAllow the login without 2FAAllow user to specify the email address of choiceAllow users to have trusted devices so they are not asked for a 2FA code during login?Allow users to use email based 2FA as secondary backup method to Allow users to use the "one-time code via email" as a secondary backup methodAllowing users to configure 2FA from a website page (no dashboard access)Almost there…Also enforce 2FA on network users with super admin privilegesAlso exclude users with super admin privilegeAn email with a verification link has been sent to your email address. If you are using the same browser and device, please click on the link to verify the login. If you are not, please copy the link and paste it in the address bar above.Any unsaved changes will be lost!Application passwordsAre you lost?Are you sure you want to remove two-factor authentication and lower the security of your user account?Are you sure?At least one 2FA method should be enabled.Authentication CodeAuthentication Code:Authy 2FA service (SMS, App, Push notification & WhatsApp) - enter the %s to enable this method.Authy API Production key settingsAuthy Production APIAuthy Production API keyAuthy integration documentationAuthy service (Push notification, or code via application, SMS, WhatsApp or incoming call)Authy service settingsAutomatically identify unauthorized file changes on your WordPress site.Available template tags:Backup 2FA methods:Backup code page textBackup codesBackup codes are a secondary method which you can use to log in to the website in case the primary 2FA method is unavailable. Therefore they can't be enabled and used as a primary method.Backup codes generatedBlock the login.Block the user (administrators have to manually unblock them)Button textBy default the plugin checks if a users grace periods to setup 2FA has passed when the user tries to login. If you would like the plugin to advise the user within an hour, enable the below option to add a cron job that runs every hour.By enabling this feature users can also configure and use the "one-time code via email" 2FA method as a secondary backup method. This allows them to receive a one-time login code via email if they need to login to the website and cannot generate the login code from their 2FA app. This feature only applies to users who are using smartphone / app 2FA methods.Can users access the WordPress dashboard or you have custom profile pages? CancelChange 2FA SettingsChange 2FA settingsChange email addressChange phoneChange the background colorChange the button colorChange the button text on 2FA code pageChange the default text used in the 2FA code page?Change the font typeChange the logoCheatin&#8217; uh?Check your Authy mobile app and approve the OneTouch request to login. Click the button below if you are having issues getting the notification or still want to enter manually the token from the mobile app or SMS.Choose 2FA methodsChoose the 2FA authentication methodChoose you prefered email address for email backup methodClick the "I'm ready" button below when you complete the application setup process to proceed with the wizard.Click the below button to reconfigure the current 2FA method. Note that once reset you will have to re-scan the QR code on all devices you want this to work on because the previous codes will stop working.Close WizardClose Wizard & RefreshClose wizardClose wizard & configure 2FA laterConfigurable 2FA code expiration timeConfigure 2FAConfigure 2FA SettingsConfigure 2FA nowConfigure Two-factor authentication (2FA)Configure backup 2FA methodConfigure backup emailConfigure different 2FA policies for different user rolesConfigure different 2FA settings for this user roleConfigure different policies for different user roles: While requiring everyone to use 2FA is generally a good idea, stricter policies for more sensitive accounts can help you keep everyone happyConfiguredConfigured (but not required)Configuring 2FA policies & making 2FA mandatoryCongratulationsCongratulations! You are all set.Congratulations! You have enabled two-factor authentication for your user. You’ve just helped towards making this website more secure!Congratulations, you're almost there...Congratulations.Contact usContact your website administrator to unlock your account.Continue SetupContinue anywayContinue with wizardCurrent memory usage: Delete dataDelete data upon uninstallDestroy sessionDestroy user session when grace period expires?Different 2FA policies per user roleDismiss this notice.Display Name:Do not enforce on any usersDo not let them access the dashboard / user page once they log in until they configure 2FADo you need assistance with the plugin? Have you noticed or encountered an issue while using WP 2FA, or do you just want to report something to us?Do you want to delete the plugin data from the database upon uninstall?Do you want to enforce 2FA for some, or all the users? Do you want to exclude all the users of a site from 2FA? Do you want to exclude any users or roles from 2FA? Do you want to redirect the user to a specific page after completing the 2FA setup wizard?DownloadDownload and start the application of your choice (for detailed steps on setting it up click on the application icon of our choice below)E-commerce, membership & other third party plugins supportERROR: Invalid backup code.ERROR: Invalid verification code.Easily add an additional layer of security to your WordPress login pages. Enable Two-Factor Authentication for you and all your website users with this easy to use plugin.Edit PageEmailEmail & forumsEmail Address:Email Settings & TemplatesEmail TemplatesEmail addressEmail backup page textEmail bodyEmail delivery failedEmail delivery testEmail sent byEmail subjectEmail successfully sentEnable cronEnforce 2FA onEnforce strong password policies on WordPress.Enter %1$s to log in.Enter a backup email code.Enter a backup verification code.Enter new license keyEnter token manuallyError processing formError: API login for user disabled.Exclude myself from 2FA policiesExclude sitesExclude the following rolesExclude the following sitesExclude the following usersExclude usersExclude yourself?Failed to create a login nonce.FilterFont typeFor detailed guides for your desired app, click below.For how long should the plugin remember a device?For more information about the WP 2FA plugin visit the %1$s. If you have any questions or would like to get in touch with us, please use %2$s. We look forward to hearing from you.For more technical information about the WP 2FA plugin please visit the plugin's knowledge base.ForumsFreeFree 14-day Premium TrialFrom email & nameFrom within the application scan the QR code provided on the right. Otherwise, enter the following code manually in the application:Frontend 2FA settings pageFrontend 2FA settings page URLGeneral plugin settingsGenerate backup codesGenerate codesGenerate list of Backup CodesGenerate list of backup codesGet a Free 14-day trialGet the Free 14-day trialGetting startedGetting started with WP 2FAGetting started with WP 2FA and making 2FA compulsory is as easy as 1 2 3 with WP 2FA. This can be easily done through the install wizard or the plugin settings. If you are stuck, no problem! Below are a few links of guides to help you get started:Give users a grace period to configure 2FAGrace periodGrace period must be at least 1 day/hourGrace period must be at least 1 day/hour for role %s.Great job, the plugin and 2FA policies are now configured. You can always change the plugin settings and 2FA policies at a later stage from the WP 2FA entry in the WordPress menu.HOTP (Email)Hello,Hello.HelpHelp & Contact UsHere are your backup codes:Here you can specify your Authy Production API Key or change an existing one. You can get the key from the Twilio console. Refer to the KB article %s for more information and instructions on how to get your key.Hey %sHide settings from other administratorsHide the Remove 2FA buttonHide the Remove 2FA button on user profile pagesHow long should the grace period for your users be?How often should the plugin check if a user's grace period is over?I'll do it laterI'm ReadyI'm readyI'm ready, close the wizardIP AddressIf you are enforcing 2FA on all users but for some reason you do not want to enforce it on a specific sub site, specify the sub site name below:If you are enforcing 2FA on all users but for some reason you would like to exclude individual user(s) or users with a specific role, you can exclude them belowIf you cancel this wizard, the default plugin settings will be applied. You can always configure the plugin settings and two-factor authentication policies at a later stage from the If your users do not have access to the WordPress dashboard (because you use custom user profile pages) enable this option. Once enabled, the plugin creates a page which ONLY authenticated users can access to configure their user 2FA settings. A link to this page is sent in the 2FA welcome email.Important: when the license limit is exceeded the plugin updates are blocked.In order to keep this site - and your details secure, this website’s administrator requires you to enable 2FA authentication to continue.In this page you will find a number of reports which allow you to get a better overview of the current state of 2FA on your website.Install on unlimited websitesInvalid Authy Token.Invalid Email Authentication code.Invalid Two Factor Authentication code.Invalid Two Factor Authentication secret key.Invalid country codeInvalid numberInvalid provider.It is recommended to generate and print some backup codes in case you lose access to your primary 2FA method. It is recommended to have a backup 2FA method in case you cannot generate a code from your 2FA app and you need to log in. You can configure any of the below. You can always configure any or both from your user profile page later.I’ll generate them laterKeep a log of users and under the hood site activity.Knowledge baseLEARN MORELast time reports data was updated:Learn moreLearn more about backup codesLearn more.Let us help you get startedLet’s get started!License validation successful. You're fully licensed.Limit 2FA settings access?Limit access to 2FA settingsLink sent to you over email.Link via email (Out-of-band email)Link will be valid for: LockedLog InLog inLogin OOB code emailLogin code emailLogin here.Login request to %s siteLogin with 2FA via push notification, SMS, WhatsApp or incoming callLogin with a backup code: you will get 10 backup codes and you can use one of them when you need to login and you cannot generate a code from the app. %sLogo on pageMany other featuresMore 2FA methods, including push notification, SMS, WhatsApp, and incoming callMore information.NO ACTIVITY LOG ACTIVITY & DATA IS SENT BACK TO OUR SERVERS.NameNever miss an important update! Opt-in to our security and feature updates notifications, and non-sensitive diagnostic tracking with freemius.com.New code sentNextNext StepNoNo Ads!No code is presented.No formNonce checking failedNonce is not providedNonce verification failed.Not allowedNot required & not configuredNote:Note: Note: If users do not configure it within the configured stipulated time, their account will be locked and have to be unlocked manually.Note: as a security precaution, the login verification link only works when the link is clicked from the same browser and device combination from where you are trying to log in. If this is not the same browser and device, please copy the link and manually paste it in the address bar of the browser which you are using to log in to the website.Note: you should be able to access the mailbox of the email address to complete the following step.Now you need to configure 2FA for your own user account. You can do this now (recommended) or later.OKOK, close wizardOne of the required parameters is missing.One-click 2FA loginOne-time code generated with your app of choice (most reliable and secure)One-time code sent to you over emailOne-time code via 2FA App (TOTP) - One-time code via email (HOTP)Only for specific users and rolesOnly plain text is allowed.Only super adminsOnly super admins and site adminsOnly the below 2FA method is allowed on this website:Only when cookie is not foundOpen Authy mobile app and approve the login request.Open support ticketOr, send me a code to my email.Or, use a backup code.Our WordPress PluginsOut of band page textOut-of-bandPage generated byPhonePlease ensure both custom email address and display name are provided.Please enter the code to finalize the 2FA setup.Please enter the two-factor authentication (2FA) verification code below to login. Depending on your 2FA setup, you can get the code from the 2FA app or it was sent to you by email.Please enter this code to confirm the 2FA setup: %s.Please help us improve %1$s! If you opt-in, some non-sensitive data about your usage of %2$s will be sent to %3$s, a diagnostic tracking service we use. If you skip this, that's okay! %2$s will still work just fine.Please only use alphanumeric text. Your display name has not been updated.Please provide a display name.Please provide a valid email address. Your email address has not been updated.Please provide an email addressPlease select the email address where the OOB code should be sent:Please select the email address where the one-time code should be sent:Please select the email address where the out of band link should be sent:Please select the phone where link should be send:Please type in the code from your Authy application with name %1$s.Please type in the one-time code from your Google Authenticator app to finalize the setup.Please type in the one-time code sent to your email address to finalize the setup.Please type in the one-time code sent to your email address to finalize the setup. Once the code is confirmed and 2FA is set up, you only have to verify a login by clicking on a link sent to you via email.Please use a valid email addressPlugin documentationPlugin supportPremiumPremium FeaturesPremium Features ➤Primary 2FA methods:PrintProcessing UpdatePrompt user for 2FA code on trusted deviceProtect website forms & login pages from spam bots & automated attacks.Receive code over email: you will receive a one-time code via email which you need to login and you cannot generate a code from the app.Reconfigure Authy methodReconfigure link over email methodReconfigure one-time code over email methodReconfigure the 2FA AppRedirect users after 2FA setupRedirect users after 2FA setup toRefer to the %s for more information on how to setup these apps and which apps are supported.Refer to the %s for more information on how to use the Authy with WP 2FA for two-factor authentication on your website.Remember this device for %d daysRemind me on next loginRemove 2FARemove 2FA?Remove backup email methodReportsReports & StatisticsReports dataRequired but not configuredResend CodeReset 2FA configurationReset KeyRoleRoles :SMS token was sent. Please allow at least 1 minute for the text to arrive.Save backup email addressSave email backup optionsSave email settings and templatesSecondary 2FA backup methodSecondary 2FA methods and other settingsSecondary 2FA methods:SecondsSelectSelect 2FA MethodsSelect actionSelect the allowed primary 2FA methodsSelect the methodsSend me another codeSend test emailSend this emailSettingsSettings saving processes completeSetup FinishSetup the 2FA methodShould users be able to disable 2FA on their user profile?Should users be asked to setup 2FA instantly or should they have a grace period?Since you have not enabled two-factor authentication for the user %1$s on the website %2$s within the grace period, your account has been locked.Site-wide policiesSites :Skip Wizard - I know how to do thisSomeone from {user_ip_address} is trying to log in to {site_name}.Sortable users' 2FA statusSpecify a URL of a page where you want to redirect the users once they complete the 2FA setup wizard. Leave empty for default behaviour, in which users are redirected back to the page from where they launched the wizard.Specify the page where you want to redirect your users to after they complete the 2FA setup. This will override the global redirect setting.SupportSystem infoSystem informationTOTP (App)Take advantage of these benefits and many others, with prices starting from as little as $59 for 5 users per year. Below is the complete list of Premium features:Test email deliveryTest email from WP 2FAThank you for installing the WP 2FA plugin. This quick wizard will assist you with configuring the plugin and the two-factor authentication (2FA) settings for your user and the users on this website.Thank you.The %1$s 2FA service is unavailable. Please check the configuration and the service's dashboard to restore functionality. If the problem persists, %2$s.The 2FA method you were using is no longer allowed on this website. Please reconfigure 2FA using one of the supported methods withinThe 2FA method you were using is no longer allowed on this website. Please reconfigure 2FA using one of the supported methods.The 2FA service you want to use is currently unavailable. Please try again later or restart the wizard to choose another method.The Authy production API key is valid.The code will be valid for: The following setting are being saved: The license is limited to %s sub-sites. You need to upgrade your license to cover all the sub-sites on this network.The plugin created the 2FA settings page with the URL:The plugin saves its settings in the WordPress database. By default the plugin settings are kept in the database so if it is installed again, you do not have to reconfigure the plugin. Enable this setting to delete the plugin settings from the database upon uninstall.The plugin sends emails with one-time codes, blocked account notifications and more. Use the button below to confirm the plugin can successfully send emails.The primary 2FA service that you are using is unavailable. Please click the button below to login using the secondary backup method.The reports' data is updated automatically every 24 hours. The process can take up to a few minutes. Click the Update reports data button below to update the data and see the most recent reportsThe updates for this plugin have been blocked because of license problems. Click %s for more information.The user has already configured 2FA. When you reset the user's current 2FA configuration, the user can log back in with just the username and password.There is %s method available from which you can choose for 2FA:There are %s methods available from which you can choose for 2FA:There may be cases in which the 2FA service is unavailable when a user is trying to log in. For example, the service is unreachable or there are no credits to complete the action. In this case you can configure the plugin to either block the login process, or allow the user to log in without 2FA authentication.There was a problem validating your license. Please try again later or %s.These are the 2FA backup codes for the userThese settings have been disabled by your site administrator, please contact them for further assistance.These sub-sitesThis email was sent by the WP 2FA plugin to test the email delivery.This is the background color selector, from here you can change the form background color.This is the button color selector, from here you can change the form button color.This is the button text, from here you can change the form button text.This is the email sent to a user upon grace period expiry.This is the email sent to a user when a login OOB link code is required.This is the email sent to a user when a login code is required.This is the email sent to a user when the user's account has been unlocked.This is the font type color selector, from here you can change the form font type.This is the logo image selector, from here you can change the form logo.This is the text shown to the users on the page when they are asked to enter the 2FA code. To change the default text, simply type it in the below placeholder.This user is excluded from configuring 2FA.This user is required to setup 2FA but has not yet done so.This website’s administrator requires you to enable 2FA authenticationTo enable Authy enter the country and cellphone number in order to use it with this account.To ensure emails are delivered so users do not have problems logging in, we recommend using the free pluginToo longToo shortTotalTrusted devices (don't ask for 2FA code)Trusted devices: Give users the option to add trusted devices so they do not have to enter the 2FA code each time they log inTwo factor authentication ensures only you have access to your account by creating an added layer of security when logging in -Two-Factor Backup Codes for %sTwo-factor authentication settingsUPGRADE NOWUncheck to disable this message.Unlock userUnlock user and reset the grace periodUpdate reports dataUpgrade nowUpgrade to Premium to benefit more!Upgrade to Premium to start benefiting from value-added features such as:Upgrade to Premium to:Upgrade to WP 2FA Premium to add more secure authentication options and automate more, encouraging users to utilize 2FA to its fullest extent and give your users more flexibility by allowing them to work from anywhere without compromising on security.Upgrade your license keyUsage of this filter is deprecated.Use a different email address:Use another email addressUse cron job to check grace periodsUse my user email Use my user email (Use the %s html tag in the email templates to include the URL of the 2FA configuration page when notifying the users to configure two-factor authentication.Use the email address from the WordPress general settings.Use the settings below to configure the emails which are sent to users as part of the 2FA plugin. If you have any questions send us an email atUse the settings below to configure the properties of the two-factor authentication on your website and how users use it. If you have any questions send us an email atUse the settings below to customize the looks of the 2FA code page so it meets your branding requirements. If you have any questions send us an email at %1$s.Use these settings to customize the "from" name and email address for all correspondence sent from our plugin.Use this setting below to configure the properties of the two-factor authentication on your website and how users use it. If you have any questions send us an email at %1$s.Use this setting to hide this plugin configuration area from all other admins.User 2FA settings have been removed.User account locked emailUser account successfully unlocked. User can login again.User account unlocked emailUser creation failedUser has not logged in yet, 2FA status is unknownUser is not providedUsernameUsers :Users can configure and also disable 2FA on their profile by clicking the "Remove 2FA" button. Enable this setting to disable the Remove 2FA button so users cannot disable 2FA from their user profile.Users have to configure 2FA straight away.Validate & Save ConfigurationValidate nowValidating your license, please wait...Verification Code:Verify configurationVerify the Production API KeyVerify the login by clicking %1$s. If this is not you, please ignore this email and contact your website administrator.View PageWP 2FAWP 2FA &rsaquo; Setup WizardWP 2FA - Two-factor authentication for WordPressWP 2FA PluginWP 2FA SettingsWP 2FA User PageWP 2FA is your trusted gatekeeper, keeping your website, users, customers, team members, and you secure and better protected than ever before.WP 2FA pluginWP 2FA plugin.WP Mail SMTPWP White SecurityWaiting for approval from application...WelcomeWelcome to WP 2FAWhat should the plugin do if the 2FA method used during a user login is unavailable?What should the plugin do with users who do not configure 2FA within the grace period?When cookie is not found or when the cookie is found but the IP address is differentWhen should users be prompted for 2FA code on trusted devices?When this feature is enabled, users can tick the option "Remember this device" in the login page so the plugin does not ask them for a 2FA code for a number of days.When users add  a trusted device, a cookie is stored in the users' browsers. If the cookie is not detected, the plugin will prompt the users to enter a 2FA code to log in to the website. For additional security, you can also configure the plugin to prompt the user for a 2FA code when there is a cookie but the IP address of the device is different from the one that was used when the device was first remembered.When you configure the 2FA policies and require users to configure 2FA, they can either have a grace period to configure 2FA, or can be required to configure 2FA before the next time they login. Choose which method you'd like to use:When you enforce 2FA on users they have a grace period to configure 2FA. If they fail to configure it within the configured stipulated time, their account will be locked and have to be unlocked manually. Note that user accounts cannot be unlocked automatically, even if you change the settings. As a security precaution they always have to be unlocked them manually. Maximum grace period is 10 days.When you enforce 2FA the users will be prompted to configure 2FA the next time they login. Users have a grace period for configuring 2FA. You can configure the grace period and also exclude user(s) or role(s) in this settings page. When you uncheck any of the below 2FA methods it won't be available for your users to use. You can always change this later on from the plugin's settings.Which email address should the plugin use as a from address?Which of the below 2FA methods can users use?Which two-factor authentication methods can your users use?White labelingWhite labeling (logo, text, colors & fonts)White labeling of 2FA code pageWhite labelling features: Gain increased trust by extending your business' branding and tone of voice to all 2FA pagesWrong or no tokenYesYou are about to enforce 2FA instantly on all users, including yourself, however you have not yet configured your own 2FA method. What would you like to do?You are enforcing 2FA on %1$d users, which is more than the license key allows. You have a %2$d users license. You can upgrade the license key, enter a new license key, or click Exclude users to go back to the configuration and reduce the number of users on which you want to enforce 2FA on.You are required to configure 2FA.You can configure 2FA from this page:You can configure other 2FA method settings and the 2FA backup methods from the plugin settings later on.You can edit this page using the page editor, like you do with all other pages.You can exit this wizard now or continue to create backup codes.You have logged in successfully.You must be logged in to view this page.You must provide a new page slug for role %s.You must provide a new page slug.You must specify at least one role or userYou need to activate the license key to use WP 2FA - Two-factor authentication for WordPress . %2$sYour 2FA settings have been removed.Your 2FA setup codeYour account just got more secureYour backup codesYour login confirmation code for {site_name}Your login confirmation link for {site_name}Your login just got more secureYour login just got more secure.Your userYour user account has been locked because you have not configured 2FA within the grace period. Please contact the website administrator to unlock your user and you can configure 2FA.Your user on {site_name} has been lockedYour user on {site_name} has been unlockedbefore %scodecomplete list of supported 2FA apps.contact our support teamdayshas been unlocked. Please configure two-factor authentication within the grace period, otherwise your account will be locked again.hourshow to get the Authy Production API keyhttps://wp2fa.io/https://www.wpwhitesecurity.com/minutesneverno grace periodon the websiteour contact formplugin's websitesupport@wpwhitesecurity.comthis linkunused backup codes remaining.Project-Id-Version: WP 2FA - Two-factor authentication for WordPress 1.5.2
Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/wp-2fa
POT-Creation-Date: 2022-05-16 14:54+0400
PO-Revision-Date: 2022-05-17 12:38+0400
Last-Translator: 
Language-Team: 
Language: de_DE
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
X-Generator: Poedit 1.8.6
X-Domain: wp-2fa
Plural-Forms: nplurals=2; plural=(n != 1);
 Eintrag in Ihrem WordPress Dashboard Menu.%s Sie müssen Ihre Lizenz erneuern, um die Premium-Funktionen weiterhin nutzen zu können.%s unbenutzter Code übrig.%s unbenutzte Codes übrig.&larr; zurück zu %s)2FA Vorgaben2FA Einstellungen aktualisiert2FA Einrichtung:2FA StatusArtikel zu 2FA-Apps in unserer Wissensdatenbank2FA HintergrundfarbeHintergrundfarbe für den 2FA Button2FA-Code per E-MailText auf der 2FA Code Seite2FA-Code über mobile App2FA-Anmeldung mit SMS, WhatsApp & eingehendem Anruf2FA-Anmeldung mit Push-Nachricht2FA-Methoden und Backup-Codes2FA Vorgaben2FA-Benutzer-Setup-AttributEin neuer Code wurde Ihnen zugeschickt.Lizenzschlüssel jetzt aktivierenVertrauenswürdige Geräte hinzufügen (Option „Dieses Gerät merken“)Zwei-Faktor-Authentifizierung aktivieren, um die Sicherheit Ihres Nutzerkontos zu verbessern.Alle 2FA StatiFertigErledigt.Alle BenutzerNutzern erlauben "Dieses Gerät merken"Login ohne 2FA erlaubenNutzern erlauben die E-Mail-Adresse ihrer Wahl anzugebenBenutzern erlauben vertrauenswürdige Geräte zu verwenden, damit sie bei der Anmeldung nicht nach einem 2FA-Code gefragt werden?Erlauben Sie Benutzern, E-Mail-basierte 2FA als sekundäre Sicherungsmethode zu verwenden, um zu gewährleisten, dass „einmalig Code per E-Mail senden“ als sekundäre Sicherungsmethode verwendet werden kann.Benutzern erlauben, 2FA von einer spezifischen Frontend-Seite aus zu konfigurieren (kein Wordpress Backend-Zugriff)Fast fertig…2FA auch für Netzwerknutzer mit Super-Admin-Rechten erzwingenNutzer mit Super-Admin-Rechten ebenfalls ausschließenEine E-Mail mit einem Bestätigungslink wurde an Ihre E-Mail-Adresse gesendet. Wenn Sie denselben Browser und dasselbe Gerät verwenden, klicken Sie bitte auf den Link, um die Anmeldung zu bestätigen. Wenn nicht, kopieren Sie bitte den Link und fügen Sie ihn oben in die Adressleiste ein.Alle nicht gespeicherten Änderungen gehen verloren!AnwendungspasswörterWissen Sie nicht weiter?Der Zugangsschutz zu Ihren Daten wird dadurch deutlich reduziert. Sind Sie sicher, daß Sie die Zweifaktorauthentifizierung entfernen möchten?Sind Sie sicher?Mindestens eine 2FA Methode sollte aktiviert werden.AnmeldecodeAuthentifizierungscode:Authy 2FA-Service (SMS, App, Push-Benachrichtigung und WhatsApp) - geben Sie %s ein, um diese Methode zu aktivieren.Authy Produktions-API-Schlüssel EinstellungenAuthy Produktion APIAuthy Produktions-API-SchlüsselDokumentation zur Authy IntegrationAuthy-Service (Push-Benachrichtigung oder Code über Anwendung, SMS, WhatsApp oder eingehenden Anruf)Authy Service EinstellungenIdentifizieren Sie automatisch nicht autorisierte Dateiänderungen auf Ihrer WordPress-Seite.Verfügbare Tags für Vorlagen:2FA Backup Methoden:Text auf der Backup Code SeiteBackup CodesBackup-Codes sind eine sekundäre Methode, mit der Sie sich auf der Website anmelden können, falls die primäre 2FA-Methode nicht verfügbar ist. Daher kann sie hier nicht aktiviert und als primäre Methode verwendet werden.Notfall-Codes bereitLogin blockieren.Blockieren Sie den Benutzer (Administratoren müssen ihn manuell entsperren)Button TextNormalerweise prüft das Plugin bei einem Loginversuch, ob die Übergangszeit eines Benutzers abgelaufen ist. Wenn das Plugin den Benutzer innerhalb einer Stunde aktiv benachrichtigen soll, aktivieren Sie die Option unten, damit der Cronjob stündlich läuft.Durch Aktivieren dieser Funktion können Benutzer auch die 2FA-Methode „Einmaliger Code per E-Mail“ als sekundäre Backup-Methode konfigurieren und verwenden. Auf diese Weise können sie einen einmaligen Anmeldecode per E-Mail erhalten, wenn sie sich auf der Website anmelden müssen und den Anmeldecode nicht aus ihrer 2FA-App generieren können. Diese Funktion gilt nur für Benutzer, die Smartphone-/App-2FA-Methoden verwenden.Können Ihre Benutzer das Dashboard nutzen oder verwenden Sie eigene Profilseiten Abbrechen2FA Einstellungen ändern2FA Einstellungen ändernE-Mail-Adresse ändernTelefon wechselnHintergrundfarbe ändernButton-Farbe ändernText des Buttons auf der 2FA Code Seite ändernDen Standardtext auf der 2FA Code-Seite anpassen?Schriftart ändernLogo ändernMogel&#8217;  ey?Wechseln Sie in Ihre mobile Authy-App und genehmigen Sie die OneTouch-Anmeldeanforderung. Klicken Sie auf die Schaltfläche unten, wenn Sie Probleme haben, die Benachrichtigung zu erhalten, oder den Token manuell über die mobile App oder SMS eingeben möchten.2FA Methoden wählenVerfahren für Zweifaktorauthentifizierung wählenWählen Sie Ihre bevorzugte E-Mail-Adresse für die E-Mail-Backup-MethodeUm fortzufahren, klicken Sie unten auf „Fertig“, sobald Sie den Einrichtungsprozess in Ihrer Anwendung abgeschlossen haben.Die Schaltfläche unten anklicken, um die aktuelle 2FA Methode neu einzustellen. Einmal zurückgesetzt, muss der QR-Code auf allen Geräten neu eingelesen werden, mit denen Sie sich einloggen wollen. Alle bisherigen Codes werden nicht mehr funktionieren.Assistenten schließenAssistenten schließen und Seite neu ladenAssistenten beendenEinrichtung beenden und 2FA später konfigurierenKonfigurierbare Ablaufzeit des 2FA-Codes2FA Einstellungen einrichten2FA Einstellungen einrichten2FA Einstellungen jetzt einrichtenZwei-Faktor-Authentifizierung einrichten (2FA)Einstellungen zur 2FA Backup MethodeBackup-E-Mail-Adresse einrichtenKonfigurieren Sie verschiedene 2FA-Richtlinien für unterschiedliche BenutzerrollenKonfigurieren Sie unterschiedliche 2FA-Einstellungen für diese BenutzerrolleKonfigurieren Sie unterschiedliche Richtlinien für unterschiedliche Benutzerrollen: Während es im Allgemeinen eine gute Idee ist, von allen die Verwendung von 2FA zu verlangen, können Ihnen strengere Richtlinien für sensiblere Konten helfen, alle zufrieden zu stellenKonfiguriertKonfiguriert (jedoch nicht benötigt)2FA-Richtlinien konfigurieren und 2FA verpflichtend einführenGlückwunschGlückwunsch, alles fertig!Glückwunsch! Sie haben für Ihren Benutzer gerade die Zweifaktorauthentifizierung aktiviert. Dadurch wird diese Website sicherer!Glückwunsch, Sie sind fast fertig…Glückwunsch.KontaktBenachrichtigen Sie den Administrator zwecks Kontenentsperrung.Einrichtung fortsetzenTrotzdem fortfahrenDie Einrichtung fortsetzenDerzeitiger Speicherverbrauch: Daten löschenDaten beim Deinstallieren löschenSession schließenBenutzersitzung beenden, wenn die Übergangszeit endet?Unterschiedliche 2FA-Vorgaben je nach BenutzerrolleDiesen Hinweis ignorieren.Anzeigename:Bei keinem Benutzer erzwingenLassen Sie sie nach dem Login nicht auf das Dashboard/die Benutzerseite zugreifen, solange sie nicht 2FA konfiguriert habenBenötigen Sie Hilfe mit dem Plugin? Haben Sie bei der Verwendung von WP 2FA ein Problem festgestellt, oder möchten Sie uns einfach etwas mitteilen?Möchten Sie bei Deinstallation des Plugins auch alle verbundenen Daten löschen?Möchten Sie die 2FA für einige oder für alle Benutzer einrichten Möchten Sie alle Benutzer der Seite vom 2FA ausschließen Möchten Sie Benutzer oder Rollen von 2FA ausschließen Möchten Sie Ihre Nutzer nach Abschluss des 2FA-Einrichtungsassistenten auf eine bestimmte Seite umleiten?HerunterladenLaden Sie die Anwendung Ihrer Wahl herunter und starten Sie diese (für eine detaillierte Anleitung klicken Sie unten auf das Icon Ihrer favorisierten Anwendung)Unterstützung für E-Commerce, Mitgliedschaft und andere Plugins von DrittanbieternFEHLER: ungültiger Notfall-Code.FEHLER: ungültiger Anmeldecode.Fügen Sie Ihren Wordpress-Login-Seiten auf einfache Weise eine zusätzliche Sicherheitsebene hinzu. Aktivieren Sie für sich und Ihre Benutzer die Zwei-Faktor-Authentifizierung mit diesem leicht bedienbaren Plugin.Seite bearbeitenE-MailE-Mail und ForenEmailadresse:Email Einstellungen & VorlagenE-Mail VorlagenEmailadresseE-Mail-Backup SeitentextNachrichtentextDie E-Mail-Zustellung ist gescheitert.EmailtestE-Mail versendet vonBetreffDie E-Mail wurde erfolgreich versendet.Timer aktivieren2FA erzwingen beiFühren Sie starke Passwortrichtlinien auf WordPress ein.Geben Sie %1$s ein, um sich anzumelden.Geben Sie einen Backup E-Mail Code einEinen Notfall-Code eingeben.Neuen Lizenzschlüssel eingebenToken manuell eingebenFehler beim Verarbeiten des FormularsFehler: API Anmeldung für Benutzer abgeschaltet.Mich selbst von den 2FA Einstellungen ausschließenSeiten ausschließenFolgende Rollen ausschließenFolgende Sites ausschließenFolgende Benutzer ausschließenNutzer ausschließenMöchten Sich sich selbst ausschließen?Fehler bei der Erzeugung des Anmeldecodes.FilterSchriftartFür genaue Anleitungen zu Ihrer gewünschten App, bitte unten klicken.Wie lange soll sich das Plugin ein Gerät merken?Weitere Informationen zum WP 2FA Plugin finden Sie unter %1$s. Bei Fragen oder wenn mit uns in Kontakt treten möchten, verwenden Sie bitte %2$s. Wir freuen uns von Ihnen zu hören.Weitere technische Informationen zum WP 2FA-Plugin finden Sie in der Wissensdatenbank des Plugins.ForenKostenlosKostenlose 14-tägige Premium-TestphaseEmail & Name des AbsendersIn der Anwendung: Scannen Sie den QR-Code, der auf der rechten Seite angezeigt wird. Alternativ geben Sie den Code manuell in Ihre Anwendung ein.Frontend 2FA-EinstellungsseiteFrontend 2FA-Einstellungsseiten-URLAllgemeine Plugin EinstellungenNotfall-Codes generierenNotfall-Codes generierenListe mit Backup Codes generierenEine Liste von Backup Codes generierenHolen Sie sich eine kostenlose 14-Tage-TestversionHolen Sie sich die kostenlose 14-Tage-TestversionLoslegenWP 2FA jetzt einrichtenMit WP 2FA zu beginnen und die Zwei-Faktor-Authentifizierung verbindlich zu machen, ist so leicht wie nie zuvor. Nutzen Sie einfach den Installationsassistenten oder die Plugin-Einstellungen. Falls Sie einmal nicht weiterkommen, kein Problem! Nachfolgend finden Sie einige Links zu Anleitungen, die Ihnen den Einstieg erleichtern:Bewilligen Sie Benutzern eine Übergangszeit zur 2FA-EinrichtungÜbergangsperiodeÜbergangsfrist muss mindestens 1 Tag / 1 Stunde lang seinDie Übergangsfrist muss für die Rolle %s mindestens 1 Tag/Stunde betragen.Gut gemacht, das Plugin und die 2FA Vorgaben sind nun konfiguriert. Sie können die Einstellungen und Vorgaben jederzeit im Wordpress Backend Menu (WP 2FA) wieder ändern.HOTP (E-Mail)Hallo,Hallo.HilfeHilfe und KontaktHier sind Ihre Notfall-Codes:Hier können Sie Ihren Authy Productions-API-Schlüssel angeben oder einen bestehenden ändern. Sie können den Schlüssel aus der Twilio-Konsole entnehmen. Weitere Informationen und Anweisungen dazu, wie Sie Ihren Schlüssel erhalten, finden Sie im KB-Artikel %s.Hi %sVerbergen Sie Einstellungen vor anderen Administratoren"2FA entfernen" Button verstecken"2FA entfernen" Button auf Profilseiten versteckenWie lange soll die Übergangsfrist dauern?Wie oft soll das Plugin prüfen, ob die Übergangszeit eines Benutzers vorüber ist?Ich werde es später tunFertigFertigFertig, Einrichtung beendenIP-AdresseWenn Sie 2FA für alle Benutzer erzwingen aber aus irgendwelchen Gründen dies nicht auf einer bestimmten Unterseite tun möchten, stellen Sie diese Unterseite unten ein:Wenn Sie 2FA für alle Benutzer erzwingen, aber aus irgendwelchen Gründen einzelne Benutzer oder Benutzer einer bestimmten Rolle davon ausnehmen möchten, tun Sie dies untenWenn Sie die Einrichtung abbrechen, werden die Standardeinstellungen des Plugins angewendet. Sie können die Einstellungen und die Zwei-Faktor-Authentifizierung später jederzeit konfigurieren vom Wenn Ihre Benutzer keinen Zugang zum WordPress Dashboard haben (weil Sie eigene Profiulseiten verwenden), aktivieren Sie diese Option. Einmal aktiviert, erstellt das Plugin eine Seite, die NUR von authentifizierten Benutzern zur EInrichtung der 2FA-Einstellungen erreicht werden kann. Ein Link zu dieser Seite wird in der Begrüßungs-Email mitgeteilt.Wichtig: Wenn das Lizenzlimit überschritten wird, werden die Plugin-Updates blockiert.Um diese Website und deren Inhalte zu schützen, fordert Sie der Administrator dazu auf die Zwei-Faktor-Authentifizierung zu aktivieren, um fortzufahren.Auf dieser Seite finden Sie eine Reihe von Berichten, mit denen Sie sich einen besseren Überblick über den aktuellen Stand von 2FA auf Ihrer Website verschaffen können.Auf unendlich vielen Websites installierenUngültiger Authy Token.Ungültiges Einmalpasswort.Ungültiger 2FA Code.Ungültiger 2FA Geheimcode.Ungültiger Länder-CodeUngültige NummerAnbieter wird nicht unterstütztEs wird empfohlen, einige Notfall-Codes zu generieren und auszudrucken, falls Sie einmal keinen Zugang zu Ihrer primären 2FA Methode haben. Für den Fall, dass Sie in Ihrer 2FA App einmal keinen Code generieren können, empfehlen wir die Einrichtung mindestens einer weiteren (Backup-)Anmeldemethode. Diese können Sie unten oder jederzeit auf Ihrer persönlichen Nutzerkonto-Seite einrichten.Ich werde sie später generierenFühren Sie ein Protokoll der Benutzer und der dahinter liegenden Website-Aktivitäten.WissensdatenbankERFAHREN SIE MEHRZuletzt wurden Berichtsdaten aktualisiert:Mehr erfahrenErfahren Sie mehr über Backup CodesMehr erfahren.Lassen Sie uns bei der Einrichtung helfenLassen Sie uns beginnen!Lizenzvalidierung erfolgreich. Sie sind vollständig lizenziert.2FA Zugang begrenzen?Zugriff auf 2FA Einstellungen einschränkenLink wurde per E-Mail an Sie gesendet.Link per E-Mail (Out-of-Band-E-Mail)Link gilt für: GesperrtAnmeldenAnmeldenLogin-OOB-Code-E-MailE-Mail mit Einmal-PasswortHier anmelden.Anmeldeversuch auf folgender Website: %sLogin mit 2FA per Push-Benachrichtigung, SMS, WhatsApp oder eingehendem AnrufMit einem Backup Code anmelden: Sie erhalten 10 Backup Codes, mit denen Sie sich anmelden können, falls Sie einmal keine Codes in der App generieren. %sLogo auf der SeiteViele weitere FunktionenWeitere 2FA-Methoden, einschließlich Push-Benachrichtigung, SMS, WhatsApp und eingehender AnrufWeitere Informationen.KEIN AKTIVITÄTSPROTOKOLL UND KEINE AKTIVITÄTEN ODER DATEN WERDEN AN UNSERE SERVER ZURÜCKGESENDET.NameVerpassen Sie nie wieder ein wichtiges Update! Abonnieren Sie unsere Benachrichtigungen zu Sicherheits- und Feature-Updates sowie die anonyme Diagnose-Datenübermittlung mit freemius.com.Neuer Code verschicktWeiterWeiterNeinWerbefrei!Es wurde kein Code eingegeben.Kein FormularCode-Bestätigung gescheitertCode-Bestätigung wird nicht unterstütztCode-Bestätigung gescheitert.Nicht erlaubtNicht benötigt und nicht konfiguriertNotiz:Hinweis:Achtung: Wenn Benutzer es nicht innerhalb der vorgegebenen Zeit einstellen, wird deren Konto gesperrt und muss manuell entsperrt werden.Hinweis: Als Sicherheitsvorkehrung funktioniert der Anmelde-Verifizierungslink nur, wenn der Link von derselben Browser- und Gerätekombination aus angeklickt wird, von der aus Sie versuchen, sich anzumelden. Wenn dies nicht derselbe Browser und dasselbe Gerät ist, kopieren Sie bitte den Link und fügen ihn manuell in die Adressleiste des Browsers ein, mit dem Sie sich auf der Website anmelden.Achtung: Sie sollten Zugang zu der Mailbox dieser Emailadresse haben um den nächsten Schritt abschließen zu können.Richten Sie jetzt (empfohlen) oder später 2FA auch für Ihr eigenes Nutzerkonto ein.OKOK, Assistenten beendenEiner der erforderlichen Parameter fehlt.Ein-Klick 2FA-AnmeldungEinmal-Passwort der von Ihnen gewählten App (zuverlässig und sicher)Einmal-Passwort, das Ihnen per Email zugeschickt wirdEinmaliger Code über 2FA App (TOTP) - Einmaliger Code per E-Mail (HOTP)Nur für bestimmte Benutzer und RollenNur einfacher Text ist erlaubt.Nur SuperadminsNur Super-Administratoren und Seiten-AdministratorenNur die unten aufgeführte Verfahrensweise ist auf dieser Website gestattet:Nur wenn kein Cookie gefunden wirdÖffnen Sie die Authentifizierung App auf dem Mobiltelefon und bestätigen Sie die Login-Anfrage.Support Ticket eröffnenOder senden Sie mir einen Code an meine E-Mail.Oder verwenden Sie den Notfallcode.Unsere Wordpress PluginsOut-of-Band-SeitentextOut-of-BandSeite erstellt vonTelefonBitte stellen Sie sicher, dass sowohl die benutzerdefinierte E-Mail-Adresse als auch der Anzeigename angegeben sind.Bitte den Code eingeben, um die Einrichtung abzuschließen.Bitte geben Sie den Zwei-Faktor-Authentifizierungscode unten ein, um sich anzumelden. Je nachdem, welche Einstellung Sie gewählt haben, erhalten Sie den Code in der 2FA App oder per E-Mail zugesendet.Bitte geben Sie diesen Code ein, um die 2FA-Einrichtung zu bestätigen: %s.Bitte helfen Sie uns, %1$s zu verbessern! Wenn Sie sich anmelden, werden einige nicht zurückverfolgbare Daten über Ihre Nutzung von %2$s an %3$s gesendet, einen von uns verwendeten Diagnose-Service. Wenn Sie dies überspringen, ist das in Ordnung! %2$s wird immer noch ohne Einschränkungen funktionieren.Bitte nur alphanumerische Zeichen. Ihr Anzeigename wurde nicht angepasst.Anzeigename notwendig.Bitte eine gültige E-Mail-Adresse angeben. Ihre Mailadresse wurde nicht angepasst.E-Mail-Adresse notwendigBitte wählen Sie die E-Mail-Adresse aus, an die der OOB-Code gesendet werden soll:Bitte wählen Sie die Mailadresse an welche das Einmalpasswort verschickt werden soll:Bitte wählen Sie die E-Mail-Adresse aus, an die der Out-of-Band-Link gesendet werden soll:Bitte wählen Sie das Telefon aus, an das der Link gesendet werden soll:Bitte geben Sie den Code aus Ihrer Authy-Anwendung mit dem Namen %1$s ein.Bitte tippen Sie das Einmalpasswort Ihrer Google Authenticator App ein um die Einrichtung abzuschließen.Bitte tippen Sie das Einmalpasswort ein, das wir an Ihre Mailadresse geschickt haben, um das Setup abzuschließen.Bitte geben Sie den an Ihre E-Mail-Adresse gesendeten Einmalcode ein, um die Einrichtung abzuschließen. Sobald der Code bestätigt und 2FA eingerichtet ist, müssen Sie sich nur noch anmelden, indem Sie auf den Link klicken, der Ihnen per E-Mail zugesandt wird.Bitte geben Sie eine gültige E-Mail-Adresse anPlugin DokumentationPlugin SupportPremiumPremium FunktionenPremium Funktionen ➤Primäre 2FA-Methoden:DruckenAktualisierung in ArbeitFordern Sie den Benutzer auf, den 2FA-Code auf einem vertrauenswürdigen Gerät einzugebenSchützen Sie Website-Formulare und Anmeldeseiten vor Spam-Bots und vor automatisierten Angriffen.Code per E-Mail erhalten: Sie erhalten einen einmaligen Code per E-Mail, den Sie zum Anmelden benötigen, und Sie können keinen Code aus der App generieren.Konfigurieren Sie die Authy-Methode neuLink über E-Mail-Methode neu konfigurierenEinmal-Code via E-Mail erneuern2FA App neu einrichtenNutzer nach der 2FA Einrichtung umleitenLeiten Sie Benutzer nach der 2FA-Einrichtung umWeitere Informationen zum Einrichten dieser Apps und zu den unterstützten Apps finden Sie unter %s.Weitere Informationen zur Verwendung von Authy mit WP 2FA für die Zwei-Faktor-Authentifizierung auf Ihrer Website finden Sie unter %s.Erinnere dieses Gerät für %d TageBei der nächsten Anmeldung erinnern2FA entfernen2FA entfernen?Backup-E-Mail-Methode entfernenBerichteBerichte & StatistikenDetaillierte Daten aus den BerichtenBenötigt, jedoch nicht konfiguriertCode erneut senden2FA Konfiguration zurücksetzenSchlüssel zurücksetzenRolleRollen :SMS Token wurde gesendet. Die Zustellung wird mindestens eine Minute dauern.Backup-E-Mail-Adresse speichernE-Mail-Backup-Optionen speichernE-Mail-Einstellungen und Vorlagen speichernSekundäre 2FA Backup MethodeZusätzliche 2FA Methoden und weitere EinstellungenSekundäre 2FA-Methoden:SekundenAuswählen2FA Methoden wählenAktion wählenWählen Sie die zulässigen primären 2FA-Methoden ausMethoden auswählenSchicken Sie mir einen neuen CodeTestnachricht versendenDiese E-Mail versendenEinstellungenSpeichern der Einstellungen abgeschlossenEinrichtung beenden2FA Verfahrensweise auswählenSollen Benutzer die Möglichkeit haben, 2FA in ihrem Konto selbst abzustellen?Sollen Benutzer sofort gebeten werden, 2FA einzurichten, oder soll es eine Übergangszeit geben?Da Sie die Zwei-Faktor-Authentifizierung für den Nutzer %1$1 auf der Website %2$s nicht in der Übergangsfrist aktiviert haben, wurde Ihr Nutzerkonto gesperrt.Seiten-übergreifende RichtlinienSeiten :Assistenten überspringenJemand von {user_ip_address} versucht, sich bei {site_name} anzumelden.Sortierbarer 2FA-Status der BenutzerGeben Sie die URL einer Seite an, auf die Sie die Benutzer umleiten möchten, sobald sie den 2FA-Einrichtungsassistenten abgeschlossen haben. Lassen Sie dieses Feld leer, wenn Nutzer zu der Seite zurückgeleitet werden sollen, von der aus sie den Assistenten gestartet haben.Geben Sie die Seite an, auf die Sie Ihre Benutzer umleiten möchten, nachdem sie die 2FA-Einrichtung abgeschlossen haben. Dadurch wird die globale Umleitungseinstellung überschrieben.SupportSystem InfoSysteminformationenTOTP (App)Profitieren Sie von diesen Vorteilen und vielen anderen, mit Preisen ab nur 59 $ für 5 Benutzer pro Jahr. Nachfolgend finden Sie die vollständige Liste der Premium-Funktionen:Teste EmailversandTestmail von WP 2FAVielen Dank für die Installation des WP 2FA Plugin. Diese kurze Einführung hilft Ihnen bei der Einrichtung des Plugins sowie bei der Zwei-Faktor-Authentifizierung für Sie und die weiteren Nutzer dieser Website.Danke.Der %1$s 2FA-Dienst ist nicht verfügbar. Bitte überprüfen Sie die Konfiguration und das Dashboard des Dienstes, um die Funktionalität wiederherzustellen. Wenn das Problem weiterhin besteht, %2$s.Ihre 2FA-Methode ist auf dieser Website nicht mehr länger zugelassen. Bitte erneuern Sie Ihre Zweifaktorauthentifizierung mit einer unterstützten Methode innerhalb vonIhre 2FA Methode ist auf dieser Website nicht mehr länger zugelassen. Bitte stellen Sie 2FA mit einer unterstützten Methode neu ein.Der 2FA-Service, den Sie verwenden möchten, ist derzeit nicht verfügbar. Bitte versuchen Sie es später erneut oder starten Sie den Assistenten neu, um eine andere Methode auszuwählen.Der Authy Produktions-API-Schlüssel is gültig.Der Code ist gültig für:Die folgende Einstellung wurde gespeichert: Die Lizenz ist auf %s Unterseiten beschränkt. Sie müssen Ihre Lizenz aktualisieren, um alle Unterseiten in diesem Netzwerk abzudecken.Das Plugin hat die 2FA Einstellungsseite mit folgender URL eingerichtet:Das Plugin speichert seine Einstellungen in der Datenbank der Website. Sta ndardmäßig werden die Plugindaten in der Datenbank abgelegt, so daß bei Reinstallation Einstellungen erhalten bleibem. Aktivieren Sie diese Einstellung, um bei Deinstallation alle Daten aus der Datenbank zu löschen.Das Plugin kann automatisierte E-Mails mit Einmal-Codes, Benachrichtigungen zu gesperrten Nutzerkonten und mehr versenden. Bitte bestätigen Sie unten, dass das Plugin diese Nachrichten versenden soll.Der primäre 2FA-Service, den Sie verwenden, ist nicht verfügbar. Bitte klicken Sie auf die Schaltfläche unten, um sich mit der sekundären Sicherungsmethode anzumelden.Die Daten der Berichte werden alle 24 Stunden automatisch aktualisiert. Der Vorgang kann einige Minuten dauern. Klicken Sie unten auf die Schaltfläche „Berichtsdaten aktualisieren“, um die Daten zu aktualisieren und die neuesten Berichte anzuzeigenDie Updates für dieses Plugin wurden aufgrund von Lizenzproblemen blockiert. Klicken Sie auf %s, um weitere Informationen zu erhalten.Der Nutzer hat 2FA bereits konfiguriert. Wenn Sie die 2FA Einstellungen für den Nutzer zurücksetzen, kann der Nutzer sich ohne 2FA anmelden.Für 2FA ist %s Methode verfügbar:Für 2FA sind %s Methoden verfügbar, aus denen Sie wählen können:Es kann Fälle geben, in denen der 2FA-Dienst nicht verfügbar ist, wenn ein Benutzer versucht, sich anzumelden. Beispielsweise ist der Dienst nicht erreichbar oder es sind keine Codes vorhanden, um die Aktion abzuschließen. In diesem Fall können Sie das Plugin so konfigurieren, dass es entweder den Anmeldevorgang blockiert oder dem Benutzer erlaubt, sich ohne 2FA-Authentifizierung anzumelden.Beim Validieren Ihrer Lizenz ist ein Problem aufgetreten. Bitte versuchen Sie es später noch einmal oder %s.Dies sind die 2FA Notfall-CodesDiese Einstellungen wurden von Ihrem Seitenadministrator außer Kraft gesetzt. Bitte wenden Sie sich für weitere Hilfe an ihn.Diese UnterseitenDiese Mail wurde von WP 2FA geschickt um die Mailzustellbarkeit zu testen.Dies ist die Auswahl für die Hintergrundfarbe des Formulars.Dies ist die Button-Farbauswahl, um die Farbe der Formular-Buttons zu ändern.Ändern Sie hier den Text der Formular-Buttons.Diese Email wird an Benutzer verschickt, deren Übergangsperiode abgelaufen ist.Dies ist die E-Mail, die an einen Benutzer gesendet wird, wenn ein Login-OOB-Linkcode erforderlich ist.Diese E-Mail wird an Benutzer verschickt, wenn ein Einmal-Passwort benötigt wird.Diese Email wird an Benutzer verschickt, deren Konto entsperrt wurde.Dies ist die Farbauswahl für die Schriftart, um die Schriftart des Formulars zu ändern.Dies ist die Logobild-Auswahl, um das Formularlogo zu ändern.Dies ist der Text, der den Nutzern auf der Seite angezeigt wird, wenn sie aufgefordert werden, den 2FA Code einzugeben. Um den Standardtext zu ändern, geben Sie ihn einfach in den folgenden Platzhalter ein.Dieser Benutzer ist von der Einrichtung der Zweifaktorauthentifizierung (2FA) ausgeschlossen.Diese Benutzer müsste 2FA einrichten, hat es bisher aber nicht getan.Der Administrator dieser Website bittet Sie um 2FA AktivierungUm Authy zu aktivieren, geben Sie das Land und die Handynummer ein, um es mit diesem Konto zu verwenden.Um sicherzustellen, dass Nutzer eine E-Mail erhalten, um sich problemlos anmelden können, empfehlen wir das kostenfreie PluginZu langZu kurzInsgesamtVertrauenswürdige Geräte (nicht nach 2FA-Code fragen)Vertrauenswürdige Geräte: Geben Sie Benutzern die Möglichkeit, vertrauenswürdige Geräte hinzuzufügen, damit sie nicht bei jeder Anmeldung den 2FA-Code eingeben müssenDie Zwei-Faktor-Authentifizierung stellt sicher, dass nur Sie selbst auf Ihr Benutzerkonto zugreifen können, indem ein zusätzlicher Schutz beim Login ergänzt wird.2FA Notfallcodes für %sEinstellungen zur Zwei-Faktor-AuthentifizierungJETZT UPGRADENAbwählen, um diese Nachricht zu blockieren.Benutzer entsperrenBenutzer entsperren und Übergangsperiode zurücksetzenBerichtsdaten aktualisierenJetzt upgradenUpgraden Sie auf Premium, um mehr zu profitieren!Führen Sie ein Upgrade auf Premium durch, um von folgenden wertvollen Funktionen zu profitieren:Upgrade auf Premium auf:Führen Sie ein Upgrade auf WP 2FA Premium durch, um sicherere Authentifizierungsoptionen hinzuzufügen und die Anmeldung zu automatisieren. Ermutigen Sie Ihre Benutzer, 2FA in vollem Umfang zu nutzen, und geben Sie ihnen mehr Flexibilität, indem Sie ihnen ermöglichen, von überall aus zu arbeiten, ohne Kompromisse bei der Sicherheit einzugehen.Upgrade des LizenzschlüsselsDie Verwendung dieses Filters ist veraltet.Eine andere Mailadresse verwenden:Andere Mailadresse verwendenTimer zur Prüfung der Übergangsfristen verwendenBenutze meine Nutzer-Mail-AdresseBenutze meine Nutzer-Mail-Adresse (Verwenden Sie das HTML-Tag %s in den E-Mail-Vorlagen, um die URL der 2FA Konfigurationsseite anzugeben, wenn Sie die Nutzer benachrichtigen, die Zwei-Faktor-Authentifizierung zu konfigurieren.Verwenden Sie die in den WordPress-Einstellungen angegebene Mailadresse.Verwenden Sie unten stehende Einstellungen um Emails an Ihre Benutzer als Teil der 2FA einzurichten. Bei Fragen schicken Sie uns bitte eine Mail anVerwenden Sie die Einstellungen unten, um die Zweifaktorauthentifizierung auf Ihrer Website einzurichten. Bei Fragen schicken Sie uns bitte eine EmailVerwenden Sie die folgenden Einstellungen, um das Aussehen der 2FA-Code-Seite so anzupassen, dass sie Ihren Branding-Anforderungen entspricht. Bei Fragen senden Sie uns eine E-Mail an %1$s.Verwenden Sie diese Einstellungen, um den Namen und die Emailadresse des Absenders für jegliche vom Plugin verschickte Korrespondenz anzupassen.Verwenden Sie diese Einstellung, um die Zwei-Faktor-Authentifizierung auf Ihrer Website zu konfigurieren. Bei Fragen zur Verwendung senden Sie uns eine E-Mail an %1$s.Verwenden Sie diese Einstellung, um das Plugin-Setup vor anderen Admins zu verbergen.Die 2FA-Einstellungen des Benutzers wurden entfernt.E-Mail wegen BenutzerkontensperreBenutzerkonto erfolgreich entsperrt. Der Benutzer kann sich wieder anmelden.Email wegen BenutzerkontenentsperrungNutzer konnte nicht erstellt werdenDer Benutzer hat sich noch nicht angemeldet. Der 2FA Status ist nicht bekannt.Benutzer wird nicht bereitgestelltBenutzernameNutzer :Benutzer können die Zweifaktorauthentifizierung in ihrem Profil einrichten und abstellen, indem sie auf die Schaltfläche dafür klicken. Aktivieren Sie diese Einstellung um Ihren Benutzern jene Schaltfläche  nicht anzuzeigen.Benutzer müssen 2FA sofort einrichten.Konfiguration prüfen und speichernJetzt freischaltenDie Lizenz wird überprüft, bitte warten…Bestätigungscode:Einrichtung bestätigenÜberprüfen Sie den Produktions-API-SchlüsselBestätigen Sie die Anmeldung, indem Sie auf %1$s klicken. Wenn Sie dies nicht sind, ignorieren Sie bitte diese E-Mail und wenden Sie sich an Ihren Website-Administrator.Seite ansehenWP 2FAWP 2FA &rsaquo; EinrichtungsassistentWP 2FA - Zweifaktorauthentifizierung für WordPressWP 2FA PluginWP 2FA EinstellungenWP 2FA ProfilseiteWP 2FA ist Ihr vertrauenswürdiger Wächter, der Ihre Website, Benutzer, Kunden, Teammitglieder und Sie sicher und besser schützt als je zuvor.WP 2FA PluginWP 2FA Modul.WP Mail SMTPWP White SecurityWarte auf Bestätigung aus der Anwendung…WillkommenWillkommen zu WP 2FAWas soll das Plugin tun, wenn die während einer Benutzeranmeldung verwendete 2FA-Methode nicht verfügbar ist?Wie soll das Plugin mit Benutzern verfahren, die 2FA nicht innerhalb der Übergangsfrist konfigurieren?Wenn kein Cookie gefunden wird oder wenn Cookies gefunden werden, jedoch die IP-Adresse abweichtWann sollten Benutzer auf vertrauenswürdigen Geräten zur Eingabe des 2FA-Codes aufgefordert werden?Wenn diese Funktion aktiviert ist, können Benutzer die Option „Dieses Gerät speichern“ auf der Anmeldeseite ankreuzen, damit das Plugin sie einige Tage lang nicht nach einem 2FA-Code fragt.Wenn Benutzer ein vertrauenswürdiges Gerät hinzufügen, wird ein Cookie im Browsern gespeichert. Wenn der Cookie nicht erkannt wird, fordert das Plugin die Benutzer auf, einen 2FA-Code einzugeben, um sich auf der Website anzumelden. Für zusätzliche Sicherheit können Sie das Plugin auch so konfigurieren, dass es den Benutzer zur Eingabe eines 2FA-Codes auffordert, wenn ein Cookie vorhanden ist, die IP-Adresse des Geräts sich jedoch von der unterscheidet, die verwendet wurde, als das Gerät zum ersten Mal gespeichert wurde.Wenn Sie die 2FA Richtlinien festlegen und die Benutzer ihren 2FA einstellen sollen, können diese entweder eine Übergangsperiode zu diesem Zweck erhalten oder sie müssen 2FA vor der nächsten Anmeldung einrichten. Welche Vorgehensweise möchten Sie anwenden:Wenn Sie 2FA für Benutzer erzwingen, erhalten diese eine Übergangsfrist, um 2FA zu konfigurieren. Wenn sie es dann innerhalb der festgelegten Zeit nicht konfigurieren, wird das Konto der betreffenden Nutzer gesperrt und muss manuell entsperrt werden. Beachten Sie, dass Benutzerkonten nicht automatisch entsperrt werden können, selbst wenn Sie die Einstellungen ändern. Aus Sicherheitsgründen müssen die Konten immer manuell entsperrt werden. Die maximale Übergangsfrist beträgt 10 Tage.Wenn Sie die 2FA erzwingen, werden Ihre Benutzer zur Einrichtung der 2FA beim nächsten Login aufgefordert werden. Die Benutzer werden einer Übergangszeit zur Einrichtung erhalten. Sie können die Dauer dieser Periode einstellen sowie einzelne Benutzer oder ganze Benutzerrollen auf der Konfigurationsseite ausschließen Wenn Sie eine der folgenden 2FA Methoden deaktivieren, steht sie Ihren Nutzern nicht mehr zur Verfügung. Sie können dies später jederzeit in den Einstellungen des Plugins ändern.Welche Emailadresse soll das Plugin als Absenderadresse verwenden?Welche der folgenden 2FA-Methoden können Benutzer verwenden?Welche Zwei-Faktor-Authentifizierungsmethoden können Ihre Nutzer verwenden?WP 2FA Logo und Hinweise entfernen (White Label) Eigene Beschriftung - White Labeling (Logo, Text, Farben & Schriftarten)Eigenes Branding der 2FA Code Seite (White labeling)White-Labeling-Funktionen: Gewinnen Sie mehr Vertrauen, indem Sie das Branding und die Tonalität Ihres Unternehmens auf alle 2FA-Seiten erweiternFalsch oder kein TokenJaSie sind dabei 2FA für alle Benutzer - einschließlich sich selbst - mit sofortiger Wirkung zu erzwingen. Da Sie Ihre 2FA Methode selbst noch nicht eingestellt haben, wollen Sie dies jetzt tun?Sie erzwingen 2FA für %1$d Benutzer, was mehr ist, als der Lizenzschlüssel zulässt. Sie haben derzeit eine Lizenz für %2$d Benutzer. Sie können den Lizenzschlüssel aktualisieren, einen neuen Lizenzschlüssel eingeben oder auf Benutzer ausschließen klicken, um zur Konfiguration zurückzukehren und die Anzahl der Benutzer zu reduzieren, für die Sie 2FA erzwingen möchten.Die Zwei-Faktor-Authentifizierung muss nun von Ihnen eingerichtet werden.Sie können die 2FA Einstellungen auf dieser Seite einrichten:Sie können später in den Plugin-Einstellungen weitere 2FA Methoden und die 2FA-Backup-Methoden konfigurieren.Sie können diese Seite mit dem Seiteneditor genauso wie alle anderen Seiten einrichten.Sie können den Assistenten nun beenden oder fortfahren, um Notfallcodes zu generieren.Sie haben sich erfolgreich angemeldet.Sie müssen angemeldet sein, um diese Seite zu sehen.Sie müssen einen neuen Seiten-Slug für die Rolle %s bereitstellen.Geben Sie ein neues, maschinenlesbares Seitenkürzel ein.Sie müssen mindestens eine Rolle oder einen Benutzer festlegenSie müssen den Lizenzschlüssel aktivieren, um WP 2FA - Zwei-Faktor-Authentifizierung für WordPress verwenden zu können. %2$sIhre 2FA-Einstellungen wurden entfernt.Ihr 2FA Einrichtung-CodeIhr Konto ist nun besser geschützt.Ihre Notfall-CodesIhr Anmeldebestätigungscode für {site_name}Ihr Login-Bestätigungslink für {site_name}Ihr Zugang ist sicherer gewordenIhre Daten sind nun besser geschützt.Ihr BenutzerIhr Benutzerkonto wurde gesperrt, weil Sie 2FA nicht innerhalb der Übergangsfrist eingerichtet haben. Bitte wenden Sie sich an den Administrator zwecks Entsperrung, damit Sie die Zweifaktorauthentifizierung einrichten können.Ihr Benutzer auf {site_name} wurde gesperrtIhr Benutzer auf {site_name} wurde entsperrtvor  %sCodevollständige Liste der unterstützten 2FA-Apps.kontaktieren Sie unser Support-TeamTagewurde entsperrt. Bitte richten Sie innerhalb der Übergangsfrist die Zweifaktorauthentifizierung ein, ansonsten tritt die Sperre erneut ein.StundenSo erhalten Sie den Authy-Produktions-API-Schlüsselhttps://wp2fa.io/https://www.wpwhitesecurity.com/MinutenniemalsKeine Übergangszeitauf der WebsiteUnser KontaktformularWebsite des Pluginsupport@wpwhitesecurity.comDieser Linkungebrauchte Notfallcodes verbleiben.languages/index.php000064400000000046150755130600010334 0ustar00<?php
/**
 * Nothing to see here.
 */
languages/wp-2fa.pot000064400000462101150755130600010340 0ustar00# Copyright (C) 2024 Melapress
# This file is distributed under the GPL v3.
msgid ""
msgstr ""
"Project-Id-Version: WP 2FA - Two-factor authentication for WordPress 2.8.0\n"
"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/wp-2fa\n"
"Last-Translator: WP White Security <info@wpwhitesecurity.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"POT-Creation-Date: 2024-07-16T10:44:32+00:00\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"X-Generator: WP-CLI 2.10.0\n"
"X-Domain: wp-2fa\n"

#. Plugin Name of the plugin
#: wp-2fa.php
msgid "WP 2FA - Two-factor authentication for WordPress"
msgstr ""

#. Plugin URI of the plugin
#. Author URI of the plugin
#: wp-2fa.php
msgid "https://melapress.com/"
msgstr ""

#. Description of the plugin
#: wp-2fa.php
msgid "Easily add an additional layer of security to your WordPress login pages. Enable Two-Factor Authentication for you and all your website users with this easy to use plugin."
msgstr ""

#. Author of the plugin
#: wp-2fa.php
msgid "Melapress"
msgstr ""

#: extensions/authy/class-authy-api.php:175
#: extensions/authy/class-authy-api.php:195
msgid "Invalid Authy Token."
msgstr ""

#: extensions/authy/class-authy-api.php:228
msgid "SMS token was sent. Please allow at least 1 minute for the text to arrive."
msgstr ""

#: extensions/authy/class-authy-render.php:56
msgid "Authy application / service integration settings"
msgstr ""

#. Translators: Authy service documentation
#: extensions/authy/class-authy-render.php:61
msgid "Here you can specify your Authy Production API Key or change an existing one. You can get the key from the Twilio console. Refer to the KB article %s for more information and instructions on how to get your key."
msgstr ""

#: extensions/authy/class-authy-render.php:62
msgid "how to get the Authy Production API key"
msgstr ""

#: extensions/authy/class-authy-render.php:69
#: extensions/authy/class-authy.php:277
msgid "Authy Production API key"
msgstr ""

#: extensions/authy/class-authy-render.php:73
#: extensions/authy/class-authy-wizard-steps.php:330
msgid "Verify the Production API Key"
msgstr ""

#: extensions/authy/class-authy-render.php:120
#: extensions/clickatell/class-clickatell-render.php:124
#: extensions/twilio/class-twilio-render.php:140
#: extensions/yubico/class-yubico-render.php:132
msgid "The primary 2FA service that you are using is unavailable. Please click the button below to login using the secondary backup method."
msgstr ""

#: extensions/authy/class-authy-render.php:140
msgid "OR"
msgstr ""

#: extensions/authy/class-authy-render.php:143
#: extensions/email-backup/class-email-backup-render.php:144
#: includes/classes/Authenticator/class-login.php:1185
#: includes/classes/Authenticator/class-login.php:1239
msgid "Verification Code:"
msgstr ""

#: extensions/authy/class-authy-user.php:81
msgid "User creation failed"
msgstr ""

#: extensions/authy/class-authy-user.php:141
#: extensions/authy/class-authy-user.php:218
#: extensions/authy/class-authy-user.php:388
#: extensions/authy/class-authy.php:389
#: extensions/authy/class-authy.php:477
#: extensions/authy/class-authy.php:741
#: extensions/clickatell/class-clickatell-user.php:137
#: extensions/clickatell/class-clickatell-user.php:209
#: extensions/clickatell/class-clickatell.php:139
#: extensions/clickatell/class-clickatell.php:184
#: extensions/clickatell/class-clickatell.php:713
#: extensions/out-of-band/class-out-of-band.php:403
#: extensions/twilio/class-twilio-user.php:137
#: extensions/twilio/class-twilio-user.php:209
#: extensions/twilio/class-twilio.php:173
#: extensions/twilio/class-twilio.php:224
#: extensions/twilio/class-twilio.php:803
#: extensions/yubico/class-yubico-user.php:120
#: extensions/yubico/class-yubico-user.php:203
#: extensions/yubico/class-yubico.php:140
#: extensions/yubico/class-yubico.php:183
#: extensions/yubico/class-yubico.php:677
#: includes/classes/Admin/class-setup-wizard.php:580
#: includes/classes/Admin/Helpers/class-ajax-helper.php:187
msgid "Nonce checking failed"
msgstr ""

#: extensions/authy/class-authy-user.php:144
#: extensions/authy/class-authy-user.php:221
#: extensions/authy/class-authy-user.php:391
#: extensions/authy/class-authy.php:392
#: extensions/authy/class-authy.php:480
#: extensions/authy/class-authy.php:744
#: extensions/clickatell/class-clickatell-user.php:140
#: extensions/clickatell/class-clickatell-user.php:212
#: extensions/clickatell/class-clickatell.php:142
#: extensions/clickatell/class-clickatell.php:187
#: extensions/clickatell/class-clickatell.php:716
#: extensions/out-of-band/class-out-of-band.php:406
#: extensions/twilio/class-twilio-user.php:140
#: extensions/twilio/class-twilio-user.php:212
#: extensions/twilio/class-twilio.php:176
#: extensions/twilio/class-twilio.php:227
#: extensions/twilio/class-twilio.php:806
#: extensions/yubico/class-yubico-user.php:123
#: extensions/yubico/class-yubico-user.php:206
#: extensions/yubico/class-yubico.php:143
#: extensions/yubico/class-yubico.php:186
#: extensions/yubico/class-yubico.php:680
msgid "Nonce is not provided"
msgstr ""

#: extensions/authy/class-authy-user.php:147
#: extensions/authy/class-authy-user.php:224
#: extensions/authy/class-authy-user.php:394
#: extensions/authy/class-authy-user.php:399
#: extensions/authy/class-authy-user.php:404
#: extensions/authy/class-authy.php:395
#: extensions/authy/class-authy.php:483
#: extensions/authy/class-authy.php:747
#: extensions/clickatell/class-clickatell-user.php:143
#: extensions/clickatell/class-clickatell-user.php:215
#: extensions/clickatell/class-clickatell.php:145
#: extensions/clickatell/class-clickatell.php:169
#: extensions/clickatell/class-clickatell.php:190
#: extensions/clickatell/class-clickatell.php:719
#: extensions/twilio/class-twilio-user.php:143
#: extensions/twilio/class-twilio-user.php:215
#: extensions/twilio/class-twilio.php:179
#: extensions/twilio/class-twilio.php:209
#: extensions/twilio/class-twilio.php:230
#: extensions/twilio/class-twilio.php:809
#: extensions/yubico/class-yubico-user.php:126
#: extensions/yubico/class-yubico-user.php:209
#: extensions/yubico/class-yubico.php:146
#: extensions/yubico/class-yubico.php:168
#: extensions/yubico/class-yubico.php:189
#: extensions/yubico/class-yubico.php:683
#: includes/classes/Utils/class-user-utils.php:370
msgid "Not allowed"
msgstr ""

#: extensions/authy/class-authy-user.php:234
#: extensions/clickatell/class-clickatell-user.php:224
#: extensions/twilio/class-twilio-user.php:224
#: extensions/yubico/class-yubico-user.php:222
msgid "Wrong or no token"
msgstr ""

#. translators: $s the site name.
#: extensions/authy/class-authy-user.php:297
msgid "Login request to %s site"
msgstr ""

#: extensions/authy/class-authy-user.php:303
msgid "Username"
msgstr ""

#: extensions/authy/class-authy-user.php:304
msgid "IP Address"
msgstr ""

#: extensions/authy/class-authy-user.php:308
msgid "Email"
msgstr ""

#: extensions/authy/class-authy-user.php:312
msgid "Name"
msgstr ""

#: extensions/authy/class-authy-wizard-steps.php:104
#: extensions/clickatell/class-clickatell-wizard-steps.php:106
#: extensions/email-backup/class-email-backup-render.php:108
#: extensions/twilio/class-twilio-wizard-steps.php:106
#: extensions/yubico/class-yubico-wizard-steps.php:105
#: includes/classes/Admin/Views/class-wizard-steps.php:174
#: includes/classes/Admin/Views/class-wizard-steps.php:180
#: includes/classes/Admin/Views/class-wizard-steps.php:321
#: includes/classes/Admin/Views/class-wizard-steps.php:324
#: includes/classes/Admin/Views/class-wizard-steps.php:350
msgid "Close wizard"
msgstr ""

#: extensions/authy/class-authy-wizard-steps.php:116
#: extensions/authy/class-authy-wizard-steps.php:194
#: extensions/clickatell/class-clickatell-wizard-steps.php:118
#: extensions/clickatell/class-clickatell-wizard-steps.php:193
#: extensions/out-of-band/class-oob-wizard-steps.php:103
#: extensions/out-of-band/class-oob-wizard-steps.php:202
#: extensions/twilio/class-twilio-wizard-steps.php:118
#: extensions/twilio/class-twilio-wizard-steps.php:193
#: extensions/yubico/class-yubico-wizard-steps.php:117
#: extensions/yubico/class-yubico-wizard-steps.php:191
#: includes/classes/Admin/Methods/class-email-wizard-steps.php:96
#: includes/classes/Admin/Methods/class-email-wizard-steps.php:316
#: includes/classes/Admin/Methods/class-totp-wizard-steps.php:258
msgid "I'm Ready"
msgstr ""

#: extensions/authy/class-authy-wizard-steps.php:116
#: extensions/clickatell/class-clickatell-wizard-steps.php:118
#: extensions/twilio/class-twilio-wizard-steps.php:118
msgid "Change phone"
msgstr ""

#: extensions/authy/class-authy-wizard-steps.php:187
msgid "Authy phone"
msgstr ""

#: extensions/authy/class-authy-wizard-steps.php:195
#: extensions/authy/class-authy-wizard-steps.php:216
#: extensions/clickatell/class-clickatell-wizard-steps.php:210
#: extensions/out-of-band/class-oob-wizard-steps.php:203
#: extensions/out-of-band/class-oob-wizard-steps.php:229
#: extensions/settings-import-export/class-settings-import-export.php:398
#: extensions/twilio/class-twilio-wizard-steps.php:210
#: extensions/yubico/class-yubico-wizard-steps.php:208
#: includes/classes/Admin/class-user-profile.php:450
#: includes/classes/Admin/Methods/class-email-wizard-steps.php:317
#: includes/classes/Admin/Methods/class-email-wizard-steps.php:343
#: includes/classes/Admin/Methods/class-totp-wizard-steps.php:259
#: includes/classes/Admin/Methods/class-totp-wizard-steps.php:282
#: includes/classes/Admin/Views/class-wizard-steps.php:64
msgid "Cancel"
msgstr ""

#: extensions/authy/class-authy-wizard-steps.php:199
#: extensions/clickatell/class-clickatell-wizard-steps.php:197
#: extensions/out-of-band/class-oob-wizard-steps.php:207
#: extensions/twilio/class-twilio-wizard-steps.php:197
#: extensions/yubico/class-yubico-wizard-steps.php:195
#: includes/classes/Admin/Methods/class-email-wizard-steps.php:321
#: includes/classes/Admin/Methods/class-totp-wizard-steps.php:262
msgid "Verify configuration"
msgstr ""

#: extensions/authy/class-authy-wizard-steps.php:209
msgid "code"
msgstr ""

#: extensions/authy/class-authy-wizard-steps.php:215
#: extensions/clickatell/class-clickatell-wizard-steps.php:209
#: extensions/out-of-band/class-oob-wizard-steps.php:225
#: extensions/twilio/class-twilio-wizard-steps.php:209
#: extensions/yubico/class-yubico-wizard-steps.php:207
#: includes/classes/Admin/Methods/class-email-wizard-steps.php:339
#: includes/classes/Admin/Methods/class-totp-wizard-steps.php:281
msgid "Validate & Save"
msgstr ""

#: extensions/authy/class-authy-wizard-steps.php:290
#: extensions/clickatell/class-clickatell-wizard-steps.php:285
#: extensions/twilio/class-twilio-wizard-steps.php:285
msgid "Please verify credentials to enable."
msgstr ""

#: extensions/authy/class-authy-wizard-steps.php:293
msgid "Push notification via Authy App"
msgstr ""

#: extensions/authy/class-authy-wizard-steps.php:299
msgid "When using this method, users will receive a notification on their Authy app to confirm the log in. "
msgstr ""

#. Translators: Authy service documentation
#: extensions/authy/class-authy-wizard-steps.php:303
msgid "Once they tap / approve the notification they log in to the website. Refer to the %s for more information on how to use the Authy with WP 2FA for two-factor authentication on your website."
msgstr ""

#: extensions/authy/class-authy-wizard-steps.php:304
msgid "Authy integration documentation"
msgstr ""

#: extensions/authy/class-authy-wizard-steps.php:313
#: extensions/clickatell/class-clickatell-wizard-steps.php:308
#: extensions/twilio/class-twilio-wizard-steps.php:308
#: includes/classes/Admin/class-settings-page.php:62
#: includes/classes/Admin/class-settings-page.php:137
msgid "Settings"
msgstr ""

#: extensions/authy/class-authy-wizard-steps.php:315
#: extensions/clickatell/class-clickatell-wizard-steps.php:310
#: extensions/twilio/class-twilio-wizard-steps.php:310
msgid "Configure connection details"
msgstr ""

#: extensions/authy/class-authy-wizard-steps.php:315
#: extensions/clickatell/class-clickatell-wizard-steps.php:310
#: extensions/twilio/class-twilio-wizard-steps.php:310
msgid "Delete connection details"
msgstr ""

#: extensions/authy/class-authy-wizard-steps.php:326
msgid "Authy Production API"
msgstr ""

#: extensions/authy/class-authy.php:266
#: extensions/authy/class-authy.php:270
#: extensions/authy/class-authy.php:271
#: extensions/authy/class-authy.php:272
#: extensions/clickatell/class-clickatell.php:363
#: extensions/clickatell/class-clickatell.php:367
#: extensions/clickatell/class-clickatell.php:368
#: extensions/clickatell/class-clickatell.php:369
#: extensions/twilio/class-twilio.php:399
#: extensions/twilio/class-twilio.php:403
#: extensions/twilio/class-twilio.php:404
#: extensions/twilio/class-twilio.php:405
#: extensions/yubico/class-yubico.php:334
#: extensions/yubico/class-yubico.php:338
#: extensions/yubico/class-yubico.php:339
#: extensions/yubico/class-yubico.php:340
msgid "Invalid number"
msgstr ""

#: extensions/authy/class-authy.php:267
#: extensions/clickatell/class-clickatell.php:364
#: extensions/twilio/class-twilio.php:400
#: extensions/yubico/class-yubico.php:335
msgid "Invalid country code"
msgstr ""

#: extensions/authy/class-authy.php:268
#: extensions/clickatell/class-clickatell.php:365
#: extensions/twilio/class-twilio.php:401
#: extensions/yubico/class-yubico.php:336
msgid "Too short"
msgstr ""

#: extensions/authy/class-authy.php:269
#: extensions/clickatell/class-clickatell.php:366
#: extensions/twilio/class-twilio.php:402
#: extensions/yubico/class-yubico.php:337
msgid "Too long"
msgstr ""

#: extensions/authy/class-authy.php:276
msgid "The Authy production API key is valid."
msgstr ""

#: extensions/authy/class-authy.php:320
#: extensions/authy/class-authy.php:613
msgid "Authy"
msgstr ""

#: extensions/authy/class-authy.php:324
#: extensions/clickatell/class-clickatell.php:424
#: extensions/twilio/class-twilio.php:460
#: extensions/yubico/class-yubico.php:383
#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:123
#: includes/classes/Admin/SettingsPages/class-settings-page-render.php:131
#: includes/classes/Admin/SettingsPages/class-settings-page-render.php:152
msgid "Use the settings below to configure the properties of the two-factor authentication on your website and how users use it. If you have any questions send us an email at"
msgstr ""

#: extensions/authy/class-authy.php:325
#: extensions/clickatell/class-clickatell.php:425
#: extensions/integrations/class-wp2fa-integrations.php:103
#: extensions/settings-import-export/class-settings-import-exporter.php:246
#: extensions/twilio/class-twilio.php:461
#: extensions/user-licensing/class-wp2fa-user-licensing.php:70
#: extensions/yubico/class-yubico.php:384
#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:124
#: includes/classes/Admin/SettingsPages/class-settings-page-render.php:132
#: includes/classes/Admin/SettingsPages/class-settings-page-render.php:153
#: includes/classes/Admin/SettingsPages/class-settings-page-render.php:174
msgid "support@melapress.com"
msgstr ""

#: extensions/authy/class-authy.php:548
msgid "Push notification via Authy app"
msgstr ""

#: extensions/authy/class-authy.php:638
#: extensions/clickatell/class-clickatell.php:650
#: extensions/email-backup/class-email-backup.php:341
#: extensions/twilio/class-twilio.php:740
#: extensions/yubico/class-yubico.php:630
#: includes/classes/Authenticator/class-login.php:973
#: includes/classes/Authenticator/class-login.php:999
#: includes/classes/Authenticator/class-login.php:1024
#: includes/classes/Authenticator/class-reset-passord.php:207
msgid "<strong>Error</strong>: User can not be authenticated."
msgstr ""

#: extensions/authy/class-authy.php:644
#: extensions/clickatell/class-clickatell.php:656
#: extensions/email-backup/class-email-backup.php:347
#: extensions/twilio/class-twilio.php:746
#: extensions/yubico/class-yubico.php:636
#: includes/classes/Authenticator/class-login.php:215
#: includes/classes/Authenticator/class-login.php:558
#: includes/classes/Authenticator/class-login.php:962
#: includes/classes/Authenticator/class-login.php:979
#: includes/classes/Authenticator/class-login.php:1004
#: includes/classes/Authenticator/class-login.php:1030
#: includes/classes/Authenticator/class-reset-passord.php:80
#: includes/classes/Authenticator/class-reset-passord.php:196
#: includes/classes/Authenticator/class-reset-passord.php:213
msgid "Failed to create a login nonce."
msgstr ""

#: extensions/authy/class-authy.php:648
#: extensions/clickatell/class-clickatell.php:660
#: extensions/email-backup/class-email-backup.php:351
#: extensions/twilio/class-twilio.php:750
#: extensions/yubico/class-yubico.php:640
msgid "One of the required parameters is missing."
msgstr ""

#: extensions/authy/class-authy.php:651
#: extensions/clickatell/class-clickatell.php:663
#: extensions/email-backup/class-email-backup.php:357
#: extensions/twilio/class-twilio.php:753
#: extensions/yubico/class-yubico.php:643
#: includes/classes/Authenticator/class-login.php:983
#: includes/classes/Authenticator/class-login.php:1036
#: includes/classes/Authenticator/class-reset-passord.php:219
msgid "ERROR: Invalid verification code."
msgstr ""

#. translators: hyperlink "contact our support team".
#: extensions/authy/class-authy.php:687
msgid "The %1$s 2FA service is unavailable. Please check the configuration and the service's dashboard to restore functionality. If the problem persists, %2$s."
msgstr ""

#: extensions/authy/class-authy.php:692
msgid "contact our support team"
msgstr ""

#: extensions/clickatell/class-clickatell-api.php:75
msgid "Clickatell is not set correctly"
msgstr ""

#: extensions/clickatell/class-clickatell-render.php:59
msgid "Clickatell application / service integration settings"
msgstr ""

#. Translators: Clickatell service documentation
#: extensions/clickatell/class-clickatell-render.php:64
msgid "Here you can specify your Clickatell Production SID Key, Auth and number / ID or change an existing ones. You can get the them from the Clickatell console. Refer to the article %s for more information and instructions on how to get your keys."
msgstr ""

#: extensions/clickatell/class-clickatell-render.php:65
msgid "how to get the Clickatell Production SID key"
msgstr ""

#: extensions/clickatell/class-clickatell-render.php:72
#: extensions/clickatell/class-clickatell-wizard-steps.php:321
msgid "API key"
msgstr ""

#: extensions/clickatell/class-clickatell-render.php:80
#: extensions/clickatell/class-clickatell-wizard-steps.php:329
#: extensions/twilio/class-twilio-render.php:96
#: extensions/twilio/class-twilio-wizard-steps.php:345
#: extensions/yubico/class-yubico-wizard-steps.php:346
msgid "Verify the details"
msgstr ""

#: extensions/clickatell/class-clickatell-render.php:82
#: extensions/clickatell/class-clickatell-wizard-steps.php:331
msgid "Verify the Clickatell API key"
msgstr ""

#: extensions/clickatell/class-clickatell-render.php:151
#: extensions/twilio/class-twilio-render.php:167
msgid "Authentication code:"
msgstr ""

#: extensions/clickatell/class-clickatell-render.php:158
msgid "Unfortunately there is a problem with Clickatell configuration. Contact the Administrator for further assistance."
msgstr ""

#: extensions/clickatell/class-clickatell-user.php:88
#: extensions/twilio/class-twilio-user.php:88
msgid "SMS sending failed"
msgstr ""

#: extensions/clickatell/class-clickatell-wizard-steps.php:186
#: extensions/twilio/class-twilio-wizard-steps.php:186
msgid "Phone number"
msgstr ""

#: extensions/clickatell/class-clickatell-wizard-steps.php:288
msgid "One-time code via SMS (with Clickatell)"
msgstr ""

#: extensions/clickatell/class-clickatell-wizard-steps.php:294
#: extensions/twilio/class-twilio-wizard-steps.php:294
#: extensions/yubico/class-yubico-wizard-steps.php:292
msgid "When using this method, users will receive an SMS message with the one-time code. "
msgstr ""

#. Translators: Clickatell service documentation
#: extensions/clickatell/class-clickatell-wizard-steps.php:298
msgid "Refer to the %s for more information on how to use the Clickatell with WP 2FA for two-factor authentication on your website."
msgstr ""

#: extensions/clickatell/class-clickatell-wizard-steps.php:299
msgid "Clickatell integration documentation"
msgstr ""

#: extensions/clickatell/class-clickatell.php:258
msgid "Provided Clickatell settings are invalid"
msgstr ""

#: extensions/clickatell/class-clickatell.php:373
msgid "The Clickatell connection has been configured successfully."
msgstr ""

#: extensions/clickatell/class-clickatell.php:374
msgid "Clickatell SMS service configuration"
msgstr ""

#: extensions/clickatell/class-clickatell.php:564
msgid "One-time code via SMS (Clickatell)"
msgstr ""

#: extensions/email-backup/class-email-backup-render.php:86
msgid "Use my user email "
msgstr ""

#: extensions/email-backup/class-email-backup-render.php:95
#: extensions/out-of-band/class-oob-wizard-steps.php:193
#: includes/classes/Admin/Methods/class-email-wizard-steps.php:292
msgid "Use a different email address:"
msgstr ""

#: extensions/email-backup/class-email-backup-render.php:96
#: extensions/out-of-band/class-oob-wizard-steps.php:194
#: includes/classes/Admin/Methods/class-email-wizard-steps.php:293
msgid "Email address"
msgstr ""

#: extensions/email-backup/class-email-backup-render.php:104
msgid "Save email backup options"
msgstr ""

#: extensions/email-backup/class-email-backup-render.php:105
msgid "Save 2FA backup email address"
msgstr ""

#: extensions/email-backup/class-email-backup-render.php:186
msgid "Or, send me a code to my email."
msgstr ""

#: extensions/email-backup/class-email-backup-render.php:241
msgid "Allow users to use email based 2FA as secondary backup method to"
msgstr ""

#: extensions/email-backup/class-email-backup-render.php:244
msgid "By enabling this feature users can also configure and use the \"one-time code via email\" 2FA method as a secondary backup method. This allows them to receive a one-time login code via email if they need to login to the website and cannot generate the login code from their 2FA app. This feature only applies to users who are using smartphone / app 2FA methods."
msgstr ""

#: extensions/email-backup/class-email-backup-render.php:247
#: extensions/out-of-band/class-oob-wizard-steps.php:329
#: includes/classes/Admin/Methods/class-email-wizard-steps.php:205
msgid "Allow user to specify the email address of choice"
msgstr ""

#: extensions/email-backup/class-email-backup-render.php:253
#: extensions/email-backup/class-email-backup-render.php:309
#: extensions/out-of-band/class-oob-wizard-steps.php:335
#: extensions/role-settings/class-role-settings-render.php:545
#: includes/classes/Admin/class-user-profile.php:392
#: includes/classes/Admin/class-user-profile.php:536
#: includes/classes/Admin/Methods/class-email-wizard-steps.php:211
#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:697
msgid "Yes"
msgstr ""

#: extensions/email-backup/class-email-backup-render.php:257
#: extensions/email-backup/class-email-backup-render.php:310
#: extensions/out-of-band/class-oob-wizard-steps.php:339
#: extensions/role-settings/class-role-settings-render.php:555
#: includes/classes/Admin/class-user-profile.php:393
#: includes/classes/Admin/class-user-profile.php:537
#: includes/classes/Admin/Methods/class-email-wizard-steps.php:215
#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:704
msgid "No"
msgstr ""

#: extensions/email-backup/class-email-backup-render.php:298
msgid "Configure backup email"
msgstr ""

#: extensions/email-backup/class-email-backup-render.php:300
msgid "Remove backup email method"
msgstr ""

#: extensions/email-backup/class-email-backup-render.php:306
msgid "Remove 2FA backup mail?"
msgstr ""

#: extensions/email-backup/class-email-backup-render.php:307
msgid "Are you sure you want to remove the backup email method?"
msgstr ""

#: extensions/email-backup/class-email-backup-render.php:354
msgid "Email backup page text"
msgstr ""

#: extensions/email-backup/class-email-backup-render.php:359
#: extensions/white-labeling/class-white-labeling-render.php:731
#: includes/classes/Admin/class-user-notices.php:201
#: includes/classes/Admin/class-user-notices.php:210
#: includes/classes/Admin/class-user-notices.php:219
#: includes/classes/Admin/class-user-notices.php:228
#: includes/classes/Admin/SettingsPages/class-settings-page-white-label.php:317
#: includes/classes/Admin/SettingsPages/class-settings-page-white-label.php:330
msgid "Note:"
msgstr ""

#: extensions/email-backup/class-email-backup-render.php:359
#: includes/classes/Admin/class-user-notices.php:201
#: includes/classes/Admin/class-user-notices.php:210
#: includes/classes/Admin/class-user-notices.php:219
#: includes/classes/Admin/class-user-notices.php:228
#: includes/classes/Admin/SettingsPages/class-settings-page-white-label.php:317
#: includes/classes/Admin/SettingsPages/class-settings-page-white-label.php:330
msgid "Only plain text is allowed."
msgstr ""

#: extensions/email-backup/class-email-backup.php:67
msgid "Enter a backup email code."
msgstr ""

#: extensions/email-backup/class-email-backup.php:114
msgid "Backup Email"
msgstr ""

#: extensions/email-backup/class-email-backup.php:193
msgid "Receive code over email: you will receive a one-time code via email which you need to login and you cannot generate a code from the app."
msgstr ""

#: extensions/email-backup/class-email-backup.php:282
#: includes/classes/Admin/Helpers/class-ajax-helper.php:55
#: includes/classes/Admin/Helpers/class-ajax-helper.php:101
#: includes/classes/Admin/Helpers/class-ajax-helper.php:152
#: includes/classes/Admin/Helpers/class-ajax-helper.php:244
#: includes/classes/Admin/Helpers/class-ajax-helper.php:278
#: includes/classes/Admin/SettingsPages/class-settings-page-email.php:251
msgid "Nonce verification failed."
msgstr ""

#: extensions/email-backup/class-email-backup.php:355
#: includes/classes/Authenticator/class-login.php:1034
#: includes/classes/Authenticator/class-reset-passord.php:217
msgid "A new code has been sent."
msgstr ""

#: extensions/integrations/class-wp2fa-integrations-render.php:48
msgid "Integrations plugins / service integration settings"
msgstr ""

#. Translators: Twilio service documentation
#: extensions/integrations/class-wp2fa-integrations-render.php:53
msgid "Here you can specify 2FA integration settings for different plugins."
msgstr ""

#: extensions/integrations/class-wp2fa-integrations.php:98
msgid "Integrations"
msgstr ""

#: extensions/integrations/class-wp2fa-integrations.php:102
#: extensions/user-licensing/class-wp2fa-user-licensing.php:69
msgid "Use the settings below to configure the WP2FA plugin integration. If you have any questions send us an email at"
msgstr ""

#: extensions/integrations/plugins/class-integrations-plugins-base.php:109
msgid "Enable this integration so a 2FA configuration entry is automatically added in the menu of your WooCommerce's user portal."
msgstr ""

#: extensions/integrations/plugins/class-woocommerce.php:175
msgid "Enable"
msgstr ""

#: extensions/integrations/plugins/class-woocommerce.php:181
msgid "Custom 2FA endpoint"
msgstr ""

#: extensions/integrations/plugins/class-woocommerce.php:187
msgid "Custom 2FA menu label in account page"
msgstr ""

#: extensions/out-of-band/class-oob-wizard-steps.php:103
#: includes/classes/Admin/Methods/class-email-wizard-steps.php:96
msgid "Change email address"
msgstr ""

#: extensions/out-of-band/class-oob-wizard-steps.php:184
#: includes/classes/Admin/Methods/class-email-wizard-steps.php:283
msgid "Use my user email ("
msgstr ""

#: extensions/out-of-band/class-oob-wizard-steps.php:184
#: includes/classes/Admin/Methods/class-email-wizard-steps.php:283
msgid ")"
msgstr ""

#: extensions/out-of-band/class-oob-wizard-steps.php:200
msgid "Note: you should be able to access the mailbox of the email address to complete the following step."
msgstr ""

#: extensions/out-of-band/class-oob-wizard-steps.php:213
#: includes/classes/Admin/Methods/class-email-wizard-steps.php:327
#: includes/classes/Admin/Methods/class-totp-wizard-steps.php:268
msgid "Authentication Code"
msgstr ""

#: extensions/out-of-band/class-oob-wizard-steps.php:227
#: includes/classes/Admin/Methods/class-email-wizard-steps.php:341
msgid "Send me another code"
msgstr ""

#: extensions/out-of-band/class-oob-wizard-steps.php:296
#: extensions/out-of-band/class-out-of-band.php:541
msgid "Link via email"
msgstr ""

#. translators: link to the knowledge base website
#: extensions/out-of-band/class-oob-wizard-steps.php:304
msgid "When using this method, users will receive a link in an email, and once they click on the link they log in. Therefore, email deliverability is very important. Users using this method should whitelist the address from which the emails are sent. By default, this is the email address configured in your WordPress. You can run an email test from the plugin's settings to confirm email deliverability. If you have had email deliverability / reliability issues, we highly recommend you to install the free plugin %s. Allowing users to set up a secondary 2FA method is highly recommended. You can do this in the next step of the wizard. This will allow users to log in using an alternative method should they, for example lose access to their phone."
msgstr ""

#: extensions/out-of-band/class-oob-wizard-steps.php:305
msgid "WP Mail SMTP"
msgstr ""

#: extensions/out-of-band/class-oob-wizard-steps.php:321
msgid "Link will be valid for: "
msgstr ""

#: extensions/out-of-band/class-oob-wizard-steps.php:323
msgid "minutes"
msgstr ""

#: extensions/out-of-band/class-oob-wizard-steps.php:401
#: extensions/out-of-band/class-out-of-band.php:542
msgid "When using this method email deliverability is very important. If you are not sure your website's email systems works well, refer to the guide on "
msgstr ""

#: extensions/out-of-band/class-oob-wizard-steps.php:401
#: extensions/out-of-band/class-out-of-band.php:542
msgid "how to improve and ensure email deliverability on WordPress websites"
msgstr ""

#: extensions/out-of-band/class-oob-wizard-steps.php:401
#: extensions/out-of-band/class-out-of-band.php:542
msgid "to ensure you can receive emails"
msgstr ""

#: extensions/out-of-band/class-out-of-band.php:373
msgid "Usage of this filter is deprecated."
msgstr ""

#: extensions/out-of-band/class-out-of-band.php:420
#: extensions/out-of-band/class-out-of-band.php:472
msgid "User is not provided"
msgstr ""

#: extensions/out-of-band/class-out-of-band.php:444
msgid "Your 2FA setup code"
msgstr ""

#. translators: Test oob code.
#: extensions/out-of-band/class-out-of-band.php:448
msgid "Please enter this code to confirm the 2FA setup: %s"
msgstr ""

#: extensions/out-of-band/class-out-of-band.php:458
msgid "this link"
msgstr ""

#: extensions/out-of-band/class-out-of-band.php:540
msgid "An email with a verification link has been sent to your email address. If you are using the same browser and device, please click on the link to verify the login. If you are not, please copy the link and paste it in the address bar above."
msgstr ""

#: extensions/out-of-band/class-out-of-band.php:593
msgid "Your login confirmation link for {site_name}"
msgstr ""

#: extensions/out-of-band/class-out-of-band.php:596
msgid "Someone from {user_ip_address} is trying to log in to {site_name}."
msgstr ""

#. translators: The login code
#: extensions/out-of-band/class-out-of-band.php:600
msgid "Verify the login by clicking %1$s. If this is not you, please ignore this email and contact your website administrator."
msgstr ""

#: extensions/out-of-band/class-out-of-band.php:604
msgid "Note: as a security precaution, the login verification link only works when the link is clicked from the same browser and device combination from where you are trying to log in. If this is not the same browser and device, please copy the link and manually paste it in the address bar of the browser which you are using to log in to the website."
msgstr ""

#: extensions/out-of-band/class-out-of-band.php:605
#: includes/classes/class-wp2fa.php:553
#: includes/classes/class-wp2fa.php:582
#: includes/classes/class-wp2fa.php:600
#: includes/classes/class-wp2fa.php:614
#: includes/classes/class-wp2fa.php:624
msgid "Email sent by"
msgstr ""

#: extensions/out-of-band/class-out-of-band.php:606
#: includes/classes/class-wp2fa.php:554
#: includes/classes/class-wp2fa.php:583
#: includes/classes/class-wp2fa.php:601
msgid "WP 2FA plugin."
msgstr ""

#: extensions/out-of-band/class-out-of-band.php:628
#: includes/classes/Admin/class-user-profile.php:725
msgid "No form"
msgstr ""

#: extensions/out-of-band/class-out-of-band.php:639
#: extensions/out-of-band/class-out-of-band.php:646
#: includes/classes/Admin/class-user-profile.php:748
msgid "Invalid Two Factor Authentication code."
msgstr ""

#: extensions/out-of-band/class-out-of-band.php:644
#: includes/classes/Admin/class-user-profile.php:760
msgid "Please enter the code to finalize the 2FA setup."
msgstr ""

#: extensions/out-of-band/class-out-of-band.php:670
msgid "No code is presented."
msgstr ""

#: extensions/out-of-band/class-out-of-band.php:811
msgid "Login link email"
msgstr ""

#: extensions/out-of-band/class-out-of-band.php:812
msgid "This is the email sent to a user when an email with a login link is required"
msgstr ""

#: extensions/out-of-band/class-out-of-band.php:844
msgid "Out-of-band"
msgstr ""

#: extensions/reporting/class-abstract-report.php:89
msgid "Role"
msgstr ""

#: extensions/reporting/class-abstract-report.php:90
msgid "Total"
msgstr ""

#: extensions/reporting/class-methods-report.php:39
msgid "2FA methods and backup codes"
msgstr ""

#: extensions/reporting/class-reporting.php:85
#: extensions/reporting/class-reporting.php:86
#: extensions/reporting/class-reporting.php:113
msgid "Reports"
msgstr ""

#: extensions/reporting/class-reporting.php:111
#: includes/classes/Admin/class-help-contact-us.php:59
#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:100
#: includes/classes/Admin/SettingsPages/class-settings-page-render.php:47
msgid "These settings have been disabled by your site administrator, please contact them for further assistance."
msgstr ""

#: extensions/reporting/class-reporting.php:115
msgid "In this page you will find a number of reports which allow you to get a better overview of the current state of 2FA on your website."
msgstr ""

#: extensions/reporting/class-reporting.php:137
msgid "Your report is preparing, please wait as it could take some time. Once ready it will automatically refresh the page."
msgstr ""

#: extensions/reporting/class-reporting.php:138
msgid "Processed users:"
msgstr ""

#: extensions/reporting/class-reporting.php:138
msgid "of"
msgstr ""

#: extensions/reporting/class-reporting.php:138
msgid "total users "
msgstr ""

#: extensions/reporting/class-reporting.php:186
msgid "Reports data"
msgstr ""

#: extensions/reporting/class-user-status-report.php:42
msgid "2FA users setup attribute"
msgstr ""

#. translators: Test oob code.
#: extensions/role-settings/class-role-settings-controller.php:196
msgid "Grace period must be at least 1 day/hour for role %s."
msgstr ""

#. translators: Test oob code.
#: extensions/role-settings/class-role-settings-controller.php:238
msgid "You must provide a new page slug for role %s."
msgstr ""

#: extensions/role-settings/class-role-settings-render.php:100
msgid "Site-wide policies"
msgstr ""

#: extensions/role-settings/class-role-settings-render.php:174
msgid "Configure different 2FA settings for this user role"
msgstr ""

#: extensions/role-settings/class-role-settings-render.php:191
#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:55
msgid "Which of the below 2FA methods can users use?"
msgstr ""

#: extensions/role-settings/class-role-settings-render.php:194
msgid "You can change the order of the 2FA methods and how they appear for the users with drag and drop. Simply hover the mouse over the method, click the mouse button and drag that method to where you'd like it to appear in the list. Once you are ready save the settings."
msgstr ""

#: extensions/role-settings/class-role-settings-render.php:197
msgid "Select the allowed primary 2FA methods"
msgstr ""

#: extensions/role-settings/class-role-settings-render.php:200
#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:62
msgid "Primary 2FA methods:"
msgstr ""

#: extensions/role-settings/class-role-settings-render.php:218
#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:84
msgid "Secondary 2FA methods:"
msgstr ""

#: extensions/role-settings/class-role-settings-render.php:244
#: includes/classes/Admin/Methods/class-backup-codes.php:228
#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:93
#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:171
msgid "Backup codes"
msgstr ""

#: extensions/role-settings/class-role-settings-render.php:246
#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:99
msgid "Backup codes are a secondary method which you can use to log in to the website in case the primary 2FA method is unavailable. Therefore they can't be enabled and used as a primary method."
msgstr ""

#: extensions/role-settings/class-role-settings-render.php:404
#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:891
msgid "Should users be asked to setup 2FA instantly or should they have a grace period?"
msgstr ""

#: extensions/role-settings/class-role-settings-render.php:406
#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:893
msgid "When you enforce 2FA on users they have a grace period to configure 2FA. If they fail to configure it within the configured stipulated time, their account will be locked and have to be unlocked manually. Note that user accounts cannot be unlocked automatically, even if you change the settings. As a security precaution they always have to be unlocked them manually. Maximum grace period is 10 days."
msgstr ""

#: extensions/role-settings/class-role-settings-render.php:406
#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:893
#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:219
msgid "Learn more."
msgstr ""

#: extensions/role-settings/class-role-settings-render.php:412
#: includes/classes/Admin/class-setup-wizard.php:522
#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:899
msgid "Grace period"
msgstr ""

#: extensions/role-settings/class-role-settings-render.php:424
#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:536
msgid "Users have to configure 2FA straight away."
msgstr ""

#: extensions/role-settings/class-role-settings-render.php:433
#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:543
msgid "Give users a grace period to configure 2FA"
msgstr ""

#: extensions/role-settings/class-role-settings-render.php:442
#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:552
msgid "hours"
msgstr ""

#: extensions/role-settings/class-role-settings-render.php:449
#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:558
msgid "days"
msgstr ""

#: extensions/role-settings/class-role-settings-render.php:489
msgid "Do you want to redirect the user to a specific page after completing the 2FA setup wizard?"
msgstr ""

#: extensions/role-settings/class-role-settings-render.php:491
#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:810
msgid "Specify a URL of a page where you want to redirect the users once they complete the 2FA setup wizard. Leave empty for default behaviour, in which users are redirected back to the page from where they launched the wizard."
msgstr ""

#: extensions/role-settings/class-role-settings-render.php:496
#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:815
msgid "Redirect users after 2FA setup to"
msgstr ""

#: extensions/role-settings/class-role-settings-render.php:528
#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:683
msgid "Can users access the WordPress dashboard or you have custom profile pages? "
msgstr ""

#: extensions/role-settings/class-role-settings-render.php:530
#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:685
msgid "If your users do not have access to the WordPress dashboard (because you use custom user profile pages) enable this option. Once enabled, the plugin creates a page which ONLY authenticated users can access to configure their user 2FA settings. A link to this page is sent in the 2FA welcome email."
msgstr ""

#: extensions/role-settings/class-role-settings-render.php:535
#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:690
msgid "Frontend 2FA settings page"
msgstr ""

#: extensions/role-settings/class-role-settings-render.php:561
#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:710
msgid "Frontend 2FA settings page URL"
msgstr ""

#: extensions/role-settings/class-role-settings-render.php:596
#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:743
msgid "Edit Page"
msgstr ""

#: extensions/role-settings/class-role-settings-render.php:596
#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:743
msgid "View Page"
msgstr ""

#: extensions/role-settings/class-role-settings-render.php:604
#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:751
msgid "Create separate pages on multisite network"
msgstr ""

#: extensions/role-settings/class-role-settings-render.php:614
#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:760
msgid "Create User settings page separately for every site"
msgstr ""

#: extensions/role-settings/class-role-settings-render.php:615
#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:761
msgid "When you enable this setting a page with the same slug is created on each site on the network, so the users of each sub site can use this page on their website to configure 2FA."
msgstr ""

#: extensions/role-settings/class-role-settings-render.php:621
#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:767
msgid "Specify the page where you want to redirect your users to after they complete the 2FA setup. This will override the global redirect setting."
msgstr ""

#: extensions/role-settings/class-role-settings-render.php:624
#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:770
msgid "Redirect users after 2FA setup"
msgstr ""

#: extensions/role-settings/class-role-settings-render.php:658
#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:932
msgid "Should users be allowed to disable 2FA from their user profile?"
msgstr ""

#: extensions/role-settings/class-role-settings-render.php:660
#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:934
msgid "Users can configure and also disable 2FA on their profile by clicking the \"Remove 2FA\" button. Enable this setting to disable the Remove 2FA button so users cannot disable 2FA from their user profile."
msgstr ""

#: extensions/role-settings/class-role-settings-render.php:665
#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:939
msgid "Hide the Remove 2FA button"
msgstr ""

#: extensions/role-settings/class-role-settings-render.php:674
#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:945
msgid "Hide the Remove 2FA button on user profile pages"
msgstr ""

#: extensions/settings-import-export/class-settings-import-export.php:132
msgid "From here you can export the plugin's settings configuration and also import them from an export file. Use the export file to keep a backup of the plugin's configuration or to import the same settings configuration to another website."
msgstr ""

#: extensions/settings-import-export/class-settings-import-export.php:140
msgid "Export settings"
msgstr ""

#: extensions/settings-import-export/class-settings-import-export.php:150
#: extensions/settings-import-export/class-settings-import-export.php:161
msgid "site-specific"
msgstr ""

#: extensions/settings-import-export/class-settings-import-export.php:151
msgid "Also export the site specific settings as enforced users and roles (the policy will be set to Do not enforce if left unchecked)"
msgstr ""

#: extensions/settings-import-export/class-settings-import-export.php:162
msgid "Add the custom from email to the export"
msgstr ""

#: extensions/settings-import-export/class-settings-import-export.php:167
msgid "Export"
msgstr ""

#: extensions/settings-import-export/class-settings-import-export.php:170
msgid "Once the settings are exported a download will automatically start. The settings are exported to a JSON file."
msgstr ""

#: extensions/settings-import-export/class-settings-import-export.php:177
msgid "Import settings"
msgstr ""

#: extensions/settings-import-export/class-settings-import-export.php:184
msgid "Validate & Import"
msgstr ""

#: extensions/settings-import-export/class-settings-import-export.php:186
msgid "Once you choose a JSON settings file, it will be checked prior to being imported to alert you of any issues, if there are any."
msgstr ""

#: extensions/settings-import-export/class-settings-import-export.php:213
#: extensions/settings-import-export/class-settings-import-export.php:240
#: includes/classes/Admin/class-plugin-updated-notice.php:123
msgid "Nonce Verification Failed."
msgstr ""

#: extensions/settings-import-export/class-settings-import-export.php:275
msgid "User not found: "
msgstr ""

#: extensions/settings-import-export/class-settings-import-export.php:285
msgid "Role not found: "
msgstr ""

#: extensions/settings-import-export/class-settings-import-export.php:295
msgid "Post type not found: "
msgstr ""

#: extensions/settings-import-export/class-settings-import-export.php:321
msgid "Setting updated"
msgstr ""

#: extensions/settings-import-export/class-settings-import-export.php:321
msgid "Setting created"
msgstr ""

#: extensions/settings-import-export/class-settings-import-export.php:388
msgid "Checking import contents"
msgstr ""

#: extensions/settings-import-export/class-settings-import-export.php:389
msgid "Ready to import"
msgstr ""

#: extensions/settings-import-export/class-settings-import-export.php:390
msgid "Issues found"
msgstr ""

#: extensions/settings-import-export/class-settings-import-export.php:391
msgid "Importing settings"
msgstr ""

#: extensions/settings-import-export/class-settings-import-export.php:392
msgid "Settings imported"
msgstr ""

#: extensions/settings-import-export/class-settings-import-export.php:393
#: includes/classes/Admin/class-help-contact-us.php:63
#: includes/classes/Admin/class-help-contact-us.php:71
msgid "Help"
msgstr ""

#: extensions/settings-import-export/class-settings-import-export.php:394
msgid "The role, user or post type contained in your settings are not currently found in this website. Importing such settings could lead to abnormal behavior. For more information and / or if you require assistance, please"
msgstr ""

#: extensions/settings-import-export/class-settings-import-export.php:395
msgid "Currently this data is not supported by our export/import wizard."
msgstr ""

#: extensions/settings-import-export/class-settings-import-export.php:396
msgid "To avoid accidental lock-out, this setting is not imported."
msgstr ""

#: extensions/settings-import-export/class-settings-import-export.php:397
msgid "Please upload a valid JSON file."
msgstr ""

#: extensions/settings-import-export/class-settings-import-export.php:399
msgid "The settings file has been tested and the configuration is ready to be imported. Would you like to proceed?"
msgstr ""

#: extensions/settings-import-export/class-settings-import-export.php:400
msgid "The configuration has been successfully imported. Click OK to close this window"
msgstr ""

#: extensions/settings-import-export/class-settings-import-export.php:401
msgid "Proceed"
msgstr ""

#: extensions/settings-import-export/class-settings-import-export.php:402
#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:204
msgid "OK"
msgstr ""

#: extensions/settings-import-export/class-settings-import-export.php:404
msgid "Contact Us"
msgstr ""

#: extensions/settings-import-export/class-settings-import-exporter.php:191
msgid "Settings access"
msgstr ""

#: extensions/settings-import-export/class-settings-import-exporter.php:241
msgid "Export/import settings"
msgstr ""

#: extensions/settings-import-export/class-settings-import-exporter.php:245
msgid "Use the settings below to import / export the plugins settings. If you have any questions send us an email at"
msgstr ""

#: extensions/status-filter/class-core.php:103
msgid "All 2FA statuses"
msgstr ""

#: extensions/status-filter/class-core.php:109
msgid "Filter"
msgstr ""

#. Translators: The number of days
#: extensions/trusted-devices/class-core.php:88
msgid "Remember this device for %d days"
msgstr ""

#: extensions/trusted-devices/class-settings.php:155
msgid "Allow users to have trusted devices so they are not asked for a 2FA code during login"
msgstr ""

#: extensions/trusted-devices/class-settings.php:157
msgid "When this feature is enabled, users can tick the option \"Remember this device\" in the login page so the plugin does not ask them for a 2FA code for a number of days."
msgstr ""

#: extensions/trusted-devices/class-settings.php:164
#: extensions/trusted-devices/class-settings.php:175
msgid "Allow the \"Remember this device\" user option"
msgstr ""

#: extensions/trusted-devices/class-settings.php:183
msgid "For how long should the plugin remember a device"
msgstr ""

#: extensions/trusted-devices/class-settings.php:191
msgid "Days"
msgstr ""

#: extensions/trusted-devices/class-settings.php:198
msgid "When should users be prompted for 2FA code on trusted devices?"
msgstr ""

#: extensions/trusted-devices/class-settings.php:200
msgid "When users add  a trusted device, a cookie is stored in the users' browsers. If the cookie is not detected, the plugin will prompt the users to enter a 2FA code to log in to the website. For additional security, you can also configure the plugin to prompt the user for a 2FA code when there is a cookie but the IP address of the device is different from the one that was used when the device was first remembered."
msgstr ""

#: extensions/trusted-devices/class-settings.php:205
msgid "Prompt user for 2FA code on trusted device"
msgstr ""

#: extensions/trusted-devices/class-settings.php:216
msgid "Only when cookie is not found"
msgstr ""

#: extensions/trusted-devices/class-settings.php:227
msgid "When cookie is not found or when the cookie is found but the IP address is different"
msgstr ""

#: extensions/twilio/class-twilio-api.php:70
#: extensions/twilio/class-twilio-api.php:94
msgid "Twilio is not set correctly"
msgstr ""

#: extensions/twilio/class-twilio-render.php:59
msgid "Twilio application / service integration settings"
msgstr ""

#. Translators: Twilio service documentation
#: extensions/twilio/class-twilio-render.php:64
msgid "Here you can specify your Twilio Production SID Key, Auth and number / ID or change an existing ones. You can get the them from the Twilio console. Refer to the article %s for more information and instructions on how to get your keys."
msgstr ""

#: extensions/twilio/class-twilio-render.php:65
msgid "how to get the Twilio Production SID key"
msgstr ""

#: extensions/twilio/class-twilio-render.php:72
#: extensions/twilio/class-twilio-wizard-steps.php:321
msgid "Account SID"
msgstr ""

#: extensions/twilio/class-twilio-render.php:80
#: extensions/twilio/class-twilio-wizard-steps.php:329
msgid "Auth token"
msgstr ""

#: extensions/twilio/class-twilio-render.php:88
#: extensions/twilio/class-twilio-wizard-steps.php:337
msgid "Twilio number or Alphanumeric ID"
msgstr ""

#: extensions/twilio/class-twilio-render.php:98
#: extensions/twilio/class-twilio-wizard-steps.php:347
msgid "Verify the Twilio keys"
msgstr ""

#: extensions/twilio/class-twilio-render.php:174
msgid "Unfortunately there is a problem with Twilio configuration. Contact the Administrator for further assistance."
msgstr ""

#: extensions/twilio/class-twilio-wizard-steps.php:288
msgid "One-time code via SMS (with Twilio)"
msgstr ""

#. Translators: Twilio service documentation
#: extensions/twilio/class-twilio-wizard-steps.php:298
msgid "Refer to the %s for more information on how to use the Twilio with WP 2FA for two-factor authentication on your website."
msgstr ""

#: extensions/twilio/class-twilio-wizard-steps.php:299
msgid "Twilio integration documentation"
msgstr ""

#: extensions/twilio/class-twilio.php:304
msgid "Provided Twilio settings are invalid"
msgstr ""

#: extensions/twilio/class-twilio.php:409
msgid "The Twilio connection has been configured successfully."
msgstr ""

#: extensions/twilio/class-twilio.php:410
msgid "Twilio SMS service configuration"
msgstr ""

#: extensions/twilio/class-twilio.php:652
msgid "One-time code via SMS (Twilio)"
msgstr ""

#: extensions/twilio/class-twilio.php:829
msgid "SMS Templates"
msgstr ""

#: extensions/twilio/class-twilio.php:830
#: extensions/twilio/class-twilio.php:836
msgid "SMS Registration text"
msgstr ""

#: extensions/twilio/class-twilio.php:831
msgid "This is the template for the SMS message sent during the 2FA setup for users to confirm their mobile number."
msgstr ""

#: extensions/twilio/class-twilio.php:838
#: extensions/twilio/class-twilio.php:873
#: extensions/white-labeling/class-white-labeling-render.php:1051
#: extensions/white-labeling/class-white-labeling-render.php:1072
#: extensions/white-labeling/class-white-labeling-render.php:1093
#: extensions/white-labeling/class-white-labeling-render.php:1124
#: extensions/white-labeling/class-white-labeling-render.php:1155
#: extensions/white-labeling/class-white-labeling-render.php:1186
#: extensions/white-labeling/class-white-labeling-render.php:1207
#: includes/classes/Admin/SettingsPages/class-settings-page-email.php:448
msgid "Available template tags:"
msgstr ""

#: extensions/twilio/class-twilio.php:865
msgid "SMS code text"
msgstr ""

#: extensions/twilio/class-twilio.php:866
msgid "This is the template for the SMS message with the one-time code sent to users when authenticating."
msgstr ""

#: extensions/twilio/class-twilio.php:871
msgid "SMS Code text"
msgstr ""

#: extensions/user-licensing/class-wp2fa-user-licensing-render.php:43
msgid "WP 2FA plugin user & website licensing information"
msgstr ""

#: extensions/user-licensing/class-wp2fa-user-licensing-render.php:47
msgid "Below is a list of websites on which this WP 2FA plugin license is activated, and how many users are using this license on these websites."
msgstr ""

#: extensions/user-licensing/class-wp2fa-user-licensing-render.php:99
msgid "Site"
msgstr ""

#: extensions/user-licensing/class-wp2fa-user-licensing-render.php:102
msgid "Users"
msgstr ""

#: extensions/user-licensing/class-wp2fa-user-licensing.php:65
msgid "User Licensing"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:74
#: includes/classes/Admin/SettingsPages/class-settings-page-white-label.php:308
msgid "2FA code page text"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:76
msgid "Use these settings to customize message shown to users upon login when a 2FA verification code is requested."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:81
msgid "Customize code page text"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:89
msgid "Show this generic message to all users regardless of the 2FA method they are using."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:97
msgid "Show a message that is specific to the method that the user is using."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:102
msgid "2FA via app"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:108
#: includes/classes/Admin/class-premium-features.php:268
msgid "2FA code over email"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:114
msgid "2FA link over email"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:120
msgid "2FA code over SMS"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:126
msgid "2FA with Push Notification - Intro"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:132
msgid "2FA with Push Notification - Awaiting Response"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:138
msgid "2FA with Push Notification"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:144
msgid "2FA with Yubico"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:171
msgid "Log in"
msgstr ""

#. translators: support email.
#: extensions/white-labeling/class-white-labeling-render.php:270
msgid "Use the settings below to customize the looks of the 2FA code page so it meets your branding requirements. If you have any questions send us an email at %1$s."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:276
msgid "Change the background color"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:278
msgid "This is the background color selector, from here you can change the form background color."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:283
msgid "2FA background color"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:292
msgid "Change the logo"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:294
msgid "This is the logo image selector, from here you can change the form logo."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:299
msgid "Logo on page"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:306
msgid "Select"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:325
msgid "Change the font type"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:327
msgid "This is the font type color selector, from here you can change the form font type."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:332
msgid "Font type"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:341
msgid "Change the button color"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:343
msgid "This is the button color selector, from here you can change the form button color."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:348
msgid "2FA button background color"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:357
msgid "Change the button text on 2FA code page"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:359
msgid "This is the button text, from here you can change the form button text."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:364
msgid "Button text"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:374
msgid "Change the styling of the code input area"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:376
msgid "By default, the user code entry screen uses the default WP CSS, use the below field to add your own CSS to this area."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:382
msgid "Login Custom CSS"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:389
msgid "- Text above code field"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:402
msgid "Disable Login CSS"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:408
msgid "Disable ALL WP 2FA styling from login page"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:447
#: extensions/white-labeling/class-white-labeling-render.php:577
msgid "Welcome & Initial Message"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:448
#: extensions/white-labeling/class-white-labeling-render.php:578
msgid "2FA Method Selection"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:449
#: extensions/white-labeling/class-white-labeling-render.php:579
msgid "2FA Method Verification"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:450
#: extensions/white-labeling/class-white-labeling-render.php:580
msgid "Backup Codes & Final Steps"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:451
#: extensions/white-labeling/class-white-labeling-render.php:581
msgid "2FA Method Reconfiguration"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:452
#: extensions/white-labeling/class-white-labeling-render.php:729
msgid "Custom CSS & Styling"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:524
msgid "Here you can control the look and feel of user areas as well as their content. Use the settings under 2FA Code page design to customize the styling of user wizards and log in messages. To edit the content of the wizard steps, select an area from the dropdown provided and use the text editors to set the content as you wish."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:533
msgid "2FA Code page design"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:542
msgid "User 2FA setup wizard"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:631
msgid "optional welcome & initial message"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:632
msgid "You can add an extra slide at the beginning of the user 2FA wizard. This slide will be shown to the users as the first slide when they launch the 2FA configuration wizard to configure 2FA for their users."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:636
msgid "Enable \"Welcome & initial message\""
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:642
msgid "Enable to display optional welcome message."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:648
msgid "optional welcome content"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:651
msgid "Use the below editor to enter the text that is to be used in the first slide."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:677
msgid "2FA Code page design settings"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:680
#: includes/classes/Admin/SettingsPages/class-settings-page-white-label.php:355
msgid "Change the styling of the user 2FA wizards"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:682
#: includes/classes/Admin/SettingsPages/class-settings-page-white-label.php:357
msgid "By default, the user 2FA wizards which the users see and use to set up 2FA have our own styling. Disable the below setting so the wizards use the styling of your website's theme."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:687
#: extensions/white-labeling/class-white-labeling-render.php:926
#: includes/classes/Admin/SettingsPages/class-settings-page-white-label.php:362
msgid "Enable styling"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:693
#: includes/classes/Admin/SettingsPages/class-settings-page-white-label.php:368
msgid "Enable our CSS within user wizards"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:700
msgid "Display logo in the user 2FA wizards"
msgstr ""

#. translators: Link to logo settings in the plugin.
#: extensions/white-labeling/class-white-labeling-render.php:705
msgid "Enable this setting to display your logo in the user 2FA wizards. To provide a logo, please use the Logo settings on the %s."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:716
msgid "Display logo"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:722
msgid "Display logo on user 2FA modals"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:730
msgid "In this section you can add your own styling to the user 2FA setup wizard via CSS. For your reference we have included a number of easy to use selectors. They are listed below the Custom CSS placeholder."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:731
msgid "Only plain text CSS is allowed. Use of \"!important\" is recommended."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:736
msgid "Custom CSS"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:743
#: extensions/white-labeling/class-white-labeling-render.php:744
msgid "- Primary button selector"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:745
msgid "- Common heading text (unless altered with custom markup)"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:746
msgid "- Common content text (unless altered with custom markup)"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:766
msgid "2FA method selection"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:767
msgid "Here you can customize the messages shown to users when selecting, settings up and reconfiguring 2FA."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:772
msgid "2FA required"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:775
msgid "This message is shown to users when logging in when 2FA is required."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:783
msgid "Inital setup text"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:786
msgid "This message is shown to users when configuring 2FA with no previous configuration."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:791
msgid "this displays the number of 2FA methods available."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:798
msgid "This message is shown to users when configuring 2FA with no previous configuration and one single method is available to them."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:807
msgid "Method selection label"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:808
msgid "Here you can customize the label shown for each method when choosing a 2FA method."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:814
msgid "Wizard option labels"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:817
msgid "TOTP (one-time code via app) Option label"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:823
msgid "TOTP option hint"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:832
msgid "Email option label"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:839
msgid "Out Of Band option label"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:845
msgid "Out Of Band option hint"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:854
msgid "Yubico option label"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:861
msgid "Authy option label"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:868
msgid "SMS over Twilio option label"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:875
msgid "Clickatell option label"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:886
msgid "Method help text"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:887
msgid "Here you can customize the help text shown when setting up a chosen method to suit your needs."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:893
msgid "One-time code via 2FA app help text"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:896
msgid "This message is shown to users when configuring one-time code via 2FA App"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:901
msgid "One-time code via 2FA app help step 1"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:907
msgid "One-time code via 2FA app help step 2"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:913
msgid "One-time code via 2FA app help step 3"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:932
msgid "Show the help text to assist users with the 2FA app configuration."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:943
msgid "One-time code via email help text"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:946
msgid "This message is shown to users when configuring One-time code via email."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:952
msgid "Email confirmation help"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:957
msgid "Emai address confirmation help."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:970
msgid "Authy 2FA service help text"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:973
msgid "This message is shown to users when configuring Authy 2FA service."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:986
msgid "SMS service help text (Twilio & Clickatell)"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:989
msgid "This messaged is shown to users when configuring 2FA over SMS via Twilio or Clickatell."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1002
msgid "YubiKey service help text"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1005
msgid "This messaged is shown to users when configuring 2FA via YubiKey."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1018
msgid "Link via email help text"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1021
msgid "This message is shown to users when configuring Link via email."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1042
msgid "2FA method reconfiguration"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1043
msgid "From here you can customize the wizard text shown to users when they already have 2FA configured and they want to reconfigure 2FA from their user profile page."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1048
msgid "Reconfigure one-time code via 2FA App intro"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1060
msgid "This message is shown to users when reconfiguring One-time code via 2FA app."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1069
msgid "Reconfigure one-time code via email intro"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1081
msgid "This message is shown to users when reconfiguring One-time code via email."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1090
msgid "Reconfigure Authy 2FA service intro"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1102
msgid "This message is shown to users when reconfiguring Authy 2FA service."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1112
msgid "This message is shown to users when reconfiguring Authy 2FA service, but Authy 2FA service service is unavailable."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1121
msgid "Reconfigure Twilio 2FA service intro"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1133
msgid "This message is shown to users when reconfiguring Twilio 2FA service."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1143
msgid "This message is shown to users when reconfiguring Twilio 2FA service, but SMS 2FA service is unavailable."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1152
msgid "Reconfigure Clickatell 2FA service intro"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1164
msgid "This message is shown to users when reconfiguring Clickatell 2FA service."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1174
msgid "This message is shown to users when reconfiguring Clickatell 2FA service, but SMS 2FA service is unavailable."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1183
msgid "Reconfigure Link via email intro"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1195
msgid "This message is shown to users when reconfiguring Link via email."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1204
msgid "Reconfigure 2FA with YubiKey intro"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1216
msgid "This message is shown to users when configuring or reconfiguring 2FA with YubiKey."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1226
msgid "This message is shown to users when reconfiguring YubiKey 2FA service, but SMS 2FA service is unavailable."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1248
msgid "2FA method verification"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1249
msgid "Here you can customize the messages shown to users when during the submission and verification of codes during setup."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1254
msgid "One-time code via 2FA app pre-submission text"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1257
msgid "This message is shown to users prior to configuring one-time code via 2FA app method."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1270
msgid "One-time code via email pre-submission text"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1273
msgid "This message is shown to users prior to configuring one-time code via email method."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1286
msgid "Link via email pre-submission text"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1289
msgid "This message is shown to users prior to configuring Link via email method."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1302
msgid "Authy 2FA service pre-submission text"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1310
msgid "This message is shown to users prior to configuring Authy 2FA service method."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1323
msgid "2FA over SMS service pre-submission text (Twilio & Clickatell)"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1326
msgid "This message is shown to users prior to configuring 2FA over SMS service method."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1339
msgid "YubiKey 2FA service pre-submission text"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1342
msgid "This message is shown to users prior to configuring 2FA via YubiKey."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1363
msgid "Backup 2FA methods & final steps"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1364
msgid "Here you can customize the messages shown to users once setup is complete."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1370
msgid "No further action"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1373
msgid "This message is shown to users when 2FA has been configured and no further actions are available."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1386
msgid "Choose backup method"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1389
msgid "This message is shown to users when more than one backup method is available."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1398
msgid "This message is shown to users when once setup is complete and backup codes are not available."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1407
msgid "This message is shown to users when once setup is complete and backup codes are optional."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1416
msgid "This message is shown to users when only email 2FA backup is available."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1429
msgid "Back codes generation intro"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1432
msgid "This message is shown to users prior to generatrion of backup codes."
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1441
msgid "Back codes generated"
msgstr ""

#: extensions/white-labeling/class-white-labeling-render.php:1444
msgid "This message is shown to users when backup codes have been generated."
msgstr ""

#: extensions/yubico/class-yubico-render.php:57
msgid "Yubico application / service integration settings"
msgstr ""

#. Translators: Yubico service documentation
#: extensions/yubico/class-yubico-render.php:62
msgid "Here you can specify your Yubico Client ID Key, Secret Key and API Endpoint or change an existing ones. You can sign up for the Client ID and Secret Key %1$s. Refer to the article %2$s for more information and instructions on how to get your keys."
msgstr ""

#: extensions/yubico/class-yubico-render.php:63
msgid "here"
msgstr ""

#: extensions/yubico/class-yubico-render.php:64
msgid "how to implement Yubico integration"
msgstr ""

#: extensions/yubico/class-yubico-render.php:71
#: extensions/yubico/class-yubico-wizard-steps.php:321
msgid "Client ID"
msgstr ""

#: extensions/yubico/class-yubico-render.php:79
#: extensions/yubico/class-yubico-wizard-steps.php:329
msgid "Secret Key"
msgstr ""

#: extensions/yubico/class-yubico-render.php:87
#: extensions/yubico/class-yubico-wizard-steps.php:337
msgid "API Endpoint"
msgstr ""

#: extensions/yubico/class-yubico-render.php:91
#: extensions/yubico/class-yubico-wizard-steps.php:341
msgid "Leave blank to use YubiCloud servers."
msgstr ""

#: extensions/yubico/class-yubico-render.php:149
msgid "Enter the OTP from your YubiKey:"
msgstr ""

#: extensions/yubico/class-yubico-render.php:156
msgid "Unfortunately there is a problem with Yubico configuration. Contact the Administrator for further assistance."
msgstr ""

#: extensions/yubico/class-yubico-user.php:139
msgid "invalid key provided"
msgstr ""

#: extensions/yubico/class-yubico-wizard-steps.php:117
msgid "Change code"
msgstr ""

#: extensions/yubico/class-yubico-wizard-steps.php:184
msgid "YubiKey OTP:"
msgstr ""

#: extensions/yubico/class-yubico-wizard-steps.php:201
msgid "YubiKey confirmation code:"
msgstr ""

#: extensions/yubico/class-yubico-wizard-steps.php:286
msgid "One-time password via hardware key (with YubiKey)"
msgstr ""

#. Translators: Yubico service documentation
#: extensions/yubico/class-yubico-wizard-steps.php:296
msgid "Refer to the %s for more information on how to use the YubiKey with WP 2FA for two-factor authentication on your website."
msgstr ""

#: extensions/yubico/class-yubico-wizard-steps.php:297
msgid "Yubico integration documentation"
msgstr ""

#: extensions/yubico/class-yubico-wizard-steps.php:348
msgid "Verify the Yubico configuration"
msgstr ""

#: extensions/yubico/class-yubico.php:252
msgid "Provided Yubico settings are invalid"
msgstr ""

#: extensions/yubico/class-yubico.php:343
msgid "The Yubico connection has been configured successfully."
msgstr ""

#: extensions/yubico/class-yubico.php:344
msgid "Yubico OTP configuration"
msgstr ""

#: extensions/yubico/class-yubico.php:524
msgid "One-time password via YubiKey"
msgstr ""

#: includes/classes/Admin/class-help-contact-us.php:34
#: includes/classes/Admin/class-help-contact-us.php:35
msgid "Help & Contact Us"
msgstr ""

#: includes/classes/Admin/class-help-contact-us.php:72
msgid "System info"
msgstr ""

#: includes/classes/Admin/class-help-contact-us.php:99
#: includes/classes/Admin/class-help-contact-us.php:109
msgid "Getting started"
msgstr ""

#: includes/classes/Admin/class-help-contact-us.php:101
msgid "Getting started with WP 2FA and making 2FA compulsory is as easy as 1 2 3 with WP 2FA. This can be easily done through the install wizard or the plugin settings. If you are stuck, no problem! Below are a few links of guides to help you get started:"
msgstr ""

#: includes/classes/Admin/class-help-contact-us.php:103
msgid "Getting started with WP 2FA"
msgstr ""

#: includes/classes/Admin/class-help-contact-us.php:104
msgid "Configuring 2FA policies & making 2FA mandatory"
msgstr ""

#: includes/classes/Admin/class-help-contact-us.php:105
msgid "Allowing users to configure 2FA from a website page (no dashboard access)"
msgstr ""

#: includes/classes/Admin/class-help-contact-us.php:113
msgid "Plugin documentation"
msgstr ""

#: includes/classes/Admin/class-help-contact-us.php:115
msgid "For more technical information about the WP 2FA plugin please visit the plugin's knowledge base."
msgstr ""

#: includes/classes/Admin/class-help-contact-us.php:117
msgid "Knowledge base"
msgstr ""

#: includes/classes/Admin/class-help-contact-us.php:123
msgid "Plugin support"
msgstr ""

#: includes/classes/Admin/class-help-contact-us.php:125
msgid "Do you need assistance with the plugin? Have you noticed or encountered an issue while using WP 2FA, or do you just want to report something to us?"
msgstr ""

#: includes/classes/Admin/class-help-contact-us.php:127
msgid "Open support ticket"
msgstr ""

#: includes/classes/Admin/class-help-contact-us.php:128
msgid "Contact us"
msgstr ""

#: includes/classes/Admin/class-help-contact-us.php:145
msgid "System information"
msgstr ""

#: includes/classes/Admin/class-help-contact-us.php:192
msgid "Our WordPress Plugins"
msgstr ""

#: includes/classes/Admin/class-help-contact-us.php:200
msgid "Keep a log of users and under the hood site activity."
msgstr ""

#: includes/classes/Admin/class-help-contact-us.php:216
#: includes/classes/Admin/class-help-contact-us.php:243
#: includes/classes/Admin/class-help-contact-us.php:270
#: includes/classes/Admin/class-help-contact-us.php:297
msgid "LEARN MORE"
msgstr ""

#: includes/classes/Admin/class-help-contact-us.php:227
msgid "Enforce strong password policies on WordPress."
msgstr ""

#: includes/classes/Admin/class-help-contact-us.php:254
msgid "Automatically identify unauthorized file changes on your WordPress site."
msgstr ""

#: includes/classes/Admin/class-help-contact-us.php:281
msgid "Protect website forms & login pages from spam bots & automated attacks."
msgstr ""

#. translators: %s: version number.
#: includes/classes/Admin/class-plugin-updated-notice.php:51
msgid "Thank you for updating WP 2FA."
msgstr ""

#. translators: %s: version number.
#: includes/classes/Admin/class-plugin-updated-notice.php:51
msgid "This is version %s. Check out the release notes to see what is new and improved in this update."
msgstr ""

#. translators: %s: version number.
#: includes/classes/Admin/class-plugin-updated-notice.php:51
msgid "Release notes"
msgstr ""

#: includes/classes/Admin/class-plugin-updated-notice.php:128
msgid "Complete."
msgstr ""

#: includes/classes/Admin/class-premium-features.php:41
msgid "Premium Features"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:42
msgid "Premium Features ➤"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:60
msgid "Upgrade to Premium & benefit:"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:61
msgid "Login with 2FA via SMS, push notification or with a simple mouse click"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:62
msgid "Add & manage trusted devices (\"Remember this device\" option)"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:63
msgid "Add alternative 2FA methods ensuring no user is ever locked out"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:64
msgid "One-click 2FA integration with WooCommerce"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:65
msgid "Completely whitelabel the 2FA user experience including the 2FA code page, email & wizards text"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:66
msgid "Configure different 2FA policies for different user roles"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:67
msgid "Many other features"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:68
#: includes/classes/Admin/class-premium-features.php:423
msgid "No Ads!"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:69
#: includes/classes/Admin/class-premium-features.php:214
#: includes/classes/Admin/class-premium-features.php:436
#: includes/classes/Admin/class-settings-page.php:212
msgid "Upgrade to Premium"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:194
msgid "Upgrade to Premium and benefit more!"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:201
msgid "WP 2FA is your trusted gatekeeper, keeping your website, users, customers, team members, and anyone who accesses your website, including you, secure and better protected than ever before."
msgstr ""

#: includes/classes/Admin/class-premium-features.php:202
msgid "Upgrade to WP 2FA Premium to add more secure authentication options and automate more, encouraging all your website users to utilize 2FA to its fullest extent and give your users more flexibility by allowing them to work from anywhere without compromising on security."
msgstr ""

#: includes/classes/Admin/class-premium-features.php:206
msgid "Upgrade to Premium and start benefiting from value-added features such as:"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:208
msgid "More 2FA methods, including SMS, push notifications & one-click login"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:209
msgid "Trusted devices: Allow users to add trusted devices so they do not have to manually enter the 2FA code each time they log in"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:210
msgid "White labeling features: Gain increased trust by extending your business’ branding and tone of voice to all 2FA pages, wizards & emails"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:211
msgid "Refer to the features matrix below for a detailed list of all the premium features"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:218
msgid "WP 2FA plugin features"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:219
msgid "Take advantage of these benefits and many others, with prices starting from as little as $29 for 5 users per year. "
msgstr ""

#: includes/classes/Admin/class-premium-features.php:227
msgid "Premium"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:230
msgid "Free"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:235
msgid "Support"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:238
msgid "1-to-1 emails, forums"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:241
msgid "forums"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:246
msgid "Out of the box support for e-commerce, membership & third party plugins (no code required)"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:257
msgid "2FA code via mobile app"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:279
msgid "2FA login with hardware key (YubiKey)"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:290
msgid "2FA login with push notification (Authy)"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:301
msgid "2FA Login with SMS (with Twilio or Clickatell)"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:313
msgid "One-click 2FA login (via link in email)"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:324
msgid "Different 2FA policies per user role"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:335
msgid "Trusted devices (remember devices)"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:346
msgid "Alternative 2FA methods"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:352
msgid "Backup codes only"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:357
msgid "White labeling (logo, wizards, email, colours, fonts & custom CSS)"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:368
msgid "One-click 2FA integration in WooCommerce user page"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:379
msgid "Reports & Statistics"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:390
msgid "Configurable 2FA code expiration time"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:401
msgid "Sortable users' 2FA status"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:412
msgid "Export/import plugin settings"
msgstr ""

#. translators: 1: Link to our site 2: Link to our contact page
#: includes/classes/Admin/class-premium-features.php:445
msgid "Visit the WP 2FA %1$s for more information or %2$s  with any questions you might have. We look forward to hearing from you."
msgstr ""

#: includes/classes/Admin/class-premium-features.php:446
msgid "plugin website"
msgstr ""

#: includes/classes/Admin/class-premium-features.php:447
#: includes/classes/Admin/SettingsPages/class-settings-page-email.php:140
msgid "contact us"
msgstr ""

#: includes/classes/Admin/class-settings-page.php:40
#: includes/classes/Admin/class-settings-page.php:41
#: includes/classes/Admin/class-settings-page.php:116
#: includes/classes/Admin/class-setup-wizard.php:365
msgid "WP 2FA"
msgstr ""

#: includes/classes/Admin/class-settings-page.php:51
#: includes/classes/Admin/class-settings-page.php:52
#: includes/classes/Admin/class-settings-page.php:126
#: includes/classes/Admin/class-settings-page.php:127
msgid "2FA Policies"
msgstr ""

#: includes/classes/Admin/class-settings-page.php:61
#: includes/classes/Admin/class-settings-page.php:115
#: includes/classes/Admin/class-settings-page.php:136
#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:96
#: includes/classes/Admin/SettingsPages/class-settings-page-render.php:42
msgid "WP 2FA Settings"
msgstr ""

#: includes/classes/Admin/class-settings-page.php:222
msgid "Configure 2FA Settings"
msgstr ""

#: includes/classes/Admin/class-settings-page.php:267
#: includes/classes/Admin/class-settings-page.php:346
msgid "2FA Settings Updated"
msgstr ""

#: includes/classes/Admin/class-settings-page.php:269
#: includes/classes/Admin/class-settings-page.php:283
#: includes/classes/Admin/class-settings-page.php:290
#: includes/classes/Admin/class-settings-page.php:313
#: includes/classes/Admin/class-settings-page.php:338
#: includes/classes/Admin/class-settings-page.php:348
#: includes/classes/Admin/class-settings-page.php:359
#: includes/classes/Admin/class-settings-page.php:369
#: includes/classes/Admin/Helpers/class-ajax-helper.php:378
#: includes/classes/Admin/Helpers/class-ajax-helper.php:394
#: includes/classes/Admin/Helpers/class-ajax-helper.php:410
msgid "Dismiss this notice."
msgstr ""

#: includes/classes/Admin/class-settings-page.php:288
#: includes/classes/Admin/class-settings-page.php:357
msgid "Please ensure both custom email address and display name are provided."
msgstr ""

#: includes/classes/Admin/class-settings-page.php:388
msgid "WP 2FA User Page"
msgstr ""

#: includes/classes/Admin/class-settings-page.php:505
msgid "By default, the plugin uses "
msgstr ""

#: includes/classes/Admin/class-settings-page.php:505
msgid "as the \"from address\" when sending emails with the 2FA code for users to log in. Do you want to keep using this or change it?"
msgstr ""

#: includes/classes/Admin/class-settings-page.php:507
msgid "Change it"
msgstr ""

#: includes/classes/Admin/class-settings-page.php:509
msgid "Keep using it"
msgstr ""

#: includes/classes/Admin/class-setup-wizard.php:127
msgid "Welcome"
msgstr ""

#: includes/classes/Admin/class-setup-wizard.php:132
msgid "Configure 2FA methods & Policies"
msgstr ""

#: includes/classes/Admin/class-setup-wizard.php:138
msgid "Setup Finish"
msgstr ""

#: includes/classes/Admin/class-setup-wizard.php:255
#: includes/classes/Shortcodes/class-shortcodes.php:59
#: includes/classes/Shortcodes/class-shortcodes.php:73
#: includes/functions/core.php:242
msgid "Please use a valid email address"
msgstr ""

#: includes/classes/Admin/class-setup-wizard.php:256
#: includes/classes/Shortcodes/class-shortcodes.php:74
#: includes/functions/core.php:257
msgid "Backup codes sent"
msgstr ""

#: includes/classes/Admin/class-setup-wizard.php:266
#: includes/classes/Shortcodes/class-shortcodes.php:53
#: includes/classes/Shortcodes/class-shortcodes.php:70
#: includes/classes/Shortcodes/class-shortcodes.php:189
#: includes/functions/core.php:254
msgid "These are the 2FA backup codes for the user"
msgstr ""

#: includes/classes/Admin/class-setup-wizard.php:267
#: includes/classes/Shortcodes/class-shortcodes.php:54
#: includes/classes/Shortcodes/class-shortcodes.php:71
#: includes/classes/Shortcodes/class-shortcodes.php:190
#: includes/functions/core.php:255
msgid "I'm ready"
msgstr ""

#: includes/classes/Admin/class-setup-wizard.php:268
#: includes/classes/Shortcodes/class-shortcodes.php:55
#: includes/classes/Shortcodes/class-shortcodes.php:72
#: includes/classes/Shortcodes/class-shortcodes.php:191
#: includes/functions/core.php:256
msgid "New code sent"
msgstr ""

#: includes/classes/Admin/class-setup-wizard.php:311
msgid "WP 2FA &rsaquo; Setup Wizard"
msgstr ""

#: includes/classes/Admin/class-setup-wizard.php:351
#: includes/classes/Shortcodes/class-shortcodes.php:58
#: includes/classes/Shortcodes/class-shortcodes.php:194
msgid "Close Wizard"
msgstr ""

#: includes/classes/Admin/class-setup-wizard.php:365
msgid "If you cancel this wizard, the default plugin settings will be applied. You can always configure the plugin settings and two-factor authentication policies at a later stage from the "
msgstr ""

#: includes/classes/Admin/class-setup-wizard.php:365
msgid " entry in your WordPress dashboard menu."
msgstr ""

#: includes/classes/Admin/class-setup-wizard.php:367
msgid "OK, close wizard"
msgstr ""

#: includes/classes/Admin/class-setup-wizard.php:368
msgid "Continue with wizard"
msgstr ""

#: includes/classes/Admin/class-setup-wizard.php:487
msgid "2FA methods"
msgstr ""

#: includes/classes/Admin/class-setup-wizard.php:490
#: includes/classes/Admin/class-setup-wizard.php:496
#: includes/classes/Admin/class-setup-wizard.php:502
#: includes/classes/Admin/class-setup-wizard.php:509
#: includes/classes/Admin/class-setup-wizard.php:517
msgid "Continue Setup"
msgstr ""

#: includes/classes/Admin/class-setup-wizard.php:493
msgid "Alternative methods"
msgstr ""

#: includes/classes/Admin/class-setup-wizard.php:499
msgid "2FA policy"
msgstr ""

#: includes/classes/Admin/class-setup-wizard.php:503
#: includes/classes/Admin/class-setup-wizard.php:527
msgid "All done"
msgstr ""

#: includes/classes/Admin/class-setup-wizard.php:506
msgid "Exclude users"
msgstr ""

#: includes/classes/Admin/class-setup-wizard.php:514
msgid "Exclude sites"
msgstr ""

#: includes/classes/Admin/class-setup-wizard.php:523
msgid "How long should the grace period for your users be?"
msgstr ""

#: includes/classes/Admin/class-setup-wizard.php:524
msgid "When you configure the 2FA policies and require users to configure 2FA, they can either have a grace period to configure 2FA, or can be required to configure 2FA before the next time they login. Choose which method you'd like to use:"
msgstr ""

#: includes/classes/Admin/class-setup-wizard.php:632
msgid "Email sending failed"
msgstr ""

#: includes/classes/Admin/class-setup-wizard.php:654
msgid "Welcome to WP 2FA"
msgstr ""

#: includes/classes/Admin/class-user-listing.php:62
msgid "2FA Status"
msgstr ""

#: includes/classes/Admin/class-user-listing.php:139
#: includes/classes/Admin/class-user-profile.php:140
msgid "Remove 2FA"
msgstr ""

#: includes/classes/Admin/class-user-listing.php:140
#: includes/classes/Admin/class-user-listing.php:203
msgid "Reset list of 2FA trusted devices"
msgstr ""

#. translators: The number of the affected users.
#: includes/classes/Admin/class-user-listing.php:221
msgid "Removed 2FA from %d users."
msgstr ""

#. translators: The number of the affected users.
#: includes/classes/Admin/class-user-listing.php:231
msgid "Removed 2FA trusted devices from %d users."
msgstr ""

#: includes/classes/Admin/class-user-notices.php:140
#: includes/classes/Admin/class-user-notices.php:161
#: includes/classes/Admin/Views/class-wizard-steps.php:359
#: includes/classes/Authenticator/class-login.php:290
msgid "Configure 2FA now"
msgstr ""

#: includes/classes/Admin/class-user-notices.php:141
msgid "Remind me on next login"
msgstr ""

#: includes/classes/Admin/class-user-notices.php:158
msgid "The 2FA method you were using is no longer allowed on this website. Please reconfigure 2FA using one of the supported methods."
msgstr ""

#: includes/classes/Admin/class-user-notices.php:162
#: includes/classes/Authenticator/class-login.php:291
msgid "I'll do it later"
msgstr ""

#: includes/classes/Admin/class-user-notices.php:196
msgid "2FA mandatory notice"
msgstr ""

#: includes/classes/Admin/class-user-notices.php:205
msgid "2FA reconfiguration mandatory notice"
msgstr ""

#: includes/classes/Admin/class-user-notices.php:214
msgid "User profile 2FA configuration area title"
msgstr ""

#: includes/classes/Admin/class-user-notices.php:223
msgid "User profile 2FA configuration area description"
msgstr ""

#: includes/classes/Admin/class-user-profile.php:92
msgid "This user is required to setup 2FA but has not yet done so."
msgstr ""

#: includes/classes/Admin/class-user-profile.php:96
msgid "This user is excluded from configuring 2FA."
msgstr ""

#: includes/classes/Admin/class-user-profile.php:136
msgid "Change 2FA settings"
msgstr ""

#: includes/classes/Admin/class-user-profile.php:148
msgid "unused backup codes remaining."
msgstr ""

#: includes/classes/Admin/class-user-profile.php:150
msgid "Learn more about backup codes"
msgstr ""

#: includes/classes/Admin/class-user-profile.php:211
msgid "Configure 2FA"
msgstr ""

#: includes/classes/Admin/class-user-profile.php:215
msgid "Configure Two-factor authentication (2FA)"
msgstr ""

#: includes/classes/Admin/class-user-profile.php:223
msgid "The user has already configured 2FA. When you reset the user's current 2FA configuration, the user can log back in with just the username and password."
msgstr ""

#: includes/classes/Admin/class-user-profile.php:235
msgid "Reset 2FA configuration"
msgstr ""

#: includes/classes/Admin/class-user-profile.php:248
msgid "Unlock user and reset the grace period"
msgstr ""

#: includes/classes/Admin/class-user-profile.php:268
msgid "No enabled primary method"
msgstr ""

#: includes/classes/Admin/class-user-profile.php:270
msgid "No enabled backup methods"
msgstr ""

#: includes/classes/Admin/class-user-profile.php:286
msgid "Currently configured:"
msgstr ""

#: includes/classes/Admin/class-user-profile.php:292
msgid "Primary method:"
msgstr ""

#: includes/classes/Admin/class-user-profile.php:300
msgid "Secondary method(s):"
msgstr ""

#: includes/classes/Admin/class-user-profile.php:311
msgid "2FA configuration:"
msgstr ""

#: includes/classes/Admin/class-user-profile.php:321
msgid "Show QR code"
msgstr ""

#: includes/classes/Admin/class-user-profile.php:327
#: includes/classes/Admin/Methods/class-totp-wizard-steps.php:236
msgid "COPY"
msgstr ""

#: includes/classes/Admin/class-user-profile.php:340
msgid "2FA Setup:"
msgstr ""

#: includes/classes/Admin/class-user-profile.php:389
msgid "Are you sure?"
msgstr ""

#: includes/classes/Admin/class-user-profile.php:390
msgid "Any unsaved changes will be lost!"
msgstr ""

#: includes/classes/Admin/class-user-profile.php:422
msgid "No available 2FA methods set"
msgstr ""

#: includes/classes/Admin/class-user-profile.php:422
msgid "Ask your administrator to enable 2FA methods"
msgstr ""

#: includes/classes/Admin/class-user-profile.php:446
#: includes/classes/Admin/Views/class-wizard-steps.php:63
msgid "Next Step"
msgstr ""

#: includes/classes/Admin/class-user-profile.php:533
msgid "Remove 2FA?"
msgstr ""

#: includes/classes/Admin/class-user-profile.php:534
msgid "Are you sure you want to remove two-factor authentication and lower the security of your user account?"
msgstr ""

#: includes/classes/Admin/class-user-profile.php:616
msgid "Unlock user"
msgstr ""

#: includes/classes/Admin/class-user-profile.php:751
msgid "Invalid Two Factor Authentication secret key."
msgstr ""

#: includes/classes/Admin/class-user-profile.php:757
msgid "Invalid Email Authentication code."
msgstr ""

#: includes/classes/Admin/class-user-profile.php:778
msgid "Error processing form"
msgstr ""

#: includes/classes/Admin/Controllers/class-methods.php:104
#: includes/classes/Utils/class-white-label.php:118
msgid "There are {available_methods_count} methods available to choose from for 2FA:"
msgstr ""

#: includes/classes/Admin/Helpers/class-ajax-helper.php:193
msgid "Unable to write to wp-config.php"
msgstr ""

#: includes/classes/Admin/Helpers/class-ajax-helper.php:206
msgid "wp-config.php successfully update, global setting deleted"
msgstr ""

#: includes/classes/Admin/Helpers/class-ajax-helper.php:215
msgid "Unable to find global secret key"
msgstr ""

#: includes/classes/Admin/Helpers/class-ajax-helper.php:334
msgid "Test email from WP 2FA"
msgstr ""

#: includes/classes/Admin/Helpers/class-ajax-helper.php:335
msgid "This email was sent by the WP 2FA plugin to test the email delivery."
msgstr ""

#: includes/classes/Admin/Helpers/class-ajax-helper.php:376
msgid "Your 2FA settings have been removed."
msgstr ""

#: includes/classes/Admin/Helpers/class-ajax-helper.php:392
msgid "User 2FA settings have been removed."
msgstr ""

#: includes/classes/Admin/Helpers/class-ajax-helper.php:408
msgid "User account successfully unlocked. User can login again."
msgstr ""

#: includes/classes/Admin/Helpers/class-file-writer.php:428
msgid "The base of WSAL working directory cannot be determined. Custom path is invalid or there is some other issue with your WordPress installation."
msgstr ""

#. translators: %s: Directory path.
#: includes/classes/Admin/Helpers/class-file-writer.php:451
msgid "Unable to create directory %s. Is its parent directory writable by the server?"
msgstr ""

#. translators: the name of the file.
#: includes/classes/Admin/Helpers/class-file-writer.php:482
msgid "The file %s could not be removed as the unlink() function is disabled. This is a system configuration issue."
msgstr ""

#. translators: the name of the file.
#: includes/classes/Admin/Helpers/class-file-writer.php:498
msgid "Unable to remove %s due to an unknown error."
msgstr ""

#. translators: %s: count
#: includes/classes/Admin/Methods/class-backup-codes.php:266
msgid "%s unused code remaining."
msgid_plural "%s unused codes remaining."
msgstr[0] ""
msgstr[1] ""

#. translators: %s: the site's domain
#: includes/classes/Admin/Methods/class-backup-codes.php:271
msgid "Two-Factor Backup Codes for %s"
msgstr ""

#. translators: URL with more information about the backup codes
#: includes/classes/Admin/Methods/class-backup-codes.php:363
msgid "Login with a backup code: you will get 10 backup codes and you can use one of them when you need to login and you cannot generate a code from the app. %s"
msgstr ""

#: includes/classes/Admin/Methods/class-backup-codes.php:364
msgid "More information."
msgstr ""

#: includes/classes/Admin/Methods/class-email-wizard-steps.php:183
msgid "One-time code via email (HOTP)"
msgstr ""

#: includes/classes/Admin/Methods/class-email-wizard-steps.php:184
msgid " - ensure email deliverability with the free plugin "
msgstr ""

#: includes/classes/Admin/Methods/class-email-wizard-steps.php:190
msgid "When using this method, users will receive the one-time login code over email. Therefore, email deliverability is very important. Users using this method should whitelist the address from which the codes are sent. By default, this is the email address configured in your WordPress. You can run an email test from the plugin's settings to confirm email deliverability. If you have had email deliverability / reliability issues, we highly recommend you to install the free plugin "
msgstr ""

#: includes/classes/Admin/Methods/class-email-wizard-steps.php:190
msgid "Allowing users to set up a secondary 2FA method is highly recommended. You can do this in the next step of the wizard. This will allow users to log in using an alternative method should they, for example lose access to their phone."
msgstr ""

#: includes/classes/Admin/Methods/class-email.php:110
msgid "HOTP (Email)"
msgstr ""

#: includes/classes/Admin/Methods/class-email.php:142
msgid "Setting up HOTP (one-time code via email)"
msgstr ""

#: includes/classes/Admin/Methods/class-email.php:142
msgid "Please select the email address where the one-time code should be sent:"
msgstr ""

#: includes/classes/Admin/Methods/class-email.php:143
msgid "To complete the 2FA configuration you will be sent a one-time code over email, therefore you should have access to the mailbox of this email address. If you do not receive the email with the one-time code please check your spam folder and contact your administrator"
msgstr ""

#: includes/classes/Admin/Methods/class-email.php:144
msgid "IMPORTANT"
msgstr ""

#: includes/classes/Admin/Methods/class-email.php:144
msgid "To ensure you always receive the one-time code whitelist the email address from which the codes are sent. This is {from_email}"
msgstr ""

#: includes/classes/Admin/Methods/class-email.php:145
#: includes/classes/Admin/Methods/class-totp.php:332
#: includes/classes/class-wp2fa.php:139
#: includes/classes/class-wp2fa.php:140
#: includes/classes/class-wp2fa.php:141
#: includes/classes/class-wp2fa.php:142
#: includes/classes/class-wp2fa.php:143
#: includes/classes/Utils/class-white-label.php:128
#: includes/classes/Utils/class-white-label.php:129
#: includes/classes/Utils/class-white-label.php:130
#: includes/classes/Utils/class-white-label.php:131
#: includes/classes/Utils/class-white-label.php:132
msgid "Almost there…"
msgstr ""

#: includes/classes/Admin/Methods/class-email.php:145
msgid "Please type in the one-time code sent to your email address to finalize the setup"
msgstr ""

#: includes/classes/Admin/Methods/class-email.php:146
msgid "{reconfigure_or_configure_capitalized} one-time code over email method"
msgstr ""

#: includes/classes/Admin/Methods/class-email.php:146
msgid "Click the below button to {reconfigure_or_configure} the email address where the one-time code should be sent."
msgstr ""

#: includes/classes/Admin/Methods/class-email.php:147
msgid "One-time code via email"
msgstr ""

#: includes/classes/Admin/Methods/class-totp-wizard-steps.php:97
msgid "Reset Key"
msgstr ""

#: includes/classes/Admin/Methods/class-totp-wizard-steps.php:249
msgid "Click on the icon of the app that you are using for a detailed guide on how to set it up."
msgstr ""

#: includes/classes/Admin/Methods/class-totp-wizard-steps.php:330
msgid "One-time code via 2FA App (TOTP) - "
msgstr ""

#: includes/classes/Admin/Methods/class-totp-wizard-steps.php:330
msgid "complete list of supported 2FA apps."
msgstr ""

#. translators: link to the knowledge base website
#: includes/classes/Admin/Methods/class-totp-wizard-steps.php:337
msgid "When using this method, users will need to configure a 2FA app to get the one-time login code. The plugin supports all standard 2FA apps. Refer to the %s for more information. Allowing users to set up a secondary 2FA method is highly recommended. You can do this in the next step of the wizard. This will allow users to log in using an alternative method should they, for example lose access to their phone."
msgstr ""

#: includes/classes/Admin/Methods/class-totp-wizard-steps.php:338
#: includes/classes/Admin/Methods/class-totp-wizard-steps.php:347
#: includes/classes/Admin/Methods/class-totp.php:338
msgid "guide on how to set up 2FA apps"
msgstr ""

#. translators: link to the knowledge base website
#: includes/classes/Admin/Methods/class-totp-wizard-steps.php:346
#: includes/classes/Admin/Methods/class-totp.php:337
msgid "Refer to the %s for more information on how to setup these apps and which apps are supported."
msgstr ""

#: includes/classes/Admin/Methods/class-totp-wizard-steps.php:381
msgid "Authentication Code:"
msgstr ""

#: includes/classes/Admin/Methods/class-totp.php:130
msgid "TOTP (one-time code via app)"
msgstr ""

#: includes/classes/Admin/Methods/class-totp.php:328
msgid "Setting up TOTP (one-time code via app)"
msgstr ""

#: includes/classes/Admin/Methods/class-totp.php:329
msgid "Download and start the application of your choice"
msgstr ""

#: includes/classes/Admin/Methods/class-totp.php:330
msgid "From within the application scan the QR code provided on the left. Otherwise, enter the following code manually in the application:"
msgstr ""

#: includes/classes/Admin/Methods/class-totp.php:331
msgid "Click the \"I'm ready\" button below when you complete the application setup process to proceed with the wizard."
msgstr ""

#: includes/classes/Admin/Methods/class-totp.php:332
msgid "Please type in the one-time code from your chosen authentication app to finalize the setup."
msgstr ""

#: includes/classes/Admin/Methods/class-totp.php:333
msgid "{reconfigure_or_configure_capitalized} the 2FA App"
msgstr ""

#: includes/classes/Admin/Methods/class-totp.php:333
msgid "Click the below button to {reconfigure_or_configure} the current 2FA method. Note that once reset you will have to re-scan the QR code on all devices you want this to work on because the previous codes will stop working."
msgstr ""

#: includes/classes/Admin/Methods/class-totp.php:334
msgid "One-time code via 2FA app"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-email.php:45
msgid "Save email settings and templates"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-email.php:102
msgid "Which email address should the plugin use as a from address?"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-email.php:104
msgid "Use these settings to customize the \"from\" name and email address for all correspondence sent from our plugin."
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-email.php:109
msgid "From email & name"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-email.php:117
msgid "Use the email address "
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-email.php:124
msgid "Use another email address"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-email.php:128
msgid "A 'From email' address with a domain different than that of your website domain name, or with a domain that the hosting does not relay might cause the notification emails to be blocked, marked as spam, or not delivered at all. If you are not 100% sure about this change, consult with your web host."
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-email.php:131
msgid "Email Address:"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-email.php:132
msgid "Display Name:"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-email.php:140
msgid "Tip: The 'From email' address should match your website domain. If the \"from address\" does not match your website domain, the emails may be blocked or marked as spam. If you are not sure about this please consult with your website administrator / developer or "
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-email.php:140
msgid "for more information."
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-email.php:144
msgid "Email delivery test"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-email.php:146
msgid "The plugin sends emails with one-time codes, blocked account notifications and more. Use the button below to confirm the plugin can successfully send emails."
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-email.php:153
msgid "Test email delivery"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-email.php:174
msgid "User backup codes email"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-email.php:175
msgid "This email can be sent a user once backup codes are generated."
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-email.php:182
msgid "2FA setup code email"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-email.php:183
msgid "This is the email sent to a user when setting up 2FA via email."
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-email.php:187
msgid "Login code email"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-email.php:188
msgid "This is the email sent to a user when a login code is required."
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-email.php:192
msgid "User account locked email"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-email.php:193
msgid "This is the email sent to a user upon grace period expiry."
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-email.php:197
msgid "User account unlocked email"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-email.php:198
msgid "This is the email sent to a user when the user's account has been unlocked."
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-email.php:202
msgid "User reset password code email"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-email.php:203
msgid "This is the email sent to a user when a password reset is requested."
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-email.php:264
msgid "Please provide an email address"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-email.php:274
msgid "Please provide a display name."
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-email.php:285
msgid "Please provide a valid email address. Your email address has not been updated."
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-email.php:305
msgid "Please only use alphanumeric text. Your display name has not been updated."
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-email.php:415
msgid "Email Templates"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-email.php:424
msgid "Send this email"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-email.php:430
msgid "Uncheck to disable this message."
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-email.php:437
msgid "Email subject"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-email.php:446
msgid "Email body"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-email.php:487
msgid "Send test email"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-general.php:182
msgid "Do you want to delete the plugin data from the database upon uninstall"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-general.php:184
msgid "The plugin saves its settings in the WordPress database. By default the plugin settings are kept in the database so if it is installed again, you do not have to reconfigure the plugin. Enable this setting to delete the plugin settings from the database upon uninstall."
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-general.php:189
msgid "Delete data"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-general.php:195
msgid "Delete data upon uninstall"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-general.php:220
msgid "Limit 2FA settings access"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-general.php:222
msgid "Use this setting to hide this plugin configuration area from all other admins."
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-general.php:227
msgid "Limit access to 2FA settings"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-general.php:233
msgid "Hide settings from other administrators"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-general.php:252
msgid "Disable 2FA code brute force protection"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-general.php:254
msgid "When using email and SMS 2FA, the plugin sends the users a new one-time code whenever they enter the wrong code when logging in. This is a security enhancement, a sort of brute force protection. You can disable this feature from the below setting, however, it is not recommended."
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-general.php:259
msgid "Disable one-time code brute force protection"
msgstr ""

#. translators: support email.
#: includes/classes/Admin/SettingsPages/class-settings-page-general.php:286
msgid "Use this setting below to configure the properties of the two-factor authentication on your website and how users use it. If you have any questions send us an email at %1$s."
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-general.php:291
msgid "What should the plugin do if the 2FA method used during a user login is unavailable"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-general.php:293
msgid "There may be cases in which the 2FA service is unavailable when a user is trying to log in. For example, the service is unreachable or there are no credits to complete the action. In this case you can configure the plugin to either block the login process, or allow the user to log in without 2FA authentication."
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-general.php:298
msgid "Select action"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-general.php:305
msgid "Block the login."
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-general.php:313
msgid "Allow the login without 2FA"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:78
msgid "Exclude yourself?"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:79
msgid "You are about to enforce 2FA instantly on all users, including yourself, however you have not yet configured your own 2FA method. What would you like to do?"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:86
msgid "Continue anyway"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:87
msgid "Exclude myself from 2FA policies"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:188
msgid "The plugin created the 2FA settings page with the URL:"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:190
msgid "You can edit this page using the page editor, like you do with all other pages."
msgstr ""

#. translators: %s: tag name.
#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:194
msgid "Use the %s html tag in the email templates to include the URL of the 2FA configuration page when notifying the users to configure two-factor authentication."
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:253
msgid "No global 2FA methods enabled."
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:335
msgid "You must specify at least one sub-site"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:355
msgid "Grace period must be at least 1 day/hour"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:417
msgid "You must provide a new page slug."
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:447
msgid "You must specify at least one role or user"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:598
msgid "Page generated by"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:599
msgid "WP 2FA Plugin"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-policies.php:808
msgid "Do you want to redirect the user to a specific page after completing the 2FA setup wizard"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-render.php:115
msgid "Emails & templates"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-render.php:127
msgid "General settings"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-render.php:169
msgid "White labeling"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-render.php:173
msgid "Use the settings below to configure the emails which are sent to users as part of the 2FA plugin. If you have any questions send us an email at"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-white-label.php:174
msgid "Markup is not allowed in Login area CSS."
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-white-label.php:300
msgid "Change the default text used in the 2FA code page"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-white-label.php:302
msgid "This is the text shown to the users on the page when they are asked to enter the 2FA code. To change the default text, simply type it in the below placeholder."
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-white-label.php:321
msgid "Backup code page text"
msgstr ""

#: includes/classes/Admin/SettingsPages/class-settings-page-white-label.php:335
msgid "Text for logged out users trying to access the 2FA configuration page"
msgstr ""

#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:44
msgid "Which 2FA methods can your users use?"
msgstr ""

#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:46
msgid "When you uncheck any of the below 2FA methods it won't be available for your users to use. You can always change this later on from the plugin's settings."
msgstr ""

#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:58
msgid "Select the methods"
msgstr ""

#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:156
msgid "Which alternative 2FA methods can users use?"
msgstr ""

#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:158
msgid "An alternative 2FA method allows users to configure another 2FA method that can be used as a backup should the primary 2FA method fail. This can happen if, for example, a user forgets their smartphone, the smartphone runs out of battery, or there are email deliverability problems."
msgstr ""

#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:161
msgid "It is highly recommended to have an alternative 2FA method configured at all times. Below is a list of alternative 2FA methods available through this plugin:"
msgstr ""

#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:178
msgid "Backup codes allow users to log in to WordPress should they find themselves unable to log in via the primary 2FA method. Backup codes are enabled by default and are generated during the 2FA configuration process. Each backup code can be used only once. Once the initial list is exhausted, more backup codes can be generated through the user’s WordPress profile page - "
msgstr ""

#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:179
msgid "More information"
msgstr ""

#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:189
msgid "Upgrade to WP 2FA Premium for"
msgstr ""

#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:190
msgid "more alternative 2FA methods"
msgstr ""

#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:191
msgid "to give your users more options."
msgstr ""

#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:217
msgid "Do you want to enforce 2FA for some, or all the users? "
msgstr ""

#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:219
msgid "When you enforce 2FA the users will be prompted to configure 2FA the next time they login. Users have a grace period for configuring 2FA. You can configure the grace period and also exclude user(s) or role(s) in this settings page. "
msgstr ""

#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:227
msgid "Enforce 2FA on"
msgstr ""

#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:235
msgid "All users"
msgstr ""

#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:242
msgid "Only super admins"
msgstr ""

#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:247
msgid "Only super admins and site admins"
msgstr ""

#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:256
msgid "Only for specific users and roles"
msgstr ""

#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:261
msgid "Users :"
msgstr ""

#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:277
msgid "Roles :"
msgstr ""

#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:299
msgid "Also enforce 2FA on network users with super admin privileges"
msgstr ""

#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:309
msgid "These sub-sites"
msgstr ""

#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:313
msgid "Sites :"
msgstr ""

#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:341
msgid "Do not enforce on any users"
msgstr ""

#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:368
msgid "Do you want to exclude any users or roles from 2FA? "
msgstr ""

#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:370
msgid "If you are enforcing 2FA on all users but for some reason you would like to exclude individual user(s) or users with a specific role, you can exclude them below"
msgstr ""

#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:378
#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:381
msgid "Exclude the following users"
msgstr ""

#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:403
#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:408
msgid "Exclude the following roles"
msgstr ""

#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:430
msgid "Also exclude users with super admin privilege"
msgstr ""

#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:456
msgid "Do you want to exclude all the users of a site from 2FA? "
msgstr ""

#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:458
msgid "If you are enforcing 2FA on all users but for some reason you do not want to enforce it on a specific sub site, specify the sub site name below:"
msgstr ""

#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:466
#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:475
msgid "Exclude the following sites"
msgstr ""

#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:588
msgid "Seconds"
msgstr ""

#: includes/classes/Admin/Views/class-first-time-wizard-steps.php:600
msgid "Note: If users do not configure it within the configured stipulated time, their account will be locked and have to be unlocked manually."
msgstr ""

#: includes/classes/Admin/Views/class-grace-period-notifications.php:71
msgid "How do you want users to be informed they are enforced to setup 2FA?"
msgstr ""

#: includes/classes/Admin/Views/class-grace-period-notifications.php:79
msgid "Show an admin notice in the dashboard"
msgstr ""

#: includes/classes/Admin/Views/class-grace-period-notifications.php:89
msgid "Show a notification on a page on its own after the user authenticates and before accessing the dashboard"
msgstr ""

#: includes/classes/Admin/Views/class-passord-reset-2fa.php:75
msgid "Require 2FA on password reset"
msgstr ""

#: includes/classes/Admin/Views/class-passord-reset-2fa.php:132
msgid "Do you want to require 2FA when users reset their password?"
msgstr ""

#: includes/classes/Admin/Views/class-passord-reset-2fa.php:134
msgid "When you enable this setting users will be required to enter a one-time code sent to them via email when resetting the password."
msgstr ""

#: includes/classes/Admin/Views/class-passord-reset-2fa.php:140
msgid "Password reset"
msgstr ""

#: includes/classes/Admin/Views/class-re-login-2fa.php:89
msgid "Log out user after 2FA setup"
msgstr ""

#: includes/classes/Admin/Views/class-re-login-2fa.php:146
msgid "Do you want to logout users after setting up 2FA on their account?"
msgstr ""

#: includes/classes/Admin/Views/class-re-login-2fa.php:148
msgid "When you enable this setting users will be logged out automatically after configuring 2FA and they will need to log back in."
msgstr ""

#: includes/classes/Admin/Views/class-re-login-2fa.php:154
msgid "Re-login"
msgstr ""

#: includes/classes/Admin/Views/class-wizard-steps.php:89
#: includes/classes/Admin/Views/class-wizard-steps.php:90
msgid "Next"
msgstr ""

#: includes/classes/Admin/Views/class-wizard-steps.php:110
msgid "Let us help you get started"
msgstr ""

#: includes/classes/Admin/Views/class-wizard-steps.php:111
msgid "Thank you for installing the WP 2FA plugin. This quick wizard will assist you with configuring the plugin and the two-factor authentication (2FA) settings for your user and the users on this website."
msgstr ""

#: includes/classes/Admin/Views/class-wizard-steps.php:116
msgid "Let’s get started!"
msgstr ""

#: includes/classes/Admin/Views/class-wizard-steps.php:120
msgid "Skip Wizard - I know how to do this"
msgstr ""

#: includes/classes/Admin/Views/class-wizard-steps.php:153
#: includes/classes/Admin/Views/class-wizard-steps.php:203
msgid "Generate backup codes"
msgstr ""

#: includes/classes/Admin/Views/class-wizard-steps.php:154
#: includes/classes/Admin/Views/class-wizard-steps.php:204
#: includes/classes/Admin/Views/class-wizard-steps.php:236
#: includes/classes/class-wp2fa.php:147
#: includes/classes/Utils/class-white-label.php:136
msgid "Generate list of backup codes"
msgstr ""

#: includes/classes/Admin/Views/class-wizard-steps.php:160
#: includes/classes/Admin/Views/class-wizard-steps.php:165
#: includes/classes/Admin/Views/class-wizard-steps.php:166
#: includes/classes/Admin/Views/class-wizard-steps.php:206
#: includes/classes/Admin/Views/class-wizard-steps.php:207
msgid "I’ll generate them later"
msgstr ""

#: includes/classes/Admin/Views/class-wizard-steps.php:198
msgid "Generate codes"
msgstr ""

#: includes/classes/Admin/Views/class-wizard-steps.php:223
msgid "Backup 2FA methods:"
msgstr ""

#: includes/classes/Admin/Views/class-wizard-steps.php:253
msgid "Your backup codes"
msgstr ""

#: includes/classes/Admin/Views/class-wizard-steps.php:262
#: includes/classes/Admin/Views/class-wizard-steps.php:266
#: includes/classes/Admin/Views/class-wizard-steps.php:267
msgid "Download"
msgstr ""

#: includes/classes/Admin/Views/class-wizard-steps.php:263
msgid "Copy"
msgstr ""

#: includes/classes/Admin/Views/class-wizard-steps.php:270
#: includes/classes/Admin/Views/class-wizard-steps.php:271
msgid "Print"
msgstr ""

#: includes/classes/Admin/Views/class-wizard-steps.php:274
#: includes/classes/Admin/Views/class-wizard-steps.php:275
msgid "Send me the codes via email"
msgstr ""

#: includes/classes/Admin/Views/class-wizard-steps.php:281
#: includes/classes/Admin/Views/class-wizard-steps.php:287
msgid "I'm ready, close the wizard"
msgstr ""

#: includes/classes/Admin/Views/class-wizard-steps.php:340
msgid "Congratulations."
msgstr ""

#: includes/classes/Admin/Views/class-wizard-steps.php:340
msgid "Congratulations, you're almost there..."
msgstr ""

#: includes/classes/Admin/Views/class-wizard-steps.php:343
msgid "Great job, the plugin and 2FA policies are now configured. You can always change the plugin settings and 2FA policies at a later stage from the WP 2FA entry in the WordPress menu."
msgstr ""

#: includes/classes/Admin/Views/class-wizard-steps.php:356
msgid "Now you need to configure 2FA for your own user account. You can do this now (recommended) or later."
msgstr ""

#: includes/classes/Admin/Views/class-wizard-steps.php:362
#: includes/classes/Admin/Views/class-wizard-steps.php:420
msgid "Close wizard & configure 2FA later"
msgstr ""

#: includes/classes/Admin/Views/class-wizard-steps.php:417
msgid "Configure backup 2FA method"
msgstr ""

#: includes/classes/App/grace-period/class-grace-period.php:162
msgid "What should the plugin do with users who do not configure 2FA within the grace period?"
msgstr ""

#: includes/classes/App/grace-period/class-grace-period.php:170
msgid "Do not let them access the dashboard / user page once they log in until they configure 2FA"
msgstr ""

#: includes/classes/App/grace-period/class-grace-period.php:180
msgid "Block the user (administrators have to manually unblock them)"
msgstr ""

#: includes/classes/Authenticator/class-login.php:474
msgid "Error: API login for user disabled."
msgstr ""

#: includes/classes/Authenticator/class-login.php:512
msgid "Your user account has been locked because you have not configured 2FA within the grace period. Please contact the website administrator to unlock your user and you can configure 2FA."
msgstr ""

#: includes/classes/Authenticator/class-login.php:612
msgid "Cheatin&#8217; uh?"
msgstr ""

#: includes/classes/Authenticator/class-login.php:719
msgid "Log In"
msgstr ""

#: includes/classes/Authenticator/class-login.php:741
#: includes/classes/Authenticator/class-reset-passord.php:156
msgid "Resend Code"
msgstr ""

#: includes/classes/Authenticator/class-login.php:775
msgid "Or, use a backup code."
msgstr ""

#: includes/classes/Authenticator/class-login.php:798
msgid "Are you lost?"
msgstr ""

#. translators: %s: site name.
#: includes/classes/Authenticator/class-login.php:803
msgid "&larr; Back to %s"
msgstr ""

#: includes/classes/Authenticator/class-login.php:955
msgid "<p> <strong>WP-2FA</strong>: Please contact the administrator for further assistance!</p>"
msgstr ""

#: includes/classes/Authenticator/class-login.php:955
msgid "Invalid provider."
msgstr ""

#: includes/classes/Authenticator/class-login.php:1009
msgid "ERROR: Invalid backup code."
msgstr ""

#: includes/classes/Authenticator/class-login.php:1038
msgid " For security reasons you have been sent a new code via email. Please use this new code to log in."
msgstr ""

#: includes/classes/Authenticator/class-login.php:1083
msgid "You have logged in successfully."
msgstr ""

#: includes/classes/Authenticator/class-reset-passord.php:145
msgid "Get New Password"
msgstr ""

#: includes/classes/class-wp2fa.php:112
#: includes/classes/class-wp2fa.php:122
#: includes/classes/class-wp2fa.php:123
#: includes/classes/Utils/class-white-label.php:98
#: includes/classes/Utils/class-white-label.php:108
#: includes/classes/Utils/class-white-label.php:109
msgid "Please enter the two-factor authentication (2FA) verification code below to login. Depending on your 2FA setup, you can get the code from the 2FA app or it was sent to you by email."
msgstr ""

#: includes/classes/class-wp2fa.php:112
#: includes/classes/class-wp2fa.php:122
#: includes/classes/class-wp2fa.php:123
#: includes/classes/Utils/class-white-label.php:98
#: includes/classes/Utils/class-white-label.php:108
#: includes/classes/Utils/class-white-label.php:109
msgid "Note: if you are supposed to receive an email but did not receive any, please click the Resend Code button to request another code."
msgstr ""

#: includes/classes/class-wp2fa.php:113
#: includes/classes/Utils/class-white-label.php:99
msgid "You have been sent a one-time code via email. Please enter the code below and then click Get New Password to proceed with the password reset."
msgstr ""

#: includes/classes/class-wp2fa.php:113
#: includes/classes/Utils/class-white-label.php:99
msgid "Note: If you have not received the code please click the button Resend Code. If you still do not get the code after pressing the button, please contact the website's administrator."
msgstr ""

#: includes/classes/class-wp2fa.php:114
#: includes/classes/class-wp2fa.php:115
#: includes/classes/Utils/class-white-label.php:100
#: includes/classes/Utils/class-white-label.php:101
msgid "This website's administrator requires you to enable two-factor authentication (2FA) {grace_period_remaining}."
msgstr ""

#: includes/classes/class-wp2fa.php:114
#: includes/classes/class-wp2fa.php:115
#: includes/classes/Utils/class-white-label.php:100
#: includes/classes/Utils/class-white-label.php:101
msgid "Failing to configure 2FA within this time period will result in a locked account. For more information, please contact your website administrator."
msgstr ""

#: includes/classes/class-wp2fa.php:116
#: includes/classes/Utils/class-white-label.php:102
msgid "If you are using the Authy app approve the OneTouch request to log in."
msgstr ""

#: includes/classes/class-wp2fa.php:117
#: includes/classes/Utils/class-white-label.php:103
msgid "Waiting for approval from application..."
msgstr ""

#: includes/classes/class-wp2fa.php:118
#: includes/classes/Utils/class-white-label.php:104
msgid "Manually enter the code from the mobile app."
msgstr ""

#: includes/classes/class-wp2fa.php:119
#: includes/classes/class-wp2fa.php:120
#: includes/classes/Utils/class-white-label.php:105
#: includes/classes/Utils/class-white-label.php:106
msgid "Enter the 2FA code you have received over SMS."
msgstr ""

#: includes/classes/class-wp2fa.php:121
#: includes/classes/Utils/class-white-label.php:107
msgid "Please insert the YubiKey in a USB port and touch / click the button on the YubiKey to generate the OTP required to log in."
msgstr ""

#: includes/classes/class-wp2fa.php:125
#: includes/classes/Utils/class-white-label.php:111
msgid "Enter a backup verification code."
msgstr ""

#: includes/classes/class-wp2fa.php:132
#: includes/classes/class-wp2fa.php:133
#: includes/classes/Utils/class-white-label.php:118
#: includes/classes/Utils/class-white-label.php:122
msgid "Choose the 2FA method"
msgstr ""

#: includes/classes/class-wp2fa.php:133
#: includes/classes/Utils/class-white-label.php:122
msgid "Only the below 2FA method is allowed on this website:"
msgstr ""

#: includes/classes/class-wp2fa.php:134
#: includes/classes/Utils/class-white-label.php:123
msgid "Setting up Push notifications"
msgstr ""

#: includes/classes/class-wp2fa.php:134
#: includes/classes/Utils/class-white-label.php:123
msgid "To enable push notifications enter the country and cellphone number in order to use it with this account."
msgstr ""

#: includes/classes/class-wp2fa.php:135
#: includes/classes/class-wp2fa.php:136
#: includes/classes/Utils/class-white-label.php:124
#: includes/classes/Utils/class-white-label.php:125
msgid "Setting up 2FA over SMS"
msgstr ""

#: includes/classes/class-wp2fa.php:135
#: includes/classes/class-wp2fa.php:136
#: includes/classes/Utils/class-white-label.php:124
#: includes/classes/Utils/class-white-label.php:125
msgid "When you use 2FA over SMS to log in to this website you will receive your one-time code via an SMS on your cellphone. Therefore please enter the cellphone number of where you would like to receive the SMS below."
msgstr ""

#: includes/classes/class-wp2fa.php:137
#: includes/classes/Utils/class-white-label.php:126
msgid "Setting up Link over email 2FA"
msgstr ""

#: includes/classes/class-wp2fa.php:137
#: includes/classes/Utils/class-white-label.php:126
msgid "Please select the email address to where the out-of-band link should be sent:"
msgstr ""

#: includes/classes/class-wp2fa.php:138
#: includes/classes/Utils/class-white-label.php:127
msgid "Setting up 2FA with YubiKey"
msgstr ""

#: includes/classes/class-wp2fa.php:138
#: includes/classes/Utils/class-white-label.php:127
msgid "1 - Insert your YubiKey into the computer's / mobile's USB port"
msgstr ""

#: includes/classes/class-wp2fa.php:138
#: includes/classes/Utils/class-white-label.php:127
msgid "2 - Touch / press the button on your YubiKey to generate the OTP code, which is automatically populated below"
msgstr ""

#: includes/classes/class-wp2fa.php:139
#: includes/classes/Utils/class-white-label.php:128
msgid "Please type in the one-time code sent to your email address to finalize the setup. Once the code is confirmed and 2FA is set up, you only have to verify a login by clicking on a link sent to you via email."
msgstr ""

#: includes/classes/class-wp2fa.php:140
#: includes/classes/Utils/class-white-label.php:129
msgid "Please type in the code from your Authy application with name {authy_name}"
msgstr ""

#: includes/classes/class-wp2fa.php:141
#: includes/classes/class-wp2fa.php:142
#: includes/classes/Utils/class-white-label.php:130
#: includes/classes/Utils/class-white-label.php:131
msgid "Please type in the one-time code sent via SMS to your phone to confirm your phone number."
msgstr ""

#: includes/classes/class-wp2fa.php:143
#: includes/classes/Utils/class-white-label.php:132
msgid "Touch the YubiKey again to generate the OTP code to confirm the setup. Once the code is populated below, it should be automatically saved and verified. If that does not happen by any reason, once the secret key was pasted, click \"Validate & save\" button below to manually save and complete the configuration."
msgstr ""

#: includes/classes/class-wp2fa.php:144
#: includes/classes/class-wp2fa.php:145
#: includes/classes/class-wp2fa.php:146
#: includes/classes/class-wp2fa.php:165
#: includes/classes/Utils/class-white-label.php:133
#: includes/classes/Utils/class-white-label.php:134
#: includes/classes/Utils/class-white-label.php:135
#: includes/classes/Utils/class-white-label.php:154
msgid "Your login just got more secure"
msgstr ""

#: includes/classes/class-wp2fa.php:144
#: includes/classes/Utils/class-white-label.php:133
msgid "It is recommended to configure a backup 2FA method in case you do not have access to the primary 2FA method to generate a code to log in. You can configure any of the below. You can always configure any or both from your user profile page later."
msgstr ""

#: includes/classes/class-wp2fa.php:145
#: includes/classes/class-wp2fa.php:146
#: includes/classes/Utils/class-white-label.php:134
#: includes/classes/Utils/class-white-label.php:135
msgid "Congratulations! You have enabled two-factor authentication for your user. You’ve just helped towards making this website more secure!"
msgstr ""

#: includes/classes/class-wp2fa.php:146
#: includes/classes/Utils/class-white-label.php:135
msgid "You should now generate the list of backup method. Although this is optional, it is highly recommended to have a secondary 2FA method. This can be used as a backup should the primary 2FA method fail. This can happen if, for example, you forget your smartphone, the smartphone runs out of battery, or there are email deliverability problems."
msgstr ""

#: includes/classes/class-wp2fa.php:147
#: includes/classes/Utils/class-white-label.php:136
msgid "It is recommended to generate and print some backup codes in case you lose access to your primary 2FA method."
msgstr ""

#: includes/classes/class-wp2fa.php:148
#: includes/classes/Utils/class-white-label.php:137
msgid "Backup codes generated"
msgstr ""

#: includes/classes/class-wp2fa.php:148
#: includes/classes/Utils/class-white-label.php:137
msgid "Here are your backup codes:"
msgstr ""

#: includes/classes/class-wp2fa.php:149
#: includes/classes/Utils/class-white-label.php:138
msgid "Congratulations! You are all set."
msgstr ""

#: includes/classes/class-wp2fa.php:150
#: includes/classes/Utils/class-white-label.php:139
msgid "You are required to configure 2FA."
msgstr ""

#: includes/classes/class-wp2fa.php:150
#: includes/classes/Utils/class-white-label.php:139
msgid "In order to keep this site - and your details secure, this website’s administrator requires you to enable 2FA authentication to continue."
msgstr ""

#: includes/classes/class-wp2fa.php:150
#: includes/classes/Utils/class-white-label.php:139
msgid "Two factor authentication ensures only you have access to your account by creating an added layer of security when logging in -"
msgstr ""

#: includes/classes/class-wp2fa.php:150
#: includes/classes/Utils/class-white-label.php:139
msgid "Learn more"
msgstr ""

#: includes/classes/class-wp2fa.php:151
#: includes/classes/class-wp2fa.php:152
#: includes/classes/Utils/class-white-label.php:140
#: includes/classes/Utils/class-white-label.php:141
msgid "{reconfigure_or_configure_capitalized} push notification method"
msgstr ""

#: includes/classes/class-wp2fa.php:151
#: includes/classes/Utils/class-white-label.php:140
msgid "Click the below button to {reconfigure_or_configure} the push notifications configuration."
msgstr ""

#: includes/classes/class-wp2fa.php:152
#: includes/classes/Utils/class-white-label.php:141
msgid "The 2FA service you want to use is currently unavailable. Please try again later or restart the wizard to choose another method."
msgstr ""

#: includes/classes/class-wp2fa.php:153
#: includes/classes/Utils/class-white-label.php:142
msgid "{reconfigure_or_configure_capitalized} SMS method (Twilio)"
msgstr ""

#: includes/classes/class-wp2fa.php:153
#: includes/classes/Utils/class-white-label.php:142
msgid "Click the below button to {reconfigure_or_configure} the mobile phone number where the one-time code should be sent."
msgstr ""

#: includes/classes/class-wp2fa.php:154
#: includes/classes/Utils/class-white-label.php:143
msgid "{reconfigure_or_configure_capitalized} SMS method (Clickatell)"
msgstr ""

#: includes/classes/class-wp2fa.php:154
#: includes/classes/Utils/class-white-label.php:143
msgid "Please select the phone where code should be send:"
msgstr ""

#: includes/classes/class-wp2fa.php:155
#: includes/classes/Utils/class-white-label.php:144
msgid "{reconfigure_or_configure_capitalized} 2FA over YubiKey"
msgstr ""

#: includes/classes/class-wp2fa.php:155
#: includes/classes/Utils/class-white-label.php:144
msgid "Click the below button to {reconfigure_or_configure} the YubiKey associated with your user."
msgstr ""

#: includes/classes/class-wp2fa.php:156
#: includes/classes/class-wp2fa.php:157
#: includes/classes/Utils/class-white-label.php:145
#: includes/classes/Utils/class-white-label.php:146
msgid "{reconfigure_or_configure_capitalized} SMS method"
msgstr ""

#: includes/classes/class-wp2fa.php:156
#: includes/classes/class-wp2fa.php:157
#: includes/classes/Utils/class-white-label.php:145
#: includes/classes/Utils/class-white-label.php:146
msgid "The 2FA over SMS service you want to use is currently unavailable. Please try again later or restart the wizard to choose another method."
msgstr ""

#: includes/classes/class-wp2fa.php:158
#: includes/classes/Utils/class-white-label.php:147
msgid " {reconfigure_or_configure_capitalized} 2FA over YubiKey"
msgstr ""

#: includes/classes/class-wp2fa.php:158
#: includes/classes/Utils/class-white-label.php:147
msgid "The Yubico service you want to use is currently unavailable. Please try again later or restart the wizard to choose another method."
msgstr ""

#: includes/classes/class-wp2fa.php:159
#: includes/classes/Utils/class-white-label.php:148
msgid "{reconfigure_or_configure_capitalized} link over email method"
msgstr ""

#: includes/classes/class-wp2fa.php:159
#: includes/classes/Utils/class-white-label.php:148
msgid "Click the below button to {reconfigure_or_configure} the email address where the link should be sent."
msgstr ""

#: includes/classes/class-wp2fa.php:164
#: includes/classes/Utils/class-white-label.php:153
msgid "You must be logged in to view this page. {login_url}"
msgstr ""

#: includes/classes/class-wp2fa.php:165
#: includes/classes/Utils/class-white-label.php:154
msgid "Well done on configuring 2FA, your login has just got more secure. To make sure you never get locked out you are required to confirm your email address and use email as an alternative and backup 2FA method in case your primary method is unavailable. Please confirm your email address below"
msgstr ""

#: includes/classes/class-wp2fa.php:166
#: includes/classes/Utils/class-white-label.php:155
msgid "Two-factor authentication settings"
msgstr ""

#: includes/classes/class-wp2fa.php:167
#: includes/classes/Utils/class-white-label.php:156
msgid "Add two-factor authentication to strengthen the security of your user account."
msgstr ""

#: includes/classes/class-wp2fa.php:544
msgid "Your login confirmation code for {site_name}"
msgstr ""

#. translators: The login code provided from the plugin.
#: includes/classes/class-wp2fa.php:548
msgid "Enter %1$1s to log in."
msgstr ""

#: includes/classes/class-wp2fa.php:552
#: includes/classes/class-wp2fa.php:581
#: includes/classes/class-wp2fa.php:599
#: includes/classes/class-wp2fa.php:614
msgid "Thank you."
msgstr ""

#: includes/classes/class-wp2fa.php:558
msgid "2FA code for password reset"
msgstr ""

#: includes/classes/class-wp2fa.php:560
#: includes/classes/class-wp2fa.php:608
#: includes/classes/class-wp2fa.php:620
msgid "Hello,"
msgstr ""

#. translators: The login code provided from the plugin.
#: includes/classes/class-wp2fa.php:564
msgid "Someone from the IP address %1$1s has requested a password reset for the user %2$2s on the website %3$3s. If this was you please use the below code to proceed with the password reset:"
msgstr ""

#: includes/classes/class-wp2fa.php:573
msgid "If this was not you, ignore this email and contact your website administrator."
msgstr ""

#. translators: The login code provided from the plugin.
#: includes/classes/class-wp2fa.php:577
msgid "Please enter this code to confirm 2FA setup: %1$1s"
msgstr ""

#: includes/classes/class-wp2fa.php:587
msgid "Your user on {site_name} has been locked"
msgstr ""

#: includes/classes/class-wp2fa.php:589
msgid "Hello."
msgstr ""

#. translators: %2s - the name of the site.
#: includes/classes/class-wp2fa.php:593
msgid "Since you have not enabled two-factor authentication for the user %1$1s on the website %2$2s within the grace period, your account has been locked."
msgstr ""

#: includes/classes/class-wp2fa.php:598
msgid "Contact your website administrator to unlock your account."
msgstr ""

#: includes/classes/class-wp2fa.php:605
msgid "Your user on {site_name} has been unlocked"
msgstr ""

#: includes/classes/class-wp2fa.php:608
msgid "Your user"
msgstr ""

#: includes/classes/class-wp2fa.php:608
#: includes/classes/class-wp2fa.php:620
msgid "on the website"
msgstr ""

#: includes/classes/class-wp2fa.php:608
msgid "has been unlocked. Please configure two-factor authentication within the grace period, otherwise your account will be locked again."
msgstr ""

#: includes/classes/class-wp2fa.php:611
msgid "You can configure 2FA from this page:"
msgstr ""

#: includes/classes/class-wp2fa.php:614
#: includes/classes/class-wp2fa.php:624
msgid "WP 2FA plugin"
msgstr ""

#: includes/classes/class-wp2fa.php:617
msgid "2FA backup codes for user {user_login_name} on {site_name}"
msgstr ""

#: includes/classes/class-wp2fa.php:620
msgid "Below please find the 2FA backup codes for your user"
msgstr ""

#: includes/classes/class-wp2fa.php:620
msgid "The website's URL is"
msgstr ""

#: includes/classes/class-wp2fa.php:624
msgid "Thank you for enabling 2FA on your account and helping us keeping the website secure."
msgstr ""

#: includes/classes/class-wp2fa.php:759
msgid "Reconfigure"
msgstr ""

#: includes/classes/class-wp2fa.php:759
msgid "Configure"
msgstr ""

#. translators: The username.
#: includes/classes/class-wp2fa.php:959
msgid "User %1$s logged in without 2FA"
msgstr ""

#. translators: the site name.
#: includes/classes/class-wp2fa.php:969
msgid "2FA is enforced on the user %1$s on the website %2$s. However, since the WP 2FA plugin has not been configured properly it cannot enforce the user to configure 2FA, so the user logged in without 2FA."
msgstr ""

#. translators: the support e-mail.
#: includes/classes/class-wp2fa.php:979
msgid "To enforce 2FA on users logging in from non default WordPress login pages please configure the %1$s. If you need assistance, please contact us at %2$s."
msgstr ""

#: includes/classes/class-wp2fa.php:1146
msgid "For security reasons WP 2FA needs to store the private key in the wp-config.php file. However, it is unable to. This can happen because of restrictive permissions, or the file is not in the default location. To fix this you can:"
msgstr ""

#: includes/classes/class-wp2fa.php:1148
msgid "Option A) allow the plugin to write to the wp-config.php file temporarily by changing the wp-config.php permissions to 755. Once ready, click the button to proceed."
msgstr ""

#: includes/classes/class-wp2fa.php:1150
msgid "Option B) Add the encryption key to the wp-config.php file yourself by "
msgstr ""

#: includes/classes/class-wp2fa.php:1151
msgid "following these instructions."
msgstr ""

#: includes/classes/class-wp2fa.php:1152
msgid ""
"Once you complete any of the above, please click the button below.\n"
"\t\t\t\t\t\t"
msgstr ""

#: includes/classes/class-wp2fa.php:1161
msgid "Write key to file now / Check for the key in file"
msgstr ""

#: includes/classes/Shortcodes/class-shortcodes.php:56
#: includes/classes/Shortcodes/class-shortcodes.php:192
msgid "All done."
msgstr ""

#: includes/classes/Shortcodes/class-shortcodes.php:57
#: includes/classes/Shortcodes/class-shortcodes.php:193
msgid "Your login just got more secure."
msgstr ""

#: includes/classes/Shortcodes/class-shortcodes.php:151
msgid "Login here."
msgstr ""

#: includes/classes/Utils/class-date-time-utils.php:41
msgid "no grace period"
msgstr ""

#. translators: Grace period expiration label. %s: Date and time formatted using WordPress date and time formats.
#: includes/classes/Utils/class-date-time-utils.php:66
msgid "before %s"
msgstr ""

#: includes/classes/Utils/class-debugging.php:65
msgid "Current memory usage: "
msgstr ""

#: includes/classes/Utils/class-user-utils.php:366
msgid "Configured"
msgstr ""

#: includes/classes/Utils/class-user-utils.php:367
msgid "Required but not configured"
msgstr ""

#: includes/classes/Utils/class-user-utils.php:368
msgid "Configured (but not required)"
msgstr ""

#: includes/classes/Utils/class-user-utils.php:369
msgid "Not required & not configured"
msgstr ""

#: includes/classes/Utils/class-user-utils.php:371
msgid "Locked"
msgstr ""

#: includes/classes/Utils/class-user-utils.php:372
msgid "User has not logged in yet, 2FA status is unknown"
msgstr ""

#: includes/functions/core.php:236
msgid "Congratulations"
msgstr ""

#: includes/functions/core.php:237
msgid "Your account just got more secure"
msgstr ""

#: includes/functions/core.php:238
msgid "Close Wizard & Refresh"
msgstr ""

#: includes/functions/core.php:239
msgid "Processing Update"
msgstr ""

#: includes/functions/core.php:240
msgid "Email successfully sent"
msgstr ""

#: includes/functions/core.php:241
msgid "Email delivery failed"
msgstr ""

#: includes/functions/core.php:243
msgid "Validating your license, please wait..."
msgstr ""

#: wp-2fa.php:167
msgid "This plugin requires OpenSSL. Contact your web host or website administrator so they can enable OpenSSL. Re-activate the plugin once the library has been enabled."
msgstr ""
dist/js/micromodal.js000064400000041135150755130600010615 0ustar00(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  typeof define === 'function' && define.amd ? define(factory) :
  (global = global || self, global.MicroModal = factory());
}(this, (function () { 'use strict';

  function _classCallCheck(instance, Constructor) {
    if (!(instance instanceof Constructor)) {
      throw new TypeError("Cannot call a class as a function");
    }
  }

  function _defineProperties(target, props) {
    for (var i = 0; i < props.length; i++) {
      var descriptor = props[i];
      descriptor.enumerable = descriptor.enumerable || false;
      descriptor.configurable = true;
      if ("value" in descriptor) descriptor.writable = true;
      Object.defineProperty(target, descriptor.key, descriptor);
    }
  }

  function _createClass(Constructor, protoProps, staticProps) {
    if (protoProps) _defineProperties(Constructor.prototype, protoProps);
    if (staticProps) _defineProperties(Constructor, staticProps);
    return Constructor;
  }

  function _toConsumableArray(arr) {
    return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
  }

  function _arrayWithoutHoles(arr) {
    if (Array.isArray(arr)) return _arrayLikeToArray(arr);
  }

  function _iterableToArray(iter) {
    if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter);
  }

  function _unsupportedIterableToArray(o, minLen) {
    if (!o) return;
    if (typeof o === "string") return _arrayLikeToArray(o, minLen);
    var n = Object.prototype.toString.call(o).slice(8, -1);
    if (n === "Object" && o.constructor) n = o.constructor.name;
    if (n === "Map" || n === "Set") return Array.from(n);
    if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
  }

  function _arrayLikeToArray(arr, len) {
    if (len == null || len > arr.length) len = arr.length;

    for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];

    return arr2;
  }

  function _nonIterableSpread() {
    throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
  }

  var MicroModal = function () {

    var FOCUSABLE_ELEMENTS = ['a[href]', 'area[href]', 'input:not([disabled]):not([type="hidden"]):not([aria-hidden])', 'select:not([disabled]):not([aria-hidden])', 'textarea:not([disabled]):not([aria-hidden])', 'button:not([disabled]):not([aria-hidden])', 'iframe', 'object', 'embed', '[contenteditable]', '[tabindex]:not([tabindex^="-"])'];

    var Modal = /*#__PURE__*/function () {
      function Modal(_ref) {
        var targetModal = _ref.targetModal,
            _ref$triggers = _ref.triggers,
            triggers = _ref$triggers === void 0 ? [] : _ref$triggers,
            _ref$onShow = _ref.onShow,
            onShow = _ref$onShow === void 0 ? function () {} : _ref$onShow,
            _ref$onClose = _ref.onClose,
            onClose = _ref$onClose === void 0 ? function () {} : _ref$onClose,
            _ref$openTrigger = _ref.openTrigger,
            openTrigger = _ref$openTrigger === void 0 ? 'data-micromodal-trigger' : _ref$openTrigger,
            _ref$closeTrigger = _ref.closeTrigger,
            closeTrigger = _ref$closeTrigger === void 0 ? 'data-micromodal-close' : _ref$closeTrigger,
            _ref$openClass = _ref.openClass,
            openClass = _ref$openClass === void 0 ? 'is-open' : _ref$openClass,
            _ref$disableScroll = _ref.disableScroll,
            disableScroll = _ref$disableScroll === void 0 ? false : _ref$disableScroll,
            _ref$disableFocus = _ref.disableFocus,
            disableFocus = _ref$disableFocus === void 0 ? false : _ref$disableFocus,
            _ref$awaitCloseAnimat = _ref.awaitCloseAnimation,
            awaitCloseAnimation = _ref$awaitCloseAnimat === void 0 ? false : _ref$awaitCloseAnimat,
            _ref$awaitOpenAnimati = _ref.awaitOpenAnimation,
            awaitOpenAnimation = _ref$awaitOpenAnimati === void 0 ? false : _ref$awaitOpenAnimati,
            _ref$debugMode = _ref.debugMode,
            debugMode = _ref$debugMode === void 0 ? false : _ref$debugMode;

        _classCallCheck(this, Modal);

        // Save a reference of the modal
        this.modal = document.getElementById(targetModal); // Save a reference to the passed config

        this.config = {
          debugMode: debugMode,
          disableScroll: disableScroll,
          openTrigger: openTrigger,
          closeTrigger: closeTrigger,
          openClass: openClass,
          onShow: onShow,
          onClose: onClose,
          awaitCloseAnimation: awaitCloseAnimation,
          awaitOpenAnimation: awaitOpenAnimation,
          disableFocus: disableFocus
        }; // Register click events only if pre binding eventListeners

        if (triggers.length > 0) this.registerTriggers.apply(this, _toConsumableArray(triggers)); // pre bind functions for event listeners

        this.onClick = this.onClick.bind(this);
        this.onKeydown = this.onKeydown.bind(this);
      }
      /**
       * Loops through all openTriggers and binds click event
       * @param  {array} triggers [Array of node elements]
       * @return {void}
       */


      _createClass(Modal, [{
        key: "registerTriggers",
        value: function registerTriggers() {
          var _this = this;

          for (var _len = arguments.length, triggers = new Array(_len), _key = 0; _key < _len; _key++) {
            triggers[_key] = arguments[_key];
          }

          triggers.filter(Boolean).forEach(function (trigger) {
            trigger.addEventListener('click', function (event) {
              return _this.showModal(event);
            });
          });
        }
      }, {
        key: "showModal",
        value: function showModal() {
          var _this2 = this;

          var event = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
          this.activeElement = document.activeElement;
          this.modal.setAttribute('aria-hidden', 'false');
          this.modal.classList.add(this.config.openClass);
          this.scrollBehaviour('disable');
          this.addEventListeners();

          if (this.config.awaitOpenAnimation) {
            var handler = function handler() {
              _this2.modal.removeEventListener('animationend', handler, false);

              _this2.setFocusToFirstNode();
            };

            this.modal.addEventListener('animationend', handler, false);
          } else {
            this.setFocusToFirstNode();
          }

          this.config.onShow(this.modal, this.activeElement, event);
        }
      }, {
        key: "closeModal",
        value: function closeModal() {
          var event = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
          var modal = this.modal;
          this.modal.setAttribute('aria-hidden', 'true');
          this.removeEventListeners();
          this.scrollBehaviour('enable');

          if (this.activeElement && this.activeElement.focus) {
            this.activeElement.focus();
          }

          this.config.onClose(this.modal, this.activeElement, event);

          if (this.config.awaitCloseAnimation) {
            var openClass = this.config.openClass; // <- old school ftw

            this.modal.addEventListener('animationend', function handler() {
              modal.classList.remove(openClass);
              modal.removeEventListener('animationend', handler, false);
            }, false);
          } else {
            modal.classList.remove(this.config.openClass);
          }
        }
      }, {
        key: "closeModalById",
        value: function closeModalById(targetModal) {
          this.modal = document.getElementById(targetModal);
          if (this.modal) this.closeModal();
        }
      }, {
        key: "scrollBehaviour",
        value: function scrollBehaviour(toggle) {
          if (!this.config.disableScroll) return;
          var body = document.querySelector('body');

          switch (toggle) {
            case 'enable':
              Object.assign(body.style, {
                overflow: ''
              });
              break;

            case 'disable':
              Object.assign(body.style, {
                overflow: 'hidden'
              });
              break;
          }
        }
      }, {
        key: "addEventListeners",
        value: function addEventListeners() {
          this.modal.addEventListener('touchstart', this.onClick);
          this.modal.addEventListener('click', this.onClick);
          document.addEventListener('keydown', this.onKeydown);
        }
      }, {
        key: "removeEventListeners",
        value: function removeEventListeners() {
          this.modal.removeEventListener('touchstart', this.onClick);
          this.modal.removeEventListener('click', this.onClick);
          document.removeEventListener('keydown', this.onKeydown);
        }
      }, {
        key: "onClick",
        value: function onClick(event) {
          if (event.target.hasAttribute(this.config.closeTrigger)) {
            this.closeModal(event);
          }
        }
      }, {
        key: "onKeydown",
        value: function onKeydown(event) {
          if (event.keyCode === 27) this.closeModal(event); // esc

          if (event.keyCode === 9) this.retainFocus(event); // tab

          if (event.keyCode === 13) { // enter
            var modal = jQuery('#' + this.modal.id);
            modal.find('.button:visible:first').click();
          }
        }
      }, {
        key: "getFocusableNodes",
        value: function getFocusableNodes() {
          var nodes = this.modal.querySelectorAll(FOCUSABLE_ELEMENTS);
          return Array.apply(void 0, _toConsumableArray(nodes));
        }
        /**
         * Tries to set focus on a node which is not a close trigger
         * if no other nodes exist then focuses on first close trigger
         */

      }, {
        key: "setFocusToFirstNode",
        value: function setFocusToFirstNode() {
          var _this3 = this;

          if (this.config.disableFocus) return;
          var focusableNodes = this.getFocusableNodes(); // no focusable nodes

          if (focusableNodes.length === 0) return; // remove nodes on whose click, the modal closes
          // could not think of a better name :(

          var nodesWhichAreNotCloseTargets = focusableNodes.filter(function (node) {
            return !node.hasAttribute(_this3.config.closeTrigger);
          });
          if (nodesWhichAreNotCloseTargets.length > 0) nodesWhichAreNotCloseTargets[0].focus();
          if (nodesWhichAreNotCloseTargets.length === 0) focusableNodes[0].focus();
        }
      }, {
        key: "retainFocus",
        value: function retainFocus(event) {
          var focusableNodes = this.getFocusableNodes(); // no focusable nodes

          if (focusableNodes.length === 0) return;
          /**
           * Filters nodes which are hidden to prevent
           * focus leak outside modal
           */

          focusableNodes = focusableNodes.filter(function (node) {
            return node.offsetParent !== null;
          }); // if disableFocus is true

          if (!this.modal.contains(document.activeElement)) {
            focusableNodes[0].focus();
          } else {
            var focusedItemIndex = focusableNodes.indexOf(document.activeElement);

            if (event.shiftKey && focusedItemIndex === 0) {
              focusableNodes[focusableNodes.length - 1].focus();
              event.preventDefault();
            }

            if (!event.shiftKey && focusableNodes.length > 0 && focusedItemIndex === focusableNodes.length - 1) {
              focusableNodes[0].focus();
              event.preventDefault();
            }
          }
        }
      }]);

      return Modal;
    }();
    /**
     * Modal prototype ends.
     * Here on code is responsible for detecting and
     * auto binding event handlers on modal triggers
     */
    // Keep a reference to the opened modal


    var activeModal = null;
    /**
     * Generates an associative array of modals and it's
     * respective triggers
     * @param  {array} triggers     An array of all triggers
     * @param  {string} triggerAttr The data-attribute which triggers the module
     * @return {array}
     */

    var generateTriggerMap = function generateTriggerMap(triggers, triggerAttr) {
      var triggerMap = [];
      triggers.forEach(function (trigger) {
        var targetModal = trigger.attributes[triggerAttr].value;
        if (triggerMap[targetModal] === undefined) triggerMap[targetModal] = [];
        triggerMap[targetModal].push(trigger);
      });
      return triggerMap;
    };
    /**
     * Validates whether a modal of the given id exists
     * in the DOM
     * @param  {number} id  The id of the modal
     * @return {boolean}
     */


    var validateModalPresence = function validateModalPresence(id) {
      if (!document.getElementById(id)) {
        console.warn("MicroModal: \u2757Seems like you have missed %c'".concat(id, "'"), 'background-color: #f8f9fa;color: #50596c;font-weight: bold;', 'ID somewhere in your code. Refer example below to resolve it.');
        console.warn("%cExample:", 'background-color: #f8f9fa;color: #50596c;font-weight: bold;', "<div class=\"modal\" id=\"".concat(id, "\"></div>"));
        return false;
      }
    };
    /**
     * Validates if there are modal triggers present
     * in the DOM
     * @param  {array} triggers An array of data-triggers
     * @return {boolean}
     */


    var validateTriggerPresence = function validateTriggerPresence(triggers) {
      if (triggers.length <= 0) {
        console.warn("MicroModal: \u2757Please specify at least one %c'micromodal-trigger'", 'background-color: #f8f9fa;color: #50596c;font-weight: bold;', 'data attribute.');
        console.warn("%cExample:", 'background-color: #f8f9fa;color: #50596c;font-weight: bold;', "<a href=\"#\" data-micromodal-trigger=\"my-modal\"></a>");
        return false;
      }
    };
    /**
     * Checks if triggers and their corresponding modals
     * are present in the DOM
     * @param  {array} triggers   Array of DOM nodes which have data-triggers
     * @param  {array} triggerMap Associative array of modals and their triggers
     * @return {boolean}
     */


    var validateArgs = function validateArgs(triggers, triggerMap) {
      validateTriggerPresence(triggers);
      if (!triggerMap) return true;

      for (var id in triggerMap) {
        validateModalPresence(id);
      }

      return true;
    };
    /**
     * Binds click handlers to all modal triggers
     * @param  {object} config [description]
     * @return void
     */


    var init = function init(config) {
      // Create an config object with default openTrigger
      var options = Object.assign({}, {
        openTrigger: 'data-micromodal-trigger'
      }, config); // Collects all the nodes with the trigger

      var triggers = _toConsumableArray(document.querySelectorAll("[".concat(options.openTrigger, "]"))); // Makes a mappings of modals with their trigger nodes


      var triggerMap = generateTriggerMap(triggers, options.openTrigger); // Checks if modals and triggers exist in dom

      if (options.debugMode === true && validateArgs(triggers, triggerMap) === false) return; // For every target modal creates a new instance

      for (var key in triggerMap) {
        var value = triggerMap[key];
        options.targetModal = key;
        options.triggers = _toConsumableArray(value);
        activeModal = new Modal(options); // eslint-disable-line no-new
      }
    };
    /**
     * Shows a particular modal
     * @param  {string} targetModal [The id of the modal to display]
     * @param  {object} config [The configuration object to pass]
     * @return {void}
     */


    var show = function show(targetModal, config) {
      var options = config || {};
      options.targetModal = targetModal; // Checks if modals and triggers exist in dom

      if (options.debugMode === true && validateModalPresence(targetModal) === false) return; // clear events in case previous modal wasn't close

      if (activeModal) activeModal.removeEventListeners(); // stores reference to active modal

      activeModal = new Modal(options); // eslint-disable-line no-new

      activeModal.showModal();
    };
    /**
     * Closes the active modal
     * @param  {string} targetModal [The id of the modal to close]
     * @return {void}
     */


    var close = function close(targetModal) {
      targetModal ? activeModal.closeModalById(targetModal) : activeModal.closeModal();
    };

    return {
      init: init,
      show: show,
      close: close
    };
  }();
  window.MicroModal = MicroModal;

  return MicroModal;

})));
dist/js/admin.js000064400000111557150755130600007565 0ustar00
try{
jQuery(document).ready(function(){if(jQuery('.wp-2fa-settings-wrapper .notice').length){jQuery('.wp-2fa-settings-wrapper .notice').addClass('2fa-budged');jQuery('.wp-2fa-settings-wrapper .notice').insertBefore('.wp-2fa-settings-wrapper');jQuery('.2fa-budged').css('margin-left','0');}
if(jQuery('#excluded_sites_search').length){const usersUrl=`${wp2faData.ajaxURL}?action=wp_2fa_get_all_network_sites&wp_2fa_nonce=${wp2faData.nonce}`;jQuery('#excluded_sites_search').autocomplete({source:usersUrl,minLength:1,focus:function(){return false;},select:function(event,ui){const currentlyExcluded=jQuery('#excluded_sites').val();if(!currentlyExcluded.includes(ui.item.value)){jQuery('#excluded_sites').val(`${currentlyExcluded + ui.item.value},`);}
const excludedUsersArray=jQuery('#excluded_sites').val().split(',');jQuery('#excluded_sites_buttons').html('');jQuery.each(excludedUsersArray,function(i){if(excludedUsersArray[i]){jQuery('#excluded_sites_buttons').append(`<a class="user-btn button button-secondary" data-user-value="${excludedUsersArray[i]}">${excludedUsersArray[i].split( ':' )[0]}<span class="remove-item">x</span></a>`);}});jQuery('#excluded_sites_search').val('');return false;},open:function(event,ui){jQuery('.ui-menu-item').each(function(i,obj){var originalLabel=jQuery(this).text();jQuery(this).text(originalLabel.split(':')[0]);});}});var excludedUsersArray=jQuery('#excluded_sites').val().split(',');jQuery.each(excludedUsersArray,function(i){if(excludedUsersArray[i]){jQuery('#excluded_sites_buttons').append(`<a class="user-btn button button-secondary" data-user-value="${excludedUsersArray[i]}">${excludedUsersArray[i].split( ':' )[0]}<span class="remove-item">x</span></a>`);}});}
jQuery('body').on('click','.remove-item',function(e){e.preventDefault();var textToRemove=jQuery(this).closest('.user-btn').attr('data-user-value');var textToRemove=`${textToRemove},`;var currentlyExcluded=jQuery(this).closest('div').siblings('input[type="hidden"]').val();var currentlyExcluded=currentlyExcluded.replace(textToRemove,'');jQuery(this).closest('div').siblings('input[type="hidden"]').val(currentlyExcluded);jQuery(this).closest('.user-btn').remove();});jQuery('[name="wp_2fa_policy[enforcement-policy]"], [name="wp_2fa_policy[grace-policy]"]').on("input",function(){if(jQuery('input[name="wp_2fa_policy[grace-policy]"]:checked').val()=='no-grace-period'&&jQuery('input[name="wp_2fa_policy[enforcement-policy]"]:checked').val()=='all-users'){var userToAdd=jQuery('[data-user-login-name]').attr('data-user-login-name');var targetElement=jQuery('#excluded-users-multi-select');if(jQuery('#exclude-self-from-instant-2fa').length&&!jQuery(targetElement).find('option[value="'+userToAdd+'"]').length){MicroModal.show('exclude-self-from-instant-2fa');}}});jQuery('body').on('click','[data-user-login-name]',function(e){e.preventDefault();var newValue=[];var userToAdd=jQuery('[data-user-login-name]').attr('data-user-login-name');newValue.push(userToAdd);var targetElement=jQuery('#excluded-users-multi-select');if(!jQuery(targetElement).find('option[value="'+newValue+'"]').length){var newState=new Option(newValue,newValue,true,true);jQuery(targetElement).append(newState).trigger('change');}});jQuery('body').on('input','input[type="number"]#grace-period',function(e){var targetElm=jQuery(this);var currentValue=targetElm.val();var newValue=!!currentValue&&0<=Math.abs(currentValue)?Math.abs(currentValue):null;var upperLimit=targetElm.attr('max');if(parseInt(upperLimit)<parseInt(newValue)){newValue=upperLimit;}
if(newValue!=currentValue){targetElm.val(newValue);}});jQuery('body').on('focusout','input[type="number"]#grace-period',function(e){var targetElm=jQuery(this);var currentValue=targetElm.val();var minVal=targetElm.attr('min');if(''===jQuery.trim(currentValue)){targetElm.val(minVal);}});jQuery('body').on('click','input[type="checkbox"]#grace-cron',function(e){if(jQuery(this).is(':checked')){jQuery('.destory-session-setting').removeClass('disabled');}else if(jQuery(this).is(':not(:checked)')){jQuery('.destory-session-setting').addClass('disabled');jQuery('input[type="checkbox"]#destory-session').prop('checked',false);}});if(jQuery('input[type="checkbox"]#grace-cron').is(':checked')){jQuery('.destory-session-setting').removeClass('disabled');}else if(jQuery('input[type="checkbox"]#grace-cron').is(':not(:checked)')){jQuery('.destory-session-setting').addClass('disabled');jQuery('input[type="checkbox"]#destory-session').prop('checked',false);}
jQuery('body').on('click','input[type="radio"][id*="use_custom_page"]',function(e){if(jQuery(this).attr('id').indexOf('dont')&&jQuery(this).is(':checked')){jQuery(this).closest('table').find('.custom-user-page-setting').removeClass('disabled');if(jQuery('#custom-user-page-url').val().trim().length===0){jQuery('#custom-user-page-url').val('/wp-2fa-config/');}}else{jQuery(this).closest('table').find('.custom-user-page-setting').addClass('disabled');if(jQuery('#custom-user-page-url').val().trim().length===0){jQuery('#custom-user-page-url').val('');}}});jQuery('body').on('click','input[type="checkbox"][name*="enable_email"]',function(e){if(jQuery(this).is(':checked')){jQuery(this).parent().parent().find('div.use-different-hotp-mail').removeClass('disabled');}else{jQuery(this).parent().parent().find('div.use-different-hotp-mail').addClass('disabled');}});jQuery('body').on('click','input[type="checkbox"][name*="enable_trusted_devices"]',function(e){if(jQuery(this).is(':checked')){jQuery(this).closest('table').find('.trusted-settings').removeClass('disabled');}else{jQuery(this).closest('table').find('.trusted-settings').addClass('disabled');}});jQuery('body').on('click','input[type="checkbox"][name*="enable_oob_email"]',function(e){if(jQuery(this).is(':checked')){jQuery(this).parent().parent().find('div.use-different-oob-mail').removeClass('disabled');}else{jQuery(this).parent().parent().find('div.use-different-oob-mail').addClass('disabled');}});jQuery('body').on('click','.js-button-test-email-trigger',function(e){e.preventDefault();const button=jQuery(this);const emailId=button.attr('data-email-id');const nonceValue=button.attr('data-nonce');button.append('<span class="spinner is-active"></span>');const spinner=button.find('.spinner');button.siblings('.notice').remove();button.attr('disabled','disabled');button.addClass('has-spinner');jQuery.post(wp2faData.ajaxURL,{action:'wp2fa_test_email',email_id:emailId,_wpnonce:nonceValue}).done(function(data){let classes='notice notice-after-button notice-';classes+=(data.success)?'success':'error';var message=(data.success)?wp2faData.email_sent_success:wp2faData.email_sent_failure;if('data'in data){message=data.data;}
button.after(`<span class="${classes}">${message}</span>`);spinner.remove();button.removeClass('has-spinner');button.removeAttr('disabled');});});jQuery('body').on('click','input[type="checkbox"].disabled[data-disabled-hint]',function(e){var text=jQuery(this).attr('data-disabled-hint');jQuery('<p id="setup-warning" style="color: red:>'+text+'</p>').insertAfter(this).delay(2000).remove();});jQuery('.white-labelling-tabs #wp2fa_links-inner-wrapper a').each(function(i){if(window.location.href==jQuery(this).attr('href')){jQuery(this).addClass('nav-tab-active');}else{jQuery(this).removeClass('nav-tab-active');}});jQuery('body').on('click','.notice-success.is-dismissible.2fa-budged .notice-dismiss',function(e){jQuery('.notice-success.is-dismissible.2fa-budged').slideUp();});jQuery('.2fa-email-notice').click(function(){const thisNotice=jQuery(this).closest('.notice');jQuery.ajax({type:'POST',url:wp2faData.ajaxURL,async:true,data:{action:'wp2fa_dismiss_notice_mail_domain',nonce:jQuery('#wp2fa_dismiss_notice_mail_domain').val()},success:function(data){jQuery(thisNotice).slideUp();}});});});
}
catch(e){console.error("An error has occurred settings.js: "+e.stack);}

try{
jQuery(document).ready(function(){MicroModal.init();function updateStepTitles(){if(jQuery('[data-step-title]').length){jQuery('.step-title-wrapper').remove();jQuery('.wp2fa-setup-content').prepend('<div class="step-title-wrapper"></div>');var counter=1;jQuery('[data-step-title]:not(.hidden)').each(function(){var stepLabel=jQuery(this).attr('data-step-title');if(jQuery(this).hasClass('active')){jQuery('.step-title-wrapper').append(`<span class="step-title active-step-title"><span>${counter}</span> ${stepLabel}</span>`);}else{jQuery('.step-title-wrapper').append(`<span class="step-title"><span>${counter}</span> ${stepLabel}</span>`);}
counter++;});}
checkWizardOffset();setTimeout(function(){jQuery('.step-setting-wrapper.active input[type="tel"] ').focus();},200);}
updateStepTitles();jQuery('body').on('click','.step-title',function(e){var currentLabel=jQuery(this).text().substr(2);jQuery('[data-step-title]:not(.hidden)').each(function(){var currentStep=jQuery(this);jQuery('[data-step-title]').removeClass('active');jQuery('.step-title').removeClass('active-step-title');var stepLabel=jQuery(this).attr('data-step-title');jQuery(`[data-step-title="${currentLabel}"]`).addClass('active');});updateStepTitles();});jQuery('[data-unhide-when-checked]').each(function(){if(jQuery(this).is(':checked')){const thingToShow=jQuery(this).attr('data-unhide-when-checked');jQuery(thingToShow).show(0);}});jQuery('body').on('click','[for="all-users"], [for="certain-roles-only"]',function(e){jQuery('.step-setting-wrapper.hidden').removeClass('hidden').addClass('un-hidden');updateStepTitles();});jQuery('body').on('click','[for="do-not-enforce"]',function(e){jQuery('.step-setting-wrapper.un-hidden').removeClass('un-hidden').addClass('hidden');updateStepTitles();});jQuery('body').on('click','.modal__btn',function(e){e.preventDefault();});jQuery('body').on('keypress','.wp2fa-modal',function(event){var keycode=(event.keyCode?event.keyCode:event.which);if('13'==keycode){return false;}});jQuery(document).on('click','[data-open-configure-2fa-wizard]',function(event){event.preventDefault();wp2fa_fireWizard();});jQuery(document).on('click','.step-setting-wrapper.active .option-pill input[type="checkbox"]',function(e){if('backup-codes'!==this.id&&!jQuery(this).hasClass('disabled')){if(true!==jQuery('#geek').prop('checked')&&true!==jQuery('#basic').prop('checked')){jQuery('#backup-codes').addClass('disabled');jQuery('label[for=\'backup-codes\']').addClass('disabled');window.backupCodes=jQuery('#backup-codes').prop('checked');jQuery('#backup-codes').prop('checked',false);if(jQuery('[name="next_step_setting"]').length){jQuery('[name="next_step_setting"]').addClass('disabled').attr('name','next_step_setting_disabled');}}else{jQuery('#backup-codes').removeClass('disabled');jQuery('label[for=\'backup-codes\']').removeClass('disabled');if('undefined'!==window.backupCodes){jQuery('#backup-codes').prop('checked',window.backupCodes);}
if(jQuery('[name="next_step_setting_disabled"]').length){jQuery('[name="next_step_setting_disabled"]').removeClass('disabled').attr('name','next_step_setting');}}}else{if(!jQuery(this).hasClass('disabled')){window.backupCodes=jQuery('#backup-codes').prop('checked');}else{jQuery('#backup-codes').prop('checked',false);}}});jQuery(document).on('click touchend','.radio-cells label, .radio-cells input[type="radio"]',function(e){jQuery('.option-pill').removeClass('isSelected');jQuery('.radio-cells input[type="radio"]:checked').closest('.option-pill').addClass('isSelected');});jQuery('.wizard-tooltip').each(function(){var contentDiv=jQuery(this).attr('data-tooltip-content');var ourItem=jQuery(this);if(jQuery('['+contentDiv+']').length){var content=jQuery('['+contentDiv+']').clone();jQuery(ourItem).append(content);}});jQuery(document).on('click touchend','.wizard-tooltip',function(e){var contentDiv=jQuery(this).attr('data-tooltip-content');var ourItem=jQuery(this);if(jQuery(this).hasClass('isOpen')){jQuery('.inline-helper').slideDown();}else{jQuery('[data-tooltip-content]').removeClass('isOpen');setTimeout(function(){if(jQuery('.inline-helper').length>2){jQuery('.inline-helper').not('['+contentDiv+']').slideUp();}},100);setTimeout(function(){if(jQuery('.inline-helper').length>2){jQuery('.inline-helper').not('['+contentDiv+']').remove();}},600);if(jQuery('['+contentDiv+']').length){var content=jQuery('['+contentDiv+']').html();jQuery('<div class="inline-helper" '+contentDiv+'>'+content+'</div>').insertAfter('.radio-cells');jQuery('.inline-helper').slideDown();}
jQuery(ourItem).addClass('isOpen');}});jQuery(document).on('click','.wp-2fa-method-select input[type="checkbox"]',function(e){let role_suffix=('global'===jQuery(this).data('role'))?'':'-'+jQuery(this).data('role');if('backup-codes'+role_suffix!==this.id&&!jQuery(this).hasClass('disabled')){let backDisabled=false;if(true!==jQuery('#totp'+role_suffix).prop('checked')&&true!==jQuery('#hotp'+role_suffix).prop('checked')){backDisabled=true;if(jQuery('#oob'+role_suffix).length){if(true===jQuery('#oob'+role_suffix).prop('checked')){backDisabled=false;}}
if(jQuery('#authy'+role_suffix).length){setTimeout(function(){},2000);if(true===jQuery('#authy'+role_suffix).prop('checked')){backDisabled=false;}}
if(jQuery('#twilio'+role_suffix).length){setTimeout(function(){},2000);if(true===jQuery('#twilio'+role_suffix).prop('checked')){backDisabled=false;}}}
if(backDisabled){jQuery('#backup-codes'+role_suffix).addClass('disabled');jQuery('label[for="backup-codes'+role_suffix+'"]').addClass('disabled');window.backupCodes=jQuery('#backup-codes'+role_suffix).prop('checked');jQuery('#backup-codes'+role_suffix).prop('checked',false);jQuery('[for="all-users"], [for="certain-roles-only"]').addClass('disabled');}else{jQuery('[for="all-users"], [for="certain-roles-only"]').removeClass('disabled');jQuery('#backup-codes'+role_suffix).removeClass('disabled');jQuery('label[for="backup-codes'+role_suffix+'"]').removeClass('disabled');if('undefined'!==window.backupCodes){jQuery('#backup-codes'+role_suffix).prop('checked',window.backupCodes);}}}else{if(!jQuery(this).hasClass('disabled')){window.backupCodes=jQuery('#backup-codes'+role_suffix).prop('checked');}else{jQuery('#backup-codes'+role_suffix).prop('checked',false);}}});jQuery(document).on('click','.wp2fa-setup-form .wp-2fa-method-select input[type="checkbox"]',function(e){let twilio=false;let authy=false;if(jQuery('#twilio').length){if(jQuery('#twilio[data-sid-setup-wizard]').length&&jQuery('#wizard-sid-key').is(':visible')){twilio=false;}else{twilio=jQuery('#twilio').prop('checked');}}
if(jQuery('#authy').length){if(jQuery('#authy[data-api-setup-wizard]').length&&jQuery('#wizard-api-key').is(':visible')){authy=false;}else{authy=jQuery('#authy').prop('checked');}}
if(true!==jQuery('#totp').prop('checked')&&true!==jQuery('#hotp').prop('checked')&&true!==jQuery('#oob').prop('checked')&&true!==authy&&true!==twilio){let showAlert=true;if(jQuery(this).is(jQuery('input#authy.disabled'))&&jQuery('#wizard-api-key').is(':visible')){showAlert=false;}
if(jQuery(this).is(jQuery('input#twilio.disabled'))&&jQuery('#wizard-sid-key').is(':visible')){showAlert=false;}
if(showAlert){alert('Please select at least one 2FA method');}
jQuery('a.button[name="next_step_setting"]').prop('disabled',true);jQuery('a.button[name="next_step_setting"]').addClass('disabled');}else{jQuery('a.button[name="next_step_setting"]').prop('disabled',false);jQuery('a.button[name="next_step_setting"]').removeClass('disabled');}});jQuery(document).on('click','[data-close-2fa-modal]',function(e){e.preventDefault();var modalToClose=`#${  jQuery( this ).closest( '.wp2fa-modal' ).attr( 'id' )}`;jQuery(modalToClose).removeClass('is-open').attr('aria-hidden','true');if('reLogin'in wp2faWizardData&&wp2faWizardData.reLoginEnabled==jQuery.trim(wp2faWizardData.reLogin)){jQuery.ajax({type:'POST',url:wp2faData.ajaxURL,data:{action:'custom_ajax_logout',_wpnonce:wp2faWizardData.nonce,},success:function(r){if('redirectToUrl'in wp2faWizardData&&''!=jQuery.trim(wp2faWizardData.redirectToUrl)){window.location.replace(wp2faWizardData.redirectToUrl);}else{removeShowParam();}}});}});jQuery(document).on('click','[data-close-2fa-modal-and-refresh]',function(e){e.preventDefault();var modalToClose=`#${  jQuery( this ).closest( '.wp2fa-modal' ).attr( 'id' )}`;jQuery(modalToClose).removeClass('is-open').attr('aria-hidden','true');if('reLogin'in wp2faWizardData&&wp2faWizardData.reLoginEnabled==jQuery.trim(wp2faWizardData.reLogin)){jQuery.ajax({type:'POST',url:wp2faData.ajaxURL,data:{action:'custom_ajax_logout',_wpnonce:wp2faWizardData.nonce,},success:function(r){if('redirectToUrl'in wp2faWizardData&&''!=jQuery.trim(wp2faWizardData.redirectToUrl)){window.location.replace(wp2faWizardData.redirectToUrl);}else{removeShowParam();}}});}else{removeShowParam();}});jQuery(document).on('click','[data-validate-authcode-ajax]',function(e){e.preventDefault();const thisButton=jQuery(this);let actionToRun='validate_authcode_via_ajax';let authcode=false;if(jQuery('#wp-2fa-totp-authcode').length&&jQuery('#wp-2fa-totp-authcode').val().length){authcode=true;}
if(typeof jQuery(this).data('oob-test')!=='undefined'){actionToRun='validate_oob_authcode_via_ajax';}
const nonceValue=jQuery(this).attr('data-nonce');var values={};jQuery.each(jQuery('.wp-2fa-user-profile-form :input, .wp2fa-modal :input').serializeArray(),function(i,field){values[field.name]=field.value;});const currentPageURL=window.location.href;const form=values;jQuery.ajax({type:'POST',dataType:'json',url:wp2faData.ajaxURL,data:{action:actionToRun,form:values,_wpnonce:nonceValue,},complete:function(data){if(false===data.responseJSON.success){jQuery(thisButton).parent().find('.verification-response').html(`<span style="color:red">${data.responseJSON.data['error']}</span>`);}
if(true===data.responseJSON.success){let nextSubStep=jQuery('#2fa-wizard-config-backup-codes');if(authcode){if(jQuery('#2fa-wizard-backup-methods').length){nextSubStep=jQuery('#2fa-wizard-backup-methods');}else if(jQuery('#2fa-wizard-email-backup-selected').length){nextSubStep=jQuery('#2fa-wizard-email-backup-selected');}}
jQuery(this).parent().parent().find('.active').not('.step-setting-wrapper').removeClass('active');jQuery('.wizard-step.active').removeClass('active');jQuery(nextSubStep).addClass('active');jQuery(document).on('click','#select-backup-method',function(e){e.preventDefault();var backupRadio=jQuery("input[name=backup_method_select]:checked");jQuery('.wizard-step.active').removeClass('active');jQuery('#'+backupRadio.data('step')).addClass('active');});jQuery(document).on('click','[name="save_step"], [data-close-2fa-modal]',function(){if('reLogin'in wp2faWizardData&&wp2faWizardData.reLoginEnabled==jQuery.trim(wp2faWizardData.reLogin)){jQuery.ajax({type:'POST',url:wp2faData.ajaxURL,data:{action:'custom_ajax_logout',_wpnonce:nonceValue,},success:function(r){if('redirectToUrl'in wp2faWizardData&&''!=jQuery.trim(wp2faWizardData.redirectToUrl)){window.location.replace(wp2faWizardData.redirectToUrl);}else{removeShowParam();}}});}else{if('redirectToUrl'in wp2faWizardData&&''!=jQuery.trim(wp2faWizardData.redirectToUrl)){window.location.replace(wp2faWizardData.redirectToUrl);}else{removeShowParam();}}});}}},);});jQuery('body').on('click','.contains-hidden-inputs input[type="radio"]',function(e){if(jQuery(this).hasClass('js-nested')){return;}
jQuery(this).closest('.contains-hidden-inputs').find('.hidden').hide(200);if(jQuery(this).is('[data-unhide-when-checked]')){const thingToShow=jQuery(this).attr('data-unhide-when-checked');if(jQuery(this).is(':checked')){jQuery(thingToShow).slideDown(200);}}});jQuery(document).on('click','.dismiss-user-configure-nag',function(){const thisNotice=jQuery(this).closest('.notice');jQuery.ajax({url:wp2faData.ajaxURL,data:{action:'dismiss_nag'},complete:function(){jQuery(thisNotice).slideUp();},});});jQuery(document).on('click','.dismiss-user-reconfigure-nag',function(){const thisNotice=jQuery(this).closest('.notice');jQuery.ajax({url:wp2faData.ajaxURL,data:{action:'wp2fa_dismiss_reconfigure_nag'},complete:function(data){jQuery(thisNotice).slideUp();},});});jQuery(document).on('click','[data-trigger-account-unlock]',function(){const nonce=jQuery(this).attr('data-nonce');const account=jQuery(this).attr('data-account-to-unlock');jQuery.ajax({url:wp2faData.ajaxURL,data:{action:'unlock_account',user_id:account,wp_2fa_nonce:nonce}});});jQuery(document).on('click','.remove-2fa',function(e){e.preventDefault();});jQuery('body').on('click','#2fa-wizard-totp .button[name="next_step_setting"]',function(e){e.preventDefault;const currentSubStep=jQuery(this).closest('.step-setting-wrapper.active');const nextSubStep=jQuery(currentSubStep).nextAll('div:not(.hidden)').filter(':first');jQuery(currentSubStep).removeClass('active');jQuery(nextSubStep).addClass('active');updateStepTitles();});jQuery('body').on('click','.wp2fa-first-time-wizard .button[name="next_step_setting"]',function(e){e.preventDefault;const currentSubStep=jQuery(this).closest('.step-setting-wrapper.active');const nextSubStep=jQuery(currentSubStep).nextAll('div:not(.hidden)').filter(':first');jQuery(currentSubStep).removeClass('active');jQuery(nextSubStep).addClass('active');updateStepTitles();});jQuery(document).on('click','.modal_cancel',function(e){e.preventDefault();if(jQuery('#notify-users').length){MicroModal.show('notify-users');jQuery('.button-confirm').blur();}});jQuery(document).on('click touchend','.button-confirm',function(e){e.preventDefault();MicroModal.close('configure-2fa');MicroModal.close('notify-users');jQuery('.inline-helper').remove();});jQuery(document).on('click touchend','.button-decline',function(e){e.preventDefault();});jQuery(document).on('click','#close-settings',function(e){e.preventDefault();MicroModal.close('notify-admin-settings-page');window.location.replace(jQuery(this).data('redirect-url'));});jQuery(document).on('click','.first-time-wizard',function(e){e.preventDefault();MicroModal.show('notify-admin-settings-page');});jQuery(document).on('click','[data-trigger-remove-2fa]',function(){const nonce=jQuery(this).attr('data-nonce');const account=jQuery(this).attr('data-user-id');jQuery.ajax({url:wp2faData.ajaxURL,data:{action:'remove_user_2fa',user_id:account,wp_2fa_nonce:nonce},complete:function(data){location.reload();},});});jQuery(document).on('click','[data-trigger-remove-2fa-backup-email]',function(){const nonce=jQuery(this).attr('data-nonce');const account=jQuery(this).attr('data-user-id');jQuery.ajax({url:wp2faData.ajaxURL,data:{action:'remove_backup_email',user_id:account,wp_2fa_nonce:nonce},complete:function(data){location.reload();},});});jQuery(document).on('click','[data-submit-2fa-form]',function(e){jQuery('#submit').click();});function validateEmail(email){var re=/\S+@\S+\.\S+/;return re.test(email);}
jQuery(document).on('click','[data-trigger-setup-email]',function(e){let actionToRun='send_authentication_setup_email';var emailAddress=false;var inputUsed='';if(jQuery('#use_custom_email').prop('checked')){emailAddress=jQuery('#custom-email-address').val();inputUsed=jQuery('#custom-email-address');}else{emailAddress=jQuery('#use_wp_email').val();inputUsed=jQuery('#use_wp_email');}
if(typeof jQuery(this).data('oob-test')!=='undefined'){actionToRun='send_authentication_oob_setup_email';emailAddress=jQuery('#use_wp_oob_email').val();if(jQuery('#use_custom_oob_email').prop('checked')){emailAddress=jQuery('#custom-oob-email-address').val();inputUsed=jQuery('#custom-oob-email-address');}}
if(!validateEmail(emailAddress)||false==emailAddress){e.preventDefault();let errMsg=jQuery('#2fa-error-msg');if(errMsg.length){errMsg.remove();}
if(jQuery(this).hasClass('resend-email-code')){if(jQuery('#wp-2fa-email-authcode').is(":visible")){inputUsed=jQuery('#wp-2fa-email-authcode');}else if(jQuery('#wp-2fa-oob-authcode').is(":visible")){inputUsed=jQuery('#wp-2fa-oob-authcode');}}
jQuery('<span id="2fa-error-msg" style="color:red;">'+wp2faData.invalidEmail+'</span>').insertAfter(inputUsed);return false;}
if(jQuery(this).hasClass('resend-email-code')){var updateBtnText=true;var originalBtnText=jQuery(this).text();if(jQuery('#wp-2fa-email-authcode').is(":visible")){inputUsed=jQuery('#wp-2fa-email-authcode');}else if(jQuery('#wp-2fa-oob-authcode').is(":visible")){inputUsed=jQuery('#wp-2fa-oob-authcode');}}else{const currentSubStep=jQuery(this).closest('.step-setting-wrapper.active');const nextSubStep=currentSubStep.nextAll('div:not(.hidden)').filter(':first');jQuery(currentSubStep).removeClass('active');nextSubStep.addClass('active');updateStepTitles();}
const userID=jQuery(this).attr('data-user-id');const nonce=jQuery(this).attr('data-nonce');const thisBtn=jQuery(this);jQuery.ajax({type:'POST',dataType:'json',url:wp2faData.ajaxURL,data:{action:actionToRun,email_address:emailAddress,user_id:userID,nonce:nonce},error:function(jqXHR,textStatus,errorThrown){if(false===jqXHR.responseJSON.success){let errMsg=jQuery('#2fa-error-msg');if(errMsg.length){errMsg.remove();}
jQuery('<span id="2fa-error-msg" style="color:red;">'+jqXHR.responseJSON.data[0].message+'</span>').insertAfter(inputUsed);}},complete:function(data){},success:function(data){let errMsg=jQuery('#2fa-error-msg');if(errMsg.length){errMsg.remove();}
if(updateBtnText){jQuery(thisBtn).find('span').fadeTo(100,0,function(){jQuery(thisBtn).find('span').delay(100);jQuery(thisBtn).find('span').text(wp2faWizardData.codeReSentText);jQuery(thisBtn).find('span').fadeTo(100,1);});setTimeout(function(){jQuery(thisBtn).find('span').fadeTo(100,0,function(){jQuery(thisBtn).find('span').delay(100);jQuery(thisBtn).find('span').text(originalBtnText);jQuery(thisBtn).find('span').fadeTo(100,1);});},2500);}}});});jQuery(document).on('change','[name="wp_2fa_enabled_methods"]',function(event){var step=jQuery('[name="wp_2fa_enabled_methods"]:checked').val();if(undefined===step){jQuery('.2fa-choose-method[data-name]').removeAttr('data-next-step');}else{jQuery('.2fa-choose-method[data-name]').attr('data-next-step',`2fa-wizard-${step}`);}});jQuery('body').on('click','.button[data-name="next_step_setting_modal_wizard"]',function(e){e.preventDefault;var nextStep=jQuery(this).attr('data-next-step');if(undefined===nextStep){var nextStep=jQuery('[name="wp_2fa_enabled_methods"]:checked').val();jQuery('.2fa-choose-method[data-name]').attr('data-next-step',`2fa-wizard-${nextStep}`);}
if(nextStep){const currentSubStep=jQuery(this).parent().parent().find('.active').not('.step-setting-wrapper');const nextSubStep=jQuery(`#${nextStep}`);jQuery(this).parent().parent().find('.active').not('.step-setting-wrapper').removeClass('active');jQuery('.wizard-step.active').removeClass('active');jQuery(nextSubStep).addClass('active');var in_el=jQuery(nextSubStep).find("input[type=text]");if(!in_el.length){in_el=jQuery(nextSubStep).find("input[type=password]");}
if(in_el.length){in_el.focus();}}else{const currentSubStep=jQuery(this).parent().parent().find('.active').not('.step-setting-wrapper');const nextSubStep=jQuery(currentSubStep).next();jQuery('.wizard-step.active').removeClass('active');jQuery(nextSubStep).addClass('active');}
jQuery('.inline-helper').remove();});jQuery('body').on('click','.button[data-trigger-generate-backup-codes]',function(e){e.preventDefault();const actionToRun='wp2fa_run_ajax_generate_json';const nonceValue=jQuery(this).attr('data-nonce');const currentSubStep=jQuery(this).closest('.step-setting-wrapper.active');const nextSubStep=jQuery(currentSubStep).nextAll('div:not(.hidden)').filter(':first');jQuery(currentSubStep).removeClass('active');jQuery(nextSubStep).addClass('active');updateStepTitles();jQuery.ajax({type:'POST',dataType:'json',url:wp2faData.ajaxURL,data:{action:actionToRun,_wpnonce:nonceValue},complete:function(data){jQuery('#backup-codes-wrapper').slideUp(0);jQuery('.wp2fa-modal.is-open #backup-codes-wrapper, .wp2fa-setup-content #backup-codes-wrapper').val('');var codes=jQuery.parseJSON(data.responseText);var codes=codes.data['codes'];jQuery.each(codes,function(index,value){var oldValue=jQuery('.wp2fa-modal.is-open #backup-codes-wrapper, .wp2fa-setup-content #backup-codes-wrapper').val();var counter=index+1;jQuery('.wp2fa-modal.is-open #backup-codes-wrapper, .wp2fa-setup-content #backup-codes-wrapper').val(oldValue+counter+': '+`${value} \n`);});jQuery('#backup-codes-wrapper').slideDown(500);jQuery('.close-wizard-link').text(wp2faWizardData.readyText).fadeIn(50);}},);});jQuery('body').on('click','.button[data-trigger-reset-key]',function(e){e.preventDefault();if(jQuery('.qr-code-wrapper').length){jQuery('.qr-code-wrapper').addClass('regenerating');}
var doReload=jQuery(this).attr('data-trigger-reset-key');const thisButton=jQuery(this);const actionToRun='regenerate_authentication_key';const nonceValue=jQuery(this).attr('data-nonce');const userID=jQuery(this).attr('data-user-id');jQuery.ajax({type:'POST',dataType:'json',url:wp2faData.ajaxURL,data:{action:actionToRun,_wpnonce:nonceValue,user_id:userID},complete:function(data){if(jQuery('.change-2fa-confirm.hidden').length){jQuery('.change-2fa-confirm.hidden').trigger('click');}
if(jQuery('.app-key').length){jQuery('#wp-2fa-totp-qrcode').attr('src',data.responseJSON.data['qr']);jQuery('.app-key').val(data.responseJSON.data['key']);jQuery('[name="wp-2fa-totp-key"]').val(data.responseJSON.data['key']);setTimeout(function(){jQuery('.qr-code-wrapper').removeClass('regenerating');},500);}}},);});jQuery('body').on('click','.button[data-trigger-backup-code-email]',function(e){e.preventDefault();const thisButton=jQuery(this);const actionToRun='send_backup_codes_email';const nonceValue=jQuery(this).attr('data-nonce');const userID=jQuery(this).attr('data-user-id');const codesWrapper=JSON.stringify(jQuery('.active #backup-codes-wrapper').val());jQuery.ajax({type:'POST',dataType:'json',url:wp2faData.ajaxURL,data:{action:actionToRun,_wpnonce:nonceValue,user_id:userID,codes:codesWrapper},complete:function(data){jQuery('.button[data-trigger-backup-code-email]').text(wp2faWizardData.backupCodesSent).attr('value',wp2faWizardData.backupCodesSent);}},);});jQuery('body').on('click','.click-to-copy',function(e){var copyText=jQuery(this).prev();copyText.select();if(typeof copyText.setSelectionRange!=="undefined"){copyText.setSelectionRange(0,99999);}
navigator.clipboard.writeText(copyText[0].value);jQuery(this).addClass('done').html('Copied');});jQuery('body').on('click','.button[data-trigger-backup-code-copy]',function(e){e.preventDefault();var copyText=jQuery('.active #backup-codes-wrapper');copyText.select();if(typeof copyText.setSelectionRange!=="undefined"){copyText.setSelectionRange(0,99999);}
navigator.clipboard.writeText(copyText[0].value);jQuery(this).addClass('done').html('Copied');});jQuery('body').on('click','.button[data-trigger-backup-code-download]',function(e){e.preventDefault();const userName=jQuery(this).attr('data-user');const websiteURL=jQuery(this).attr('data-website-url');const preamble=`${wp2faWizardData.codesPreamble} ${userName} on the website ${websiteURL}:\n\n`;var codesWrapper=jQuery('.active #backup-codes-wrapper').val().split(' ').join('\n');download('backup_codes.txt',preamble+codesWrapper);});jQuery('body').on('click','.button[data-trigger-print]',function(e){e.preventDefault();const userName=jQuery(this).attr('data-user-id');const websiteURL=jQuery(this).attr('data-website-url');const preamble=`${wp2faWizardData.codesPreamble} ${userName} on the website ${websiteURL}:\n\n`;const divToPrint=jQuery('.active #backup-codes-wrapper').val();const newWin=window.open('','Print-Window');newWin.document.open();newWin.document.write(`<html><body onload="window.print()">${preamble}</br></br>${divToPrint}</body></html>`);newWin.document.close();setTimeout(function(){newWin.close();},10);});function download(filename,text){const element=document.createElement('a');element.setAttribute('href',`data:text/plain;charset=utf-8,${encodeURIComponent( text )}`);element.setAttribute('download',filename);element.style.display='none';document.body.appendChild(element);element.click();document.body.removeChild(element);}
jQuery(document).on('click','#custom-email-address',function(){jQuery('#use_custom_email').prop('checked',true);});jQuery(document).on('click','#custom-oob-email-address',function(){jQuery('#use_custom_oob_email').prop('checked',true);});jQuery(document).on('click','[data-check-on-click]',function(){const thingToCheck=jQuery(this).attr('data-check-on-click');jQuery(thingToCheck).prop('checked',true);});jQuery(document).on('click','[data-trigger-submit-form]',function(e){e.preventDefault();const thingToSubmit=jQuery(this).attr('data-trigger-submit-form');jQuery('.change-2fa-confirm').trigger('click');});jQuery(document).on('click','[data-reload]',function(e){removeShowParam();});window.removeShowParam=function(){let url=new URL(location.href);let params=new URLSearchParams(url.search);params.delete('show');location.replace(`${location.pathname}?${params}`);}
jQuery('[name="wp_2fa_settings[enforcement-policy]"]').on("input",function(){if(jQuery('input[name="wp_2fa_settings[enforcement-policy]"]:checked').val()!=='do-not-enforce'){jQuery('[data-step-title="Exclude users"]').removeClass('hidden');updateStepTitles();}else{jQuery('[data-step-title="Exclude users"]').addClass('hidden');updateStepTitles();}});jQuery('body').on('click','.iti__flag-container',function(e){var isExpand=(jQuery('.iti__selected-flag').attr('aria-expanded'))?'expand-panel':'';jQuery(this).closest('.wizard-step.active').toggleClass(isExpand);});jQuery('body').on('click','.step-setting-wrapper #all-users, .step-setting-wrapper #certain-roles-only',function(e){jQuery('.step-setting-wrapper.active .continue-wizard').removeClass('hidden');jQuery('.step-setting-wrapper.active .save-wizard').addClass('hidden');});jQuery('body').on('click','.step-setting-wrapper #do-not-enforce',function(e){jQuery('.step-setting-wrapper.active .continue-wizard').addClass('hidden');jQuery('.step-setting-wrapper.active .save-wizard').removeClass('hidden');});});window.onresize=function(){checkWizardOffset();}
function checkWizardOffset(){var elem=document.querySelector('.setup-wizard-wrapper');if(elem){var bounding=elem.getBoundingClientRect();if(bounding.top<0){var clientHeight=bounding.height / 2;elem.style.cssText+='top: '+clientHeight+'px';}}}
window.wp2fa_fireWizard=function(){jQuery('.verification-response span').remove();jQuery('#configure-2fa .wizard-step.active, #configure-2fa .step-setting-wrapper.active').removeClass('active');jQuery('#configure-2fa .wizard-step:first-of-type, #configure-2fa .step-setting-wrapper:first-of-type').addClass('active');jQuery('.modal__content input:not([type="radio"]):not([type="hidden"])').not('.app-key').val('');MicroModal.show('configure-2fa');if(jQuery('input#basic').is(':visible')){jQuery('input#basic').trigger("click");}else{if(jQuery('input#geek').is(':visible')){jQuery('input#geek').trigger("click");}else{if(jQuery('input#oob').length){jQuery('input#oob').trigger("click");}else if(jQuery('input#authy').length){jQuery('input#authy').trigger("click");}else if(jQuery('input#twilio').length){jQuery('input#twilio').trigger("click");}}}
jQuery('[name="wp_2fa_enabled_methods"]').change();if(1===jQuery('.wizard-step.active .option-pill').length){jQuery('.wp-2fa-button-primary.2fa-choose-method').trigger("click");jQuery('#configure-2fa .wizard-step:first-of-type, #configure-2fa input:radio[name=wp_2fa_enabled_methods]:first').attr("checked",true);}else{jQuery('#configure-2fa .wizard-step:first-of-type, #configure-2fa input:radio[name=wp_2fa_enabled_methods]:first').prop("checked",true);jQuery('[name="wp_2fa_enabled_methods"]').change();jQuery('#configure-2fa .wizard-step:first-of-type, #configure-2fa input:radio[name=wp_2fa_enabled_methods]:first').trigger("click");}};
}
catch(e){console.error("An error has occurred common.js: "+e.stack);}

try{
jQuery(function(){const select2Autocomplete=function(source,functionName){jQuery(source).select2({width:'resolve',ajax:{url:`${wp2faData.ajaxURL}?wp_2fa_nonce=${wp2faData.nonce}`,dataType:'json',delay:250,data:function(params){return{term:params.term,action:functionName};},processResults:function(data){var exclData=[];if(source==='#excluded-users-multi-select'){exclData=jQuery('#enforced_users-multi-select').val();}else if(source==='#enforced_users-multi-select'){exclData=jQuery('#excluded-users-multi-select').val();}else if(source==='#excluded-roles-multi-select'){exclData=jQuery('#enforced-roles-multi-select').val();}else if(source==='#enforced-roles-multi-select'){exclData=jQuery('#excluded-roles-multi-select').val();}else if(source==='#excluded-sites-multi-select'){exclData=jQuery('#enforced-sites-multi-select').val();}else if(source==='#enforced-sites-multi-select'){exclData=jQuery('#excluded-sites-multi-select').val();}
const options=[];if(data){jQuery.each(data,function(index,text){if(exclData.indexOf(text['label'])===-1){options.push({id:text['label'],text:text['value']});}});}
return{results:options};},cache:true},minimumInputLength:2});};if(jQuery('#excluded-users-multi-select').length){select2Autocomplete('#excluded-users-multi-select','wp_2fa_get_all_users');}
if(jQuery('#enforced_users-multi-select').length){select2Autocomplete('#enforced_users-multi-select','wp_2fa_get_all_users');}
if(jQuery('#excluded-roles-multi-select').length){select2Autocomplete('#excluded-roles-multi-select','wp_2fa_get_all_roles');}
if(jQuery('#enforced-roles-multi-select').length){select2Autocomplete('#enforced-roles-multi-select','wp_2fa_get_all_roles');}
if(jQuery('#excluded-sites-multi-select').length){select2Autocomplete('#excluded-sites-multi-select','wp_2fa_get_all_network_sites');}
if(jQuery('#enforced-sites-multi-select').length){select2Autocomplete('#enforced-sites-multi-select','wp_2fa_get_all_network_sites');}});
}
catch(e){console.error("An error has occurred select2control.js: "+e.stack);}
dist/js/select2.min.js000064400000202150150755130600010606 0ustar00/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):a("object"==typeof exports?require("jquery"):jQuery)}(function(a){var b=function(){if(a&&a.fn&&a.fn.select2&&a.fn.select2.amd)var b=a.fn.select2.amd;var b;return function(){if(!b||!b.requirejs){b?c=b:b={};var a,c,d;!function(b){function e(a,b){return u.call(a,b)}function f(a,b){var c,d,e,f,g,h,i,j,k,l,m,n=b&&b.split("/"),o=s.map,p=o&&o["*"]||{};if(a&&"."===a.charAt(0))if(b){for(a=a.split("/"),g=a.length-1,s.nodeIdCompat&&w.test(a[g])&&(a[g]=a[g].replace(w,"")),a=n.slice(0,n.length-1).concat(a),k=0;k<a.length;k+=1)if(m=a[k],"."===m)a.splice(k,1),k-=1;else if(".."===m){if(1===k&&(".."===a[2]||".."===a[0]))break;k>0&&(a.splice(k-1,2),k-=2)}a=a.join("/")}else 0===a.indexOf("./")&&(a=a.substring(2));if((n||p)&&o){for(c=a.split("/"),k=c.length;k>0;k-=1){if(d=c.slice(0,k).join("/"),n)for(l=n.length;l>0;l-=1)if(e=o[n.slice(0,l).join("/")],e&&(e=e[d])){f=e,h=k;break}if(f)break;!i&&p&&p[d]&&(i=p[d],j=k)}!f&&i&&(f=i,h=j),f&&(c.splice(0,h,f),a=c.join("/"))}return a}function g(a,c){return function(){var d=v.call(arguments,0);return"string"!=typeof d[0]&&1===d.length&&d.push(null),n.apply(b,d.concat([a,c]))}}function h(a){return function(b){return f(b,a)}}function i(a){return function(b){q[a]=b}}function j(a){if(e(r,a)){var c=r[a];delete r[a],t[a]=!0,m.apply(b,c)}if(!e(q,a)&&!e(t,a))throw new Error("No "+a);return q[a]}function k(a){var b,c=a?a.indexOf("!"):-1;return c>-1&&(b=a.substring(0,c),a=a.substring(c+1,a.length)),[b,a]}function l(a){return function(){return s&&s.config&&s.config[a]||{}}}var m,n,o,p,q={},r={},s={},t={},u=Object.prototype.hasOwnProperty,v=[].slice,w=/\.js$/;o=function(a,b){var c,d=k(a),e=d[0];return a=d[1],e&&(e=f(e,b),c=j(e)),e?a=c&&c.normalize?c.normalize(a,h(b)):f(a,b):(a=f(a,b),d=k(a),e=d[0],a=d[1],e&&(c=j(e))),{f:e?e+"!"+a:a,n:a,pr:e,p:c}},p={require:function(a){return g(a)},exports:function(a){var b=q[a];return"undefined"!=typeof b?b:q[a]={}},module:function(a){return{id:a,uri:"",exports:q[a],config:l(a)}}},m=function(a,c,d,f){var h,k,l,m,n,s,u=[],v=typeof d;if(f=f||a,"undefined"===v||"function"===v){for(c=!c.length&&d.length?["require","exports","module"]:c,n=0;n<c.length;n+=1)if(m=o(c[n],f),k=m.f,"require"===k)u[n]=p.require(a);else if("exports"===k)u[n]=p.exports(a),s=!0;else if("module"===k)h=u[n]=p.module(a);else if(e(q,k)||e(r,k)||e(t,k))u[n]=j(k);else{if(!m.p)throw new Error(a+" missing "+k);m.p.load(m.n,g(f,!0),i(k),{}),u[n]=q[k]}l=d?d.apply(q[a],u):void 0,a&&(h&&h.exports!==b&&h.exports!==q[a]?q[a]=h.exports:l===b&&s||(q[a]=l))}else a&&(q[a]=d)},a=c=n=function(a,c,d,e,f){if("string"==typeof a)return p[a]?p[a](c):j(o(a,c).f);if(!a.splice){if(s=a,s.deps&&n(s.deps,s.callback),!c)return;c.splice?(a=c,c=d,d=null):a=b}return c=c||function(){},"function"==typeof d&&(d=e,e=f),e?m(b,a,c,d):setTimeout(function(){m(b,a,c,d)},4),n},n.config=function(a){return n(a)},a._defined=q,d=function(a,b,c){if("string"!=typeof a)throw new Error("See almond README: incorrect module build, no module name");b.splice||(c=b,b=[]),e(q,a)||e(r,a)||(r[a]=[a,b,c])},d.amd={jQuery:!0}}(),b.requirejs=a,b.require=c,b.define=d}}(),b.define("almond",function(){}),b.define("jquery",[],function(){var b=a||$;return null==b&&console&&console.error&&console.error("Select2: An instance of jQuery or a jQuery-compatible library was not found. Make sure that you are including jQuery before Select2 on your web page."),b}),b.define("select2/utils",["jquery"],function(a){function b(a){var b=a.prototype,c=[];for(var d in b){var e=b[d];"function"==typeof e&&"constructor"!==d&&c.push(d)}return c}var c={};c.Extend=function(a,b){function c(){this.constructor=a}var d={}.hasOwnProperty;for(var e in b)d.call(b,e)&&(a[e]=b[e]);return c.prototype=b.prototype,a.prototype=new c,a.__super__=b.prototype,a},c.Decorate=function(a,c){function d(){var b=Array.prototype.unshift,d=c.prototype.constructor.length,e=a.prototype.constructor;d>0&&(b.call(arguments,a.prototype.constructor),e=c.prototype.constructor),e.apply(this,arguments)}function e(){this.constructor=d}var f=b(c),g=b(a);c.displayName=a.displayName,d.prototype=new e;for(var h=0;h<g.length;h++){var i=g[h];d.prototype[i]=a.prototype[i]}for(var j=(function(a){var b=function(){};a in d.prototype&&(b=d.prototype[a]);var e=c.prototype[a];return function(){var a=Array.prototype.unshift;return a.call(arguments,b),e.apply(this,arguments)}}),k=0;k<f.length;k++){var l=f[k];d.prototype[l]=j(l)}return d};var d=function(){this.listeners={}};return d.prototype.on=function(a,b){this.listeners=this.listeners||{},a in this.listeners?this.listeners[a].push(b):this.listeners[a]=[b]},d.prototype.trigger=function(a){var b=Array.prototype.slice,c=b.call(arguments,1);this.listeners=this.listeners||{},null==c&&(c=[]),0===c.length&&c.push({}),c[0]._type=a,a in this.listeners&&this.invoke(this.listeners[a],b.call(arguments,1)),"*"in this.listeners&&this.invoke(this.listeners["*"],arguments)},d.prototype.invoke=function(a,b){for(var c=0,d=a.length;d>c;c++)a[c].apply(this,b)},c.Observable=d,c.generateChars=function(a){for(var b="",c=0;a>c;c++){var d=Math.floor(36*Math.random());b+=d.toString(36)}return b},c.bind=function(a,b){return function(){a.apply(b,arguments)}},c._convertData=function(a){for(var b in a){var c=b.split("-"),d=a;if(1!==c.length){for(var e=0;e<c.length;e++){var f=c[e];f=f.substring(0,1).toLowerCase()+f.substring(1),f in d||(d[f]={}),e==c.length-1&&(d[f]=a[b]),d=d[f]}delete a[b]}}return a},c.hasScroll=function(b,c){var d=a(c),e=c.style.overflowX,f=c.style.overflowY;return e!==f||"hidden"!==f&&"visible"!==f?"scroll"===e||"scroll"===f?!0:d.innerHeight()<c.scrollHeight||d.innerWidth()<c.scrollWidth:!1},c.escapeMarkup=function(a){var b={"\\":"&#92;","&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#47;"};return"string"!=typeof a?a:String(a).replace(/[&<>"'\/\\]/g,function(a){return b[a]})},c.appendMany=function(b,c){if("1.7"===a.fn.jquery.substr(0,3)){var d=a();a.map(c,function(a){d=d.add(a)}),c=d}b.append(c)},c}),b.define("select2/results",["jquery","./utils"],function(a,b){function c(a,b,d){this.$element=a,this.data=d,this.options=b,c.__super__.constructor.call(this)}return b.Extend(c,b.Observable),c.prototype.render=function(){var b=a('<ul class="select2-results__options" role="tree"></ul>');return this.options.get("multiple")&&b.attr("aria-multiselectable","true"),this.$results=b,b},c.prototype.clear=function(){this.$results.empty()},c.prototype.displayMessage=function(b){var c=this.options.get("escapeMarkup");this.clear(),this.hideLoading();var d=a('<li role="treeitem" aria-live="assertive" class="select2-results__option"></li>'),e=this.options.get("translations").get(b.message);d.append(c(e(b.args))),d[0].className+=" select2-results__message",this.$results.append(d)},c.prototype.hideMessages=function(){this.$results.find(".select2-results__message").remove()},c.prototype.append=function(a){this.hideLoading();var b=[];if(null==a.results||0===a.results.length)return void(0===this.$results.children().length&&this.trigger("results:message",{message:"noResults"}));a.results=this.sort(a.results);for(var c=0;c<a.results.length;c++){var d=a.results[c],e=this.option(d);b.push(e)}this.$results.append(b)},c.prototype.position=function(a,b){var c=b.find(".select2-results");c.append(a)},c.prototype.sort=function(a){var b=this.options.get("sorter");return b(a)},c.prototype.highlightFirstItem=function(){var a=this.$results.find(".select2-results__option[aria-selected]"),b=a.filter("[aria-selected=true]");b.length>0?b.first().trigger("mouseenter"):a.first().trigger("mouseenter"),this.ensureHighlightVisible()},c.prototype.setClasses=function(){var b=this;this.data.current(function(c){var d=a.map(c,function(a){return a.id.toString()}),e=b.$results.find(".select2-results__option[aria-selected]");e.each(function(){var b=a(this),c=a.data(this,"data"),e=""+c.id;null!=c.element&&c.element.selected||null==c.element&&a.inArray(e,d)>-1?b.attr("aria-selected","true"):b.attr("aria-selected","false")})})},c.prototype.showLoading=function(a){this.hideLoading();var b=this.options.get("translations").get("searching"),c={disabled:!0,loading:!0,text:b(a)},d=this.option(c);d.className+=" loading-results",this.$results.prepend(d)},c.prototype.hideLoading=function(){this.$results.find(".loading-results").remove()},c.prototype.option=function(b){var c=document.createElement("li");c.className="select2-results__option";var d={role:"treeitem","aria-selected":"false"};b.disabled&&(delete d["aria-selected"],d["aria-disabled"]="true"),null==b.id&&delete d["aria-selected"],null!=b._resultId&&(c.id=b._resultId),b.title&&(c.title=b.title),b.children&&(d.role="group",d["aria-label"]=b.text,delete d["aria-selected"]);for(var e in d){var f=d[e];c.setAttribute(e,f)}if(b.children){var g=a(c),h=document.createElement("strong");h.className="select2-results__group";a(h);this.template(b,h);for(var i=[],j=0;j<b.children.length;j++){var k=b.children[j],l=this.option(k);i.push(l)}var m=a("<ul></ul>",{"class":"select2-results__options select2-results__options--nested"});m.append(i),g.append(h),g.append(m)}else this.template(b,c);return a.data(c,"data",b),c},c.prototype.bind=function(b,c){var d=this,e=b.id+"-results";this.$results.attr("id",e),b.on("results:all",function(a){d.clear(),d.append(a.data),b.isOpen()&&(d.setClasses(),d.highlightFirstItem())}),b.on("results:append",function(a){d.append(a.data),b.isOpen()&&d.setClasses()}),b.on("query",function(a){d.hideMessages(),d.showLoading(a)}),b.on("select",function(){b.isOpen()&&(d.setClasses(),d.highlightFirstItem())}),b.on("unselect",function(){b.isOpen()&&(d.setClasses(),d.highlightFirstItem())}),b.on("open",function(){d.$results.attr("aria-expanded","true"),d.$results.attr("aria-hidden","false"),d.setClasses(),d.ensureHighlightVisible()}),b.on("close",function(){d.$results.attr("aria-expanded","false"),d.$results.attr("aria-hidden","true"),d.$results.removeAttr("aria-activedescendant")}),b.on("results:toggle",function(){var a=d.getHighlightedResults();0!==a.length&&a.trigger("mouseup")}),b.on("results:select",function(){var a=d.getHighlightedResults();if(0!==a.length){var b=a.data("data");"true"==a.attr("aria-selected")?d.trigger("close",{}):d.trigger("select",{data:b})}}),b.on("results:previous",function(){var a=d.getHighlightedResults(),b=d.$results.find("[aria-selected]"),c=b.index(a);if(0!==c){var e=c-1;0===a.length&&(e=0);var f=b.eq(e);f.trigger("mouseenter");var g=d.$results.offset().top,h=f.offset().top,i=d.$results.scrollTop()+(h-g);0===e?d.$results.scrollTop(0):0>h-g&&d.$results.scrollTop(i)}}),b.on("results:next",function(){var a=d.getHighlightedResults(),b=d.$results.find("[aria-selected]"),c=b.index(a),e=c+1;if(!(e>=b.length)){var f=b.eq(e);f.trigger("mouseenter");var g=d.$results.offset().top+d.$results.outerHeight(!1),h=f.offset().top+f.outerHeight(!1),i=d.$results.scrollTop()+h-g;0===e?d.$results.scrollTop(0):h>g&&d.$results.scrollTop(i)}}),b.on("results:focus",function(a){a.element.addClass("select2-results__option--highlighted")}),b.on("results:message",function(a){d.displayMessage(a)}),a.fn.mousewheel&&this.$results.on("mousewheel",function(a){var b=d.$results.scrollTop(),c=d.$results.get(0).scrollHeight-b+a.deltaY,e=a.deltaY>0&&b-a.deltaY<=0,f=a.deltaY<0&&c<=d.$results.height();e?(d.$results.scrollTop(0),a.preventDefault(),a.stopPropagation()):f&&(d.$results.scrollTop(d.$results.get(0).scrollHeight-d.$results.height()),a.preventDefault(),a.stopPropagation())}),this.$results.on("mouseup",".select2-results__option[aria-selected]",function(b){var c=a(this),e=c.data("data");return"true"===c.attr("aria-selected")?void(d.options.get("multiple")?d.trigger("unselect",{originalEvent:b,data:e}):d.trigger("close",{})):void d.trigger("select",{originalEvent:b,data:e})}),this.$results.on("mouseenter",".select2-results__option[aria-selected]",function(b){var c=a(this).data("data");d.getHighlightedResults().removeClass("select2-results__option--highlighted"),d.trigger("results:focus",{data:c,element:a(this)})})},c.prototype.getHighlightedResults=function(){var a=this.$results.find(".select2-results__option--highlighted");return a},c.prototype.destroy=function(){this.$results.remove()},c.prototype.ensureHighlightVisible=function(){var a=this.getHighlightedResults();if(0!==a.length){var b=this.$results.find("[aria-selected]"),c=b.index(a),d=this.$results.offset().top,e=a.offset().top,f=this.$results.scrollTop()+(e-d),g=e-d;f-=2*a.outerHeight(!1),2>=c?this.$results.scrollTop(0):(g>this.$results.outerHeight()||0>g)&&this.$results.scrollTop(f)}},c.prototype.template=function(b,c){var d=this.options.get("templateResult"),e=this.options.get("escapeMarkup"),f=d(b,c);null==f?c.style.display="none":"string"==typeof f?c.innerHTML=e(f):a(c).append(f)},c}),b.define("select2/keys",[],function(){var a={BACKSPACE:8,TAB:9,ENTER:13,SHIFT:16,CTRL:17,ALT:18,ESC:27,SPACE:32,PAGE_UP:33,PAGE_DOWN:34,END:35,HOME:36,LEFT:37,UP:38,RIGHT:39,DOWN:40,DELETE:46};return a}),b.define("select2/selection/base",["jquery","../utils","../keys"],function(a,b,c){function d(a,b){this.$element=a,this.options=b,d.__super__.constructor.call(this)}return b.Extend(d,b.Observable),d.prototype.render=function(){var b=a('<span class="select2-selection" role="combobox"  aria-haspopup="true" aria-expanded="false"></span>');return this._tabindex=0,null!=this.$element.data("old-tabindex")?this._tabindex=this.$element.data("old-tabindex"):null!=this.$element.attr("tabindex")&&(this._tabindex=this.$element.attr("tabindex")),b.attr("title",this.$element.attr("title")),b.attr("tabindex",this._tabindex),this.$selection=b,b},d.prototype.bind=function(a,b){var d=this,e=(a.id+"-container",a.id+"-results");this.container=a,this.$selection.on("focus",function(a){d.trigger("focus",a)}),this.$selection.on("blur",function(a){d._handleBlur(a)}),this.$selection.on("keydown",function(a){d.trigger("keypress",a),a.which===c.SPACE&&a.preventDefault()}),a.on("results:focus",function(a){d.$selection.attr("aria-activedescendant",a.data._resultId)}),a.on("selection:update",function(a){d.update(a.data)}),a.on("open",function(){d.$selection.attr("aria-expanded","true"),d.$selection.attr("aria-owns",e),d._attachCloseHandler(a)}),a.on("close",function(){d.$selection.attr("aria-expanded","false"),d.$selection.removeAttr("aria-activedescendant"),d.$selection.removeAttr("aria-owns"),d.$selection.focus(),d._detachCloseHandler(a)}),a.on("enable",function(){d.$selection.attr("tabindex",d._tabindex)}),a.on("disable",function(){d.$selection.attr("tabindex","-1")})},d.prototype._handleBlur=function(b){var c=this;window.setTimeout(function(){document.activeElement==c.$selection[0]||a.contains(c.$selection[0],document.activeElement)||c.trigger("blur",b)},1)},d.prototype._attachCloseHandler=function(b){a(document.body).on("mousedown.select2."+b.id,function(b){var c=a(b.target),d=c.closest(".select2"),e=a(".select2.select2-container--open");e.each(function(){var b=a(this);if(this!=d[0]){var c=b.data("element");c.select2("close")}})})},d.prototype._detachCloseHandler=function(b){a(document.body).off("mousedown.select2."+b.id)},d.prototype.position=function(a,b){var c=b.find(".selection");c.append(a)},d.prototype.destroy=function(){this._detachCloseHandler(this.container)},d.prototype.update=function(a){throw new Error("The `update` method must be defined in child classes.")},d}),b.define("select2/selection/single",["jquery","./base","../utils","../keys"],function(a,b,c,d){function e(){e.__super__.constructor.apply(this,arguments)}return c.Extend(e,b),e.prototype.render=function(){var a=e.__super__.render.call(this);return a.addClass("select2-selection--single"),a.html('<span class="select2-selection__rendered"></span><span class="select2-selection__arrow" role="presentation"><b role="presentation"></b></span>'),a},e.prototype.bind=function(a,b){var c=this;e.__super__.bind.apply(this,arguments);var d=a.id+"-container";this.$selection.find(".select2-selection__rendered").attr("id",d),this.$selection.attr("aria-labelledby",d),this.$selection.on("mousedown",function(a){1===a.which&&c.trigger("toggle",{originalEvent:a})}),this.$selection.on("focus",function(a){}),this.$selection.on("blur",function(a){}),a.on("focus",function(b){a.isOpen()||c.$selection.focus()}),a.on("selection:update",function(a){c.update(a.data)})},e.prototype.clear=function(){this.$selection.find(".select2-selection__rendered").empty()},e.prototype.display=function(a,b){var c=this.options.get("templateSelection"),d=this.options.get("escapeMarkup");return d(c(a,b))},e.prototype.selectionContainer=function(){return a("<span></span>")},e.prototype.update=function(a){if(0===a.length)return void this.clear();var b=a[0],c=this.$selection.find(".select2-selection__rendered"),d=this.display(b,c);c.empty().append(d),c.prop("title",b.title||b.text)},e}),b.define("select2/selection/multiple",["jquery","./base","../utils"],function(a,b,c){function d(a,b){d.__super__.constructor.apply(this,arguments)}return c.Extend(d,b),d.prototype.render=function(){var a=d.__super__.render.call(this);return a.addClass("select2-selection--multiple"),a.html('<ul class="select2-selection__rendered"></ul>'),a},d.prototype.bind=function(b,c){var e=this;d.__super__.bind.apply(this,arguments),this.$selection.on("click",function(a){e.trigger("toggle",{originalEvent:a})}),this.$selection.on("click",".select2-selection__choice__remove",function(b){if(!e.options.get("disabled")){var c=a(this),d=c.parent(),f=d.data("data");e.trigger("unselect",{originalEvent:b,data:f})}})},d.prototype.clear=function(){this.$selection.find(".select2-selection__rendered").empty()},d.prototype.display=function(a,b){var c=this.options.get("templateSelection"),d=this.options.get("escapeMarkup");return d(c(a,b))},d.prototype.selectionContainer=function(){var b=a('<li class="select2-selection__choice"><span class="select2-selection__choice__remove" role="presentation">&times;</span></li>');return b},d.prototype.update=function(a){if(this.clear(),0!==a.length){for(var b=[],d=0;d<a.length;d++){var e=a[d],f=this.selectionContainer(),g=this.display(e,f);f.append(g),f.prop("title",e.title||e.text),f.data("data",e),b.push(f)}var h=this.$selection.find(".select2-selection__rendered");c.appendMany(h,b)}},d}),b.define("select2/selection/placeholder",["../utils"],function(a){function b(a,b,c){this.placeholder=this.normalizePlaceholder(c.get("placeholder")),a.call(this,b,c)}return b.prototype.normalizePlaceholder=function(a,b){return"string"==typeof b&&(b={id:"",text:b}),b},b.prototype.createPlaceholder=function(a,b){var c=this.selectionContainer();return c.html(this.display(b)),c.addClass("select2-selection__placeholder").removeClass("select2-selection__choice"),c},b.prototype.update=function(a,b){var c=1==b.length&&b[0].id!=this.placeholder.id,d=b.length>1;if(d||c)return a.call(this,b);this.clear();var e=this.createPlaceholder(this.placeholder);this.$selection.find(".select2-selection__rendered").append(e)},b}),b.define("select2/selection/allowClear",["jquery","../keys"],function(a,b){function c(){}return c.prototype.bind=function(a,b,c){var d=this;a.call(this,b,c),null==this.placeholder&&this.options.get("debug")&&window.console&&console.error&&console.error("Select2: The `allowClear` option should be used in combination with the `placeholder` option."),this.$selection.on("mousedown",".select2-selection__clear",function(a){d._handleClear(a)}),b.on("keypress",function(a){d._handleKeyboardClear(a,b)})},c.prototype._handleClear=function(a,b){if(!this.options.get("disabled")){var c=this.$selection.find(".select2-selection__clear");if(0!==c.length){b.stopPropagation();for(var d=c.data("data"),e=0;e<d.length;e++){var f={data:d[e]};if(this.trigger("unselect",f),f.prevented)return}this.$element.val(this.placeholder.id).trigger("change"),this.trigger("toggle",{})}}},c.prototype._handleKeyboardClear=function(a,c,d){d.isOpen()||(c.which==b.DELETE||c.which==b.BACKSPACE)&&this._handleClear(c)},c.prototype.update=function(b,c){if(b.call(this,c),!(this.$selection.find(".select2-selection__placeholder").length>0||0===c.length)){var d=a('<span class="select2-selection__clear">&times;</span>');d.data("data",c),this.$selection.find(".select2-selection__rendered").prepend(d)}},c}),b.define("select2/selection/search",["jquery","../utils","../keys"],function(a,b,c){function d(a,b,c){a.call(this,b,c)}return d.prototype.render=function(b){var c=a('<li class="select2-search select2-search--inline"><input class="select2-search__field" type="search" tabindex="-1" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" role="textbox" aria-autocomplete="list" /></li>');this.$searchContainer=c,this.$search=c.find("input");var d=b.call(this);return this._transferTabIndex(),d},d.prototype.bind=function(a,b,d){var e=this;a.call(this,b,d),b.on("open",function(){e.$search.trigger("focus")}),b.on("close",function(){e.$search.val(""),e.$search.removeAttr("aria-activedescendant"),e.$search.trigger("focus")}),b.on("enable",function(){e.$search.prop("disabled",!1),e._transferTabIndex()}),b.on("disable",function(){e.$search.prop("disabled",!0)}),b.on("focus",function(a){e.$search.trigger("focus")}),b.on("results:focus",function(a){e.$search.attr("aria-activedescendant",a.id)}),this.$selection.on("focusin",".select2-search--inline",function(a){e.trigger("focus",a)}),this.$selection.on("focusout",".select2-search--inline",function(a){e._handleBlur(a)}),this.$selection.on("keydown",".select2-search--inline",function(a){a.stopPropagation(),e.trigger("keypress",a),e._keyUpPrevented=a.isDefaultPrevented();var b=a.which;if(b===c.BACKSPACE&&""===e.$search.val()){var d=e.$searchContainer.prev(".select2-selection__choice");if(d.length>0){var f=d.data("data");e.searchRemoveChoice(f),a.preventDefault()}}});var f=document.documentMode,g=f&&11>=f;this.$selection.on("input.searchcheck",".select2-search--inline",function(a){return g?void e.$selection.off("input.search input.searchcheck"):void e.$selection.off("keyup.search")}),this.$selection.on("keyup.search input.search",".select2-search--inline",function(a){if(g&&"input"===a.type)return void e.$selection.off("input.search input.searchcheck");var b=a.which;b!=c.SHIFT&&b!=c.CTRL&&b!=c.ALT&&b!=c.TAB&&e.handleSearch(a)})},d.prototype._transferTabIndex=function(a){this.$search.attr("tabindex",this.$selection.attr("tabindex")),this.$selection.attr("tabindex","-1")},d.prototype.createPlaceholder=function(a,b){this.$search.attr("placeholder",b.text)},d.prototype.update=function(a,b){var c=this.$search[0]==document.activeElement;this.$search.attr("placeholder",""),a.call(this,b),this.$selection.find(".select2-selection__rendered").append(this.$searchContainer),this.resizeSearch(),c&&this.$search.focus()},d.prototype.handleSearch=function(){if(this.resizeSearch(),!this._keyUpPrevented){var a=this.$search.val();this.trigger("query",{term:a})}this._keyUpPrevented=!1},d.prototype.searchRemoveChoice=function(a,b){this.trigger("unselect",{data:b}),this.$search.val(b.text),this.handleSearch()},d.prototype.resizeSearch=function(){this.$search.css("width","25px");var a="";if(""!==this.$search.attr("placeholder"))a=this.$selection.find(".select2-selection__rendered").innerWidth();else{var b=this.$search.val().length+1;a=.75*b+"em"}this.$search.css("width",a)},d}),b.define("select2/selection/eventRelay",["jquery"],function(a){function b(){}return b.prototype.bind=function(b,c,d){var e=this,f=["open","opening","close","closing","select","selecting","unselect","unselecting"],g=["opening","closing","selecting","unselecting"];b.call(this,c,d),c.on("*",function(b,c){if(-1!==a.inArray(b,f)){c=c||{};var d=a.Event("select2:"+b,{params:c});e.$element.trigger(d),-1!==a.inArray(b,g)&&(c.prevented=d.isDefaultPrevented())}})},b}),b.define("select2/translation",["jquery","require"],function(a,b){function c(a){this.dict=a||{}}return c.prototype.all=function(){return this.dict},c.prototype.get=function(a){return this.dict[a]},c.prototype.extend=function(b){this.dict=a.extend({},b.all(),this.dict)},c._cache={},c.loadPath=function(a){if(!(a in c._cache)){var d=b(a);c._cache[a]=d}return new c(c._cache[a])},c}),b.define("select2/diacritics",[],function(){var a={"Ⓐ":"A","A":"A","À":"A","Á":"A","Â":"A","Ầ":"A","Ấ":"A","Ẫ":"A","Ẩ":"A","Ã":"A","Ā":"A","Ă":"A","Ằ":"A","Ắ":"A","Ẵ":"A","Ẳ":"A","Ȧ":"A","Ǡ":"A","Ä":"A","Ǟ":"A","Ả":"A","Å":"A","Ǻ":"A","Ǎ":"A","Ȁ":"A","Ȃ":"A","Ạ":"A","Ậ":"A","Ặ":"A","Ḁ":"A","Ą":"A","Ⱥ":"A","Ɐ":"A","Ꜳ":"AA","Æ":"AE","Ǽ":"AE","Ǣ":"AE","Ꜵ":"AO","Ꜷ":"AU","Ꜹ":"AV","Ꜻ":"AV","Ꜽ":"AY","Ⓑ":"B","B":"B","Ḃ":"B","Ḅ":"B","Ḇ":"B","Ƀ":"B","Ƃ":"B","Ɓ":"B","Ⓒ":"C","C":"C","Ć":"C","Ĉ":"C","Ċ":"C","Č":"C","Ç":"C","Ḉ":"C","Ƈ":"C","Ȼ":"C","Ꜿ":"C","Ⓓ":"D","D":"D","Ḋ":"D","Ď":"D","Ḍ":"D","Ḑ":"D","Ḓ":"D","Ḏ":"D","Đ":"D","Ƌ":"D","Ɗ":"D","Ɖ":"D","Ꝺ":"D","DZ":"DZ","DŽ":"DZ","Dz":"Dz","Dž":"Dz","Ⓔ":"E","E":"E","È":"E","É":"E","Ê":"E","Ề":"E","Ế":"E","Ễ":"E","Ể":"E","Ẽ":"E","Ē":"E","Ḕ":"E","Ḗ":"E","Ĕ":"E","Ė":"E","Ë":"E","Ẻ":"E","Ě":"E","Ȅ":"E","Ȇ":"E","Ẹ":"E","Ệ":"E","Ȩ":"E","Ḝ":"E","Ę":"E","Ḙ":"E","Ḛ":"E","Ɛ":"E","Ǝ":"E","Ⓕ":"F","F":"F","Ḟ":"F","Ƒ":"F","Ꝼ":"F","Ⓖ":"G","G":"G","Ǵ":"G","Ĝ":"G","Ḡ":"G","Ğ":"G","Ġ":"G","Ǧ":"G","Ģ":"G","Ǥ":"G","Ɠ":"G","Ꞡ":"G","Ᵹ":"G","Ꝿ":"G","Ⓗ":"H","H":"H","Ĥ":"H","Ḣ":"H","Ḧ":"H","Ȟ":"H","Ḥ":"H","Ḩ":"H","Ḫ":"H","Ħ":"H","Ⱨ":"H","Ⱶ":"H","Ɥ":"H","Ⓘ":"I","I":"I","Ì":"I","Í":"I","Î":"I","Ĩ":"I","Ī":"I","Ĭ":"I","İ":"I","Ï":"I","Ḯ":"I","Ỉ":"I","Ǐ":"I","Ȉ":"I","Ȋ":"I","Ị":"I","Į":"I","Ḭ":"I","Ɨ":"I","Ⓙ":"J","J":"J","Ĵ":"J","Ɉ":"J","Ⓚ":"K","K":"K","Ḱ":"K","Ǩ":"K","Ḳ":"K","Ķ":"K","Ḵ":"K","Ƙ":"K","Ⱪ":"K","Ꝁ":"K","Ꝃ":"K","Ꝅ":"K","Ꞣ":"K","Ⓛ":"L","L":"L","Ŀ":"L","Ĺ":"L","Ľ":"L","Ḷ":"L","Ḹ":"L","Ļ":"L","Ḽ":"L","Ḻ":"L","Ł":"L","Ƚ":"L","Ɫ":"L","Ⱡ":"L","Ꝉ":"L","Ꝇ":"L","Ꞁ":"L","LJ":"LJ","Lj":"Lj","Ⓜ":"M","M":"M","Ḿ":"M","Ṁ":"M","Ṃ":"M","Ɱ":"M","Ɯ":"M","Ⓝ":"N","N":"N","Ǹ":"N","Ń":"N","Ñ":"N","Ṅ":"N","Ň":"N","Ṇ":"N","Ņ":"N","Ṋ":"N","Ṉ":"N","Ƞ":"N","Ɲ":"N","Ꞑ":"N","Ꞥ":"N","NJ":"NJ","Nj":"Nj","Ⓞ":"O","O":"O","Ò":"O","Ó":"O","Ô":"O","Ồ":"O","Ố":"O","Ỗ":"O","Ổ":"O","Õ":"O","Ṍ":"O","Ȭ":"O","Ṏ":"O","Ō":"O","Ṑ":"O","Ṓ":"O","Ŏ":"O","Ȯ":"O","Ȱ":"O","Ö":"O","Ȫ":"O","Ỏ":"O","Ő":"O","Ǒ":"O","Ȍ":"O","Ȏ":"O","Ơ":"O","Ờ":"O","Ớ":"O","Ỡ":"O","Ở":"O","Ợ":"O","Ọ":"O","Ộ":"O","Ǫ":"O","Ǭ":"O","Ø":"O","Ǿ":"O","Ɔ":"O","Ɵ":"O","Ꝋ":"O","Ꝍ":"O","Ƣ":"OI","Ꝏ":"OO","Ȣ":"OU","Ⓟ":"P","P":"P","Ṕ":"P","Ṗ":"P","Ƥ":"P","Ᵽ":"P","Ꝑ":"P","Ꝓ":"P","Ꝕ":"P","Ⓠ":"Q","Q":"Q","Ꝗ":"Q","Ꝙ":"Q","Ɋ":"Q","Ⓡ":"R","R":"R","Ŕ":"R","Ṙ":"R","Ř":"R","Ȑ":"R","Ȓ":"R","Ṛ":"R","Ṝ":"R","Ŗ":"R","Ṟ":"R","Ɍ":"R","Ɽ":"R","Ꝛ":"R","Ꞧ":"R","Ꞃ":"R","Ⓢ":"S","S":"S","ẞ":"S","Ś":"S","Ṥ":"S","Ŝ":"S","Ṡ":"S","Š":"S","Ṧ":"S","Ṣ":"S","Ṩ":"S","Ș":"S","Ş":"S","Ȿ":"S","Ꞩ":"S","Ꞅ":"S","Ⓣ":"T","T":"T","Ṫ":"T","Ť":"T","Ṭ":"T","Ț":"T","Ţ":"T","Ṱ":"T","Ṯ":"T","Ŧ":"T","Ƭ":"T","Ʈ":"T","Ⱦ":"T","Ꞇ":"T","Ꜩ":"TZ","Ⓤ":"U","U":"U","Ù":"U","Ú":"U","Û":"U","Ũ":"U","Ṹ":"U","Ū":"U","Ṻ":"U","Ŭ":"U","Ü":"U","Ǜ":"U","Ǘ":"U","Ǖ":"U","Ǚ":"U","Ủ":"U","Ů":"U","Ű":"U","Ǔ":"U","Ȕ":"U","Ȗ":"U","Ư":"U","Ừ":"U","Ứ":"U","Ữ":"U","Ử":"U","Ự":"U","Ụ":"U","Ṳ":"U","Ų":"U","Ṷ":"U","Ṵ":"U","Ʉ":"U","Ⓥ":"V","V":"V","Ṽ":"V","Ṿ":"V","Ʋ":"V","Ꝟ":"V","Ʌ":"V","Ꝡ":"VY","Ⓦ":"W","W":"W","Ẁ":"W","Ẃ":"W","Ŵ":"W","Ẇ":"W","Ẅ":"W","Ẉ":"W","Ⱳ":"W","Ⓧ":"X","X":"X","Ẋ":"X","Ẍ":"X","Ⓨ":"Y","Y":"Y","Ỳ":"Y","Ý":"Y","Ŷ":"Y","Ỹ":"Y","Ȳ":"Y","Ẏ":"Y","Ÿ":"Y","Ỷ":"Y","Ỵ":"Y","Ƴ":"Y","Ɏ":"Y","Ỿ":"Y","Ⓩ":"Z","Z":"Z","Ź":"Z","Ẑ":"Z","Ż":"Z","Ž":"Z","Ẓ":"Z","Ẕ":"Z","Ƶ":"Z","Ȥ":"Z","Ɀ":"Z","Ⱬ":"Z","Ꝣ":"Z","ⓐ":"a","a":"a","ẚ":"a","à":"a","á":"a","â":"a","ầ":"a","ấ":"a","ẫ":"a","ẩ":"a","ã":"a","ā":"a","ă":"a","ằ":"a","ắ":"a","ẵ":"a","ẳ":"a","ȧ":"a","ǡ":"a","ä":"a","ǟ":"a","ả":"a","å":"a","ǻ":"a","ǎ":"a","ȁ":"a","ȃ":"a","ạ":"a","ậ":"a","ặ":"a","ḁ":"a","ą":"a","ⱥ":"a","ɐ":"a","ꜳ":"aa","æ":"ae","ǽ":"ae","ǣ":"ae","ꜵ":"ao","ꜷ":"au","ꜹ":"av","ꜻ":"av","ꜽ":"ay","ⓑ":"b","b":"b","ḃ":"b","ḅ":"b","ḇ":"b","ƀ":"b","ƃ":"b","ɓ":"b","ⓒ":"c","c":"c","ć":"c","ĉ":"c","ċ":"c","č":"c","ç":"c","ḉ":"c","ƈ":"c","ȼ":"c","ꜿ":"c","ↄ":"c","ⓓ":"d","d":"d","ḋ":"d","ď":"d","ḍ":"d","ḑ":"d","ḓ":"d","ḏ":"d","đ":"d","ƌ":"d","ɖ":"d","ɗ":"d","ꝺ":"d","dz":"dz","dž":"dz","ⓔ":"e","e":"e","è":"e","é":"e","ê":"e","ề":"e","ế":"e","ễ":"e","ể":"e","ẽ":"e","ē":"e","ḕ":"e","ḗ":"e","ĕ":"e","ė":"e","ë":"e","ẻ":"e","ě":"e","ȅ":"e","ȇ":"e","ẹ":"e","ệ":"e","ȩ":"e","ḝ":"e","ę":"e","ḙ":"e","ḛ":"e","ɇ":"e","ɛ":"e","ǝ":"e","ⓕ":"f","f":"f","ḟ":"f","ƒ":"f","ꝼ":"f","ⓖ":"g","g":"g","ǵ":"g","ĝ":"g","ḡ":"g","ğ":"g","ġ":"g","ǧ":"g","ģ":"g","ǥ":"g","ɠ":"g","ꞡ":"g","ᵹ":"g","ꝿ":"g","ⓗ":"h","h":"h","ĥ":"h","ḣ":"h","ḧ":"h","ȟ":"h","ḥ":"h","ḩ":"h","ḫ":"h","ẖ":"h","ħ":"h","ⱨ":"h","ⱶ":"h","ɥ":"h","ƕ":"hv","ⓘ":"i","i":"i","ì":"i","í":"i","î":"i","ĩ":"i","ī":"i","ĭ":"i","ï":"i","ḯ":"i","ỉ":"i","ǐ":"i","ȉ":"i","ȋ":"i","ị":"i","į":"i","ḭ":"i","ɨ":"i","ı":"i","ⓙ":"j","j":"j","ĵ":"j","ǰ":"j","ɉ":"j","ⓚ":"k","k":"k","ḱ":"k","ǩ":"k","ḳ":"k","ķ":"k","ḵ":"k","ƙ":"k","ⱪ":"k","ꝁ":"k","ꝃ":"k","ꝅ":"k","ꞣ":"k","ⓛ":"l","l":"l","ŀ":"l","ĺ":"l","ľ":"l","ḷ":"l","ḹ":"l","ļ":"l","ḽ":"l","ḻ":"l","ſ":"l","ł":"l","ƚ":"l","ɫ":"l","ⱡ":"l","ꝉ":"l","ꞁ":"l","ꝇ":"l","lj":"lj","ⓜ":"m","m":"m","ḿ":"m","ṁ":"m","ṃ":"m","ɱ":"m","ɯ":"m","ⓝ":"n","n":"n","ǹ":"n","ń":"n","ñ":"n","ṅ":"n","ň":"n","ṇ":"n","ņ":"n","ṋ":"n","ṉ":"n","ƞ":"n","ɲ":"n","ʼn":"n","ꞑ":"n","ꞥ":"n","nj":"nj","ⓞ":"o","o":"o","ò":"o","ó":"o","ô":"o","ồ":"o","ố":"o","ỗ":"o","ổ":"o","õ":"o","ṍ":"o","ȭ":"o","ṏ":"o","ō":"o","ṑ":"o","ṓ":"o","ŏ":"o","ȯ":"o","ȱ":"o","ö":"o","ȫ":"o","ỏ":"o","ő":"o","ǒ":"o","ȍ":"o","ȏ":"o","ơ":"o","ờ":"o","ớ":"o","ỡ":"o","ở":"o","ợ":"o","ọ":"o","ộ":"o","ǫ":"o","ǭ":"o","ø":"o","ǿ":"o","ɔ":"o","ꝋ":"o","ꝍ":"o","ɵ":"o","ƣ":"oi","ȣ":"ou","ꝏ":"oo","ⓟ":"p","p":"p","ṕ":"p","ṗ":"p","ƥ":"p","ᵽ":"p","ꝑ":"p","ꝓ":"p","ꝕ":"p","ⓠ":"q","q":"q","ɋ":"q","ꝗ":"q","ꝙ":"q","ⓡ":"r","r":"r","ŕ":"r","ṙ":"r","ř":"r","ȑ":"r","ȓ":"r","ṛ":"r","ṝ":"r","ŗ":"r","ṟ":"r","ɍ":"r","ɽ":"r","ꝛ":"r","ꞧ":"r","ꞃ":"r","ⓢ":"s","s":"s","ß":"s","ś":"s","ṥ":"s","ŝ":"s","ṡ":"s","š":"s","ṧ":"s","ṣ":"s","ṩ":"s","ș":"s","ş":"s","ȿ":"s","ꞩ":"s","ꞅ":"s","ẛ":"s","ⓣ":"t","t":"t","ṫ":"t","ẗ":"t","ť":"t","ṭ":"t","ț":"t","ţ":"t","ṱ":"t","ṯ":"t","ŧ":"t","ƭ":"t","ʈ":"t","ⱦ":"t","ꞇ":"t","ꜩ":"tz","ⓤ":"u","u":"u","ù":"u","ú":"u","û":"u","ũ":"u","ṹ":"u","ū":"u","ṻ":"u","ŭ":"u","ü":"u","ǜ":"u","ǘ":"u","ǖ":"u","ǚ":"u","ủ":"u","ů":"u","ű":"u","ǔ":"u","ȕ":"u","ȗ":"u","ư":"u","ừ":"u","ứ":"u","ữ":"u","ử":"u","ự":"u","ụ":"u","ṳ":"u","ų":"u","ṷ":"u","ṵ":"u","ʉ":"u","ⓥ":"v","v":"v","ṽ":"v","ṿ":"v","ʋ":"v","ꝟ":"v","ʌ":"v","ꝡ":"vy","ⓦ":"w","w":"w","ẁ":"w","ẃ":"w","ŵ":"w","ẇ":"w","ẅ":"w","ẘ":"w","ẉ":"w","ⱳ":"w","ⓧ":"x","x":"x","ẋ":"x","ẍ":"x","ⓨ":"y","y":"y","ỳ":"y","ý":"y","ŷ":"y","ỹ":"y","ȳ":"y","ẏ":"y","ÿ":"y","ỷ":"y","ẙ":"y","ỵ":"y","ƴ":"y","ɏ":"y","ỿ":"y","ⓩ":"z","z":"z","ź":"z","ẑ":"z","ż":"z","ž":"z","ẓ":"z","ẕ":"z","ƶ":"z","ȥ":"z","ɀ":"z","ⱬ":"z","ꝣ":"z","Ά":"Α","Έ":"Ε","Ή":"Η","Ί":"Ι","Ϊ":"Ι","Ό":"Ο","Ύ":"Υ","Ϋ":"Υ","Ώ":"Ω","ά":"α","έ":"ε","ή":"η","ί":"ι","ϊ":"ι","ΐ":"ι","ό":"ο","ύ":"υ","ϋ":"υ","ΰ":"υ","ω":"ω","ς":"σ"};return a}),b.define("select2/data/base",["../utils"],function(a){function b(a,c){b.__super__.constructor.call(this)}return a.Extend(b,a.Observable),b.prototype.current=function(a){throw new Error("The `current` method must be defined in child classes.")},b.prototype.query=function(a,b){throw new Error("The `query` method must be defined in child classes.")},b.prototype.bind=function(a,b){},b.prototype.destroy=function(){},b.prototype.generateResultId=function(b,c){var d=b.id+"-result-";return d+=a.generateChars(4),d+=null!=c.id?"-"+c.id.toString():"-"+a.generateChars(4)},b}),b.define("select2/data/select",["./base","../utils","jquery"],function(a,b,c){function d(a,b){this.$element=a,this.options=b,d.__super__.constructor.call(this)}return b.Extend(d,a),d.prototype.current=function(a){var b=[],d=this;this.$element.find(":selected").each(function(){var a=c(this),e=d.item(a);b.push(e)}),a(b)},d.prototype.select=function(a){var b=this;if(a.selected=!0,c(a.element).is("option"))return a.element.selected=!0,void this.$element.trigger("change");
if(this.$element.prop("multiple"))this.current(function(d){var e=[];a=[a],a.push.apply(a,d);for(var f=0;f<a.length;f++){var g=a[f].id;-1===c.inArray(g,e)&&e.push(g)}b.$element.val(e),b.$element.trigger("change")});else{var d=a.id;this.$element.val(d),this.$element.trigger("change")}},d.prototype.unselect=function(a){var b=this;if(this.$element.prop("multiple"))return a.selected=!1,c(a.element).is("option")?(a.element.selected=!1,void this.$element.trigger("change")):void this.current(function(d){for(var e=[],f=0;f<d.length;f++){var g=d[f].id;g!==a.id&&-1===c.inArray(g,e)&&e.push(g)}b.$element.val(e),b.$element.trigger("change")})},d.prototype.bind=function(a,b){var c=this;this.container=a,a.on("select",function(a){c.select(a.data)}),a.on("unselect",function(a){c.unselect(a.data)})},d.prototype.destroy=function(){this.$element.find("*").each(function(){c.removeData(this,"data")})},d.prototype.query=function(a,b){var d=[],e=this,f=this.$element.children();f.each(function(){var b=c(this);if(b.is("option")||b.is("optgroup")){var f=e.item(b),g=e.matches(a,f);null!==g&&d.push(g)}}),b({results:d})},d.prototype.addOptions=function(a){b.appendMany(this.$element,a)},d.prototype.option=function(a){var b;a.children?(b=document.createElement("optgroup"),b.label=a.text):(b=document.createElement("option"),void 0!==b.textContent?b.textContent=a.text:b.innerText=a.text),a.id&&(b.value=a.id),a.disabled&&(b.disabled=!0),a.selected&&(b.selected=!0),a.title&&(b.title=a.title);var d=c(b),e=this._normalizeItem(a);return e.element=b,c.data(b,"data",e),d},d.prototype.item=function(a){var b={};if(b=c.data(a[0],"data"),null!=b)return b;if(a.is("option"))b={id:a.val(),text:a.text(),disabled:a.prop("disabled"),selected:a.prop("selected"),title:a.prop("title")};else if(a.is("optgroup")){b={text:a.prop("label"),children:[],title:a.prop("title")};for(var d=a.children("option"),e=[],f=0;f<d.length;f++){var g=c(d[f]),h=this.item(g);e.push(h)}b.children=e}return b=this._normalizeItem(b),b.element=a[0],c.data(a[0],"data",b),b},d.prototype._normalizeItem=function(a){c.isPlainObject(a)||(a={id:a,text:a}),a=c.extend({},{text:""},a);var b={selected:!1,disabled:!1};return null!=a.id&&(a.id=a.id.toString()),null!=a.text&&(a.text=a.text.toString()),null==a._resultId&&a.id&&null!=this.container&&(a._resultId=this.generateResultId(this.container,a)),c.extend({},b,a)},d.prototype.matches=function(a,b){var c=this.options.get("matcher");return c(a,b)},d}),b.define("select2/data/array",["./select","../utils","jquery"],function(a,b,c){function d(a,b){var c=b.get("data")||[];d.__super__.constructor.call(this,a,b),this.addOptions(this.convertToOptions(c))}return b.Extend(d,a),d.prototype.select=function(a){var b=this.$element.find("option").filter(function(b,c){return c.value==a.id.toString()});0===b.length&&(b=this.option(a),this.addOptions(b)),d.__super__.select.call(this,a)},d.prototype.convertToOptions=function(a){function d(a){return function(){return c(this).val()==a.id}}for(var e=this,f=this.$element.find("option"),g=f.map(function(){return e.item(c(this)).id}).get(),h=[],i=0;i<a.length;i++){var j=this._normalizeItem(a[i]);if(c.inArray(j.id,g)>=0){var k=f.filter(d(j)),l=this.item(k),m=c.extend(!0,{},j,l),n=this.option(m);k.replaceWith(n)}else{var o=this.option(j);if(j.children){var p=this.convertToOptions(j.children);b.appendMany(o,p)}h.push(o)}}return h},d}),b.define("select2/data/ajax",["./array","../utils","jquery"],function(a,b,c){function d(a,b){this.ajaxOptions=this._applyDefaults(b.get("ajax")),null!=this.ajaxOptions.processResults&&(this.processResults=this.ajaxOptions.processResults),d.__super__.constructor.call(this,a,b)}return b.Extend(d,a),d.prototype._applyDefaults=function(a){var b={data:function(a){return c.extend({},a,{q:a.term})},transport:function(a,b,d){var e=c.ajax(a);return e.then(b),e.fail(d),e}};return c.extend({},b,a,!0)},d.prototype.processResults=function(a){return a},d.prototype.query=function(a,b){function d(){var d=f.transport(f,function(d){var f=e.processResults(d,a);e.options.get("debug")&&window.console&&console.error&&(f&&f.results&&c.isArray(f.results)||console.error("Select2: The AJAX results did not return an array in the `results` key of the response.")),b(f)},function(){d.status&&"0"===d.status||e.trigger("results:message",{message:"errorLoading"})});e._request=d}var e=this;null!=this._request&&(c.isFunction(this._request.abort)&&this._request.abort(),this._request=null);var f=c.extend({type:"GET"},this.ajaxOptions);"function"==typeof f.url&&(f.url=f.url.call(this.$element,a)),"function"==typeof f.data&&(f.data=f.data.call(this.$element,a)),this.ajaxOptions.delay&&null!=a.term?(this._queryTimeout&&window.clearTimeout(this._queryTimeout),this._queryTimeout=window.setTimeout(d,this.ajaxOptions.delay)):d()},d}),b.define("select2/data/tags",["jquery"],function(a){function b(b,c,d){var e=d.get("tags"),f=d.get("createTag");void 0!==f&&(this.createTag=f);var g=d.get("insertTag");if(void 0!==g&&(this.insertTag=g),b.call(this,c,d),a.isArray(e))for(var h=0;h<e.length;h++){var i=e[h],j=this._normalizeItem(i),k=this.option(j);this.$element.append(k)}}return b.prototype.query=function(a,b,c){function d(a,f){for(var g=a.results,h=0;h<g.length;h++){var i=g[h],j=null!=i.children&&!d({results:i.children},!0),k=i.text===b.term;if(k||j)return f?!1:(a.data=g,void c(a))}if(f)return!0;var l=e.createTag(b);if(null!=l){var m=e.option(l);m.attr("data-select2-tag",!0),e.addOptions([m]),e.insertTag(g,l)}a.results=g,c(a)}var e=this;return this._removeOldTags(),null==b.term||null!=b.page?void a.call(this,b,c):void a.call(this,b,d)},b.prototype.createTag=function(b,c){var d=a.trim(c.term);return""===d?null:{id:d,text:d}},b.prototype.insertTag=function(a,b,c){b.unshift(c)},b.prototype._removeOldTags=function(b){var c=(this._lastTag,this.$element.find("option[data-select2-tag]"));c.each(function(){this.selected||a(this).remove()})},b}),b.define("select2/data/tokenizer",["jquery"],function(a){function b(a,b,c){var d=c.get("tokenizer");void 0!==d&&(this.tokenizer=d),a.call(this,b,c)}return b.prototype.bind=function(a,b,c){a.call(this,b,c),this.$search=b.dropdown.$search||b.selection.$search||c.find(".select2-search__field")},b.prototype.query=function(b,c,d){function e(b){var c=g._normalizeItem(b),d=g.$element.find("option").filter(function(){return a(this).val()===c.id});if(!d.length){var e=g.option(c);e.attr("data-select2-tag",!0),g._removeOldTags(),g.addOptions([e])}f(c)}function f(a){g.trigger("select",{data:a})}var g=this;c.term=c.term||"";var h=this.tokenizer(c,this.options,e);h.term!==c.term&&(this.$search.length&&(this.$search.val(h.term),this.$search.focus()),c.term=h.term),b.call(this,c,d)},b.prototype.tokenizer=function(b,c,d,e){for(var f=d.get("tokenSeparators")||[],g=c.term,h=0,i=this.createTag||function(a){return{id:a.term,text:a.term}};h<g.length;){var j=g[h];if(-1!==a.inArray(j,f)){var k=g.substr(0,h),l=a.extend({},c,{term:k}),m=i(l);null!=m?(e(m),g=g.substr(h+1)||"",h=0):h++}else h++}return{term:g}},b}),b.define("select2/data/minimumInputLength",[],function(){function a(a,b,c){this.minimumInputLength=c.get("minimumInputLength"),a.call(this,b,c)}return a.prototype.query=function(a,b,c){return b.term=b.term||"",b.term.length<this.minimumInputLength?void this.trigger("results:message",{message:"inputTooShort",args:{minimum:this.minimumInputLength,input:b.term,params:b}}):void a.call(this,b,c)},a}),b.define("select2/data/maximumInputLength",[],function(){function a(a,b,c){this.maximumInputLength=c.get("maximumInputLength"),a.call(this,b,c)}return a.prototype.query=function(a,b,c){return b.term=b.term||"",this.maximumInputLength>0&&b.term.length>this.maximumInputLength?void this.trigger("results:message",{message:"inputTooLong",args:{maximum:this.maximumInputLength,input:b.term,params:b}}):void a.call(this,b,c)},a}),b.define("select2/data/maximumSelectionLength",[],function(){function a(a,b,c){this.maximumSelectionLength=c.get("maximumSelectionLength"),a.call(this,b,c)}return a.prototype.query=function(a,b,c){var d=this;this.current(function(e){var f=null!=e?e.length:0;return d.maximumSelectionLength>0&&f>=d.maximumSelectionLength?void d.trigger("results:message",{message:"maximumSelected",args:{maximum:d.maximumSelectionLength}}):void a.call(d,b,c)})},a}),b.define("select2/dropdown",["jquery","./utils"],function(a,b){function c(a,b){this.$element=a,this.options=b,c.__super__.constructor.call(this)}return b.Extend(c,b.Observable),c.prototype.render=function(){var b=a('<span class="select2-dropdown"><span class="select2-results"></span></span>');return b.attr("dir",this.options.get("dir")),this.$dropdown=b,b},c.prototype.bind=function(){},c.prototype.position=function(a,b){},c.prototype.destroy=function(){this.$dropdown.remove()},c}),b.define("select2/dropdown/search",["jquery","../utils"],function(a,b){function c(){}return c.prototype.render=function(b){var c=b.call(this),d=a('<span class="select2-search select2-search--dropdown"><input class="select2-search__field" type="search" tabindex="-1" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" role="textbox" /></span>');return this.$searchContainer=d,this.$search=d.find("input"),c.prepend(d),c},c.prototype.bind=function(b,c,d){var e=this;b.call(this,c,d),this.$search.on("keydown",function(a){e.trigger("keypress",a),e._keyUpPrevented=a.isDefaultPrevented()}),this.$search.on("input",function(b){a(this).off("keyup")}),this.$search.on("keyup input",function(a){e.handleSearch(a)}),c.on("open",function(){e.$search.attr("tabindex",0),e.$search.focus(),window.setTimeout(function(){e.$search.focus()},0)}),c.on("close",function(){e.$search.attr("tabindex",-1),e.$search.val("")}),c.on("focus",function(){c.isOpen()&&e.$search.focus()}),c.on("results:all",function(a){if(null==a.query.term||""===a.query.term){var b=e.showSearch(a);b?e.$searchContainer.removeClass("select2-search--hide"):e.$searchContainer.addClass("select2-search--hide")}})},c.prototype.handleSearch=function(a){if(!this._keyUpPrevented){var b=this.$search.val();this.trigger("query",{term:b})}this._keyUpPrevented=!1},c.prototype.showSearch=function(a,b){return!0},c}),b.define("select2/dropdown/hidePlaceholder",[],function(){function a(a,b,c,d){this.placeholder=this.normalizePlaceholder(c.get("placeholder")),a.call(this,b,c,d)}return a.prototype.append=function(a,b){b.results=this.removePlaceholder(b.results),a.call(this,b)},a.prototype.normalizePlaceholder=function(a,b){return"string"==typeof b&&(b={id:"",text:b}),b},a.prototype.removePlaceholder=function(a,b){for(var c=b.slice(0),d=b.length-1;d>=0;d--){var e=b[d];this.placeholder.id===e.id&&c.splice(d,1)}return c},a}),b.define("select2/dropdown/infiniteScroll",["jquery"],function(a){function b(a,b,c,d){this.lastParams={},a.call(this,b,c,d),this.$loadingMore=this.createLoadingMore(),this.loading=!1}return b.prototype.append=function(a,b){this.$loadingMore.remove(),this.loading=!1,a.call(this,b),this.showLoadingMore(b)&&this.$results.append(this.$loadingMore)},b.prototype.bind=function(b,c,d){var e=this;b.call(this,c,d),c.on("query",function(a){e.lastParams=a,e.loading=!0}),c.on("query:append",function(a){e.lastParams=a,e.loading=!0}),this.$results.on("scroll",function(){var b=a.contains(document.documentElement,e.$loadingMore[0]);if(!e.loading&&b){var c=e.$results.offset().top+e.$results.outerHeight(!1),d=e.$loadingMore.offset().top+e.$loadingMore.outerHeight(!1);c+50>=d&&e.loadMore()}})},b.prototype.loadMore=function(){this.loading=!0;var b=a.extend({},{page:1},this.lastParams);b.page++,this.trigger("query:append",b)},b.prototype.showLoadingMore=function(a,b){return b.pagination&&b.pagination.more},b.prototype.createLoadingMore=function(){var b=a('<li class="select2-results__option select2-results__option--load-more"role="treeitem" aria-disabled="true"></li>'),c=this.options.get("translations").get("loadingMore");return b.html(c(this.lastParams)),b},b}),b.define("select2/dropdown/attachBody",["jquery","../utils"],function(a,b){function c(b,c,d){this.$dropdownParent=d.get("dropdownParent")||a(document.body),b.call(this,c,d)}return c.prototype.bind=function(a,b,c){var d=this,e=!1;a.call(this,b,c),b.on("open",function(){d._showDropdown(),d._attachPositioningHandler(b),e||(e=!0,b.on("results:all",function(){d._positionDropdown(),d._resizeDropdown()}),b.on("results:append",function(){d._positionDropdown(),d._resizeDropdown()}))}),b.on("close",function(){d._hideDropdown(),d._detachPositioningHandler(b)}),this.$dropdownContainer.on("mousedown",function(a){a.stopPropagation()})},c.prototype.destroy=function(a){a.call(this),this.$dropdownContainer.remove()},c.prototype.position=function(a,b,c){b.attr("class",c.attr("class")),b.removeClass("select2"),b.addClass("select2-container--open"),b.css({position:"absolute",top:-999999}),this.$container=c},c.prototype.render=function(b){var c=a("<span></span>"),d=b.call(this);return c.append(d),this.$dropdownContainer=c,c},c.prototype._hideDropdown=function(a){this.$dropdownContainer.detach()},c.prototype._attachPositioningHandler=function(c,d){var e=this,f="scroll.select2."+d.id,g="resize.select2."+d.id,h="orientationchange.select2."+d.id,i=this.$container.parents().filter(b.hasScroll);i.each(function(){a(this).data("select2-scroll-position",{x:a(this).scrollLeft(),y:a(this).scrollTop()})}),i.on(f,function(b){var c=a(this).data("select2-scroll-position");a(this).scrollTop(c.y)}),a(window).on(f+" "+g+" "+h,function(a){e._positionDropdown(),e._resizeDropdown()})},c.prototype._detachPositioningHandler=function(c,d){var e="scroll.select2."+d.id,f="resize.select2."+d.id,g="orientationchange.select2."+d.id,h=this.$container.parents().filter(b.hasScroll);h.off(e),a(window).off(e+" "+f+" "+g)},c.prototype._positionDropdown=function(){var b=a(window),c=this.$dropdown.hasClass("select2-dropdown--above"),d=this.$dropdown.hasClass("select2-dropdown--below"),e=null,f=this.$container.offset();f.bottom=f.top+this.$container.outerHeight(!1);var g={height:this.$container.outerHeight(!1)};g.top=f.top,g.bottom=f.top+g.height;var h={height:this.$dropdown.outerHeight(!1)},i={top:b.scrollTop(),bottom:b.scrollTop()+b.height()},j=i.top<f.top-h.height,k=i.bottom>f.bottom+h.height,l={left:f.left,top:g.bottom},m=this.$dropdownParent;"static"===m.css("position")&&(m=m.offsetParent());var n=m.offset();l.top-=n.top,l.left-=n.left,c||d||(e="below"),k||!j||c?!j&&k&&c&&(e="below"):e="above",("above"==e||c&&"below"!==e)&&(l.top=g.top-n.top-h.height),null!=e&&(this.$dropdown.removeClass("select2-dropdown--below select2-dropdown--above").addClass("select2-dropdown--"+e),this.$container.removeClass("select2-container--below select2-container--above").addClass("select2-container--"+e)),this.$dropdownContainer.css(l)},c.prototype._resizeDropdown=function(){var a={width:this.$container.outerWidth(!1)+"px"};this.options.get("dropdownAutoWidth")&&(a.minWidth=a.width,a.position="relative",a.width="auto"),this.$dropdown.css(a)},c.prototype._showDropdown=function(a){this.$dropdownContainer.appendTo(this.$dropdownParent),this._positionDropdown(),this._resizeDropdown()},c}),b.define("select2/dropdown/minimumResultsForSearch",[],function(){function a(b){for(var c=0,d=0;d<b.length;d++){var e=b[d];e.children?c+=a(e.children):c++}return c}function b(a,b,c,d){this.minimumResultsForSearch=c.get("minimumResultsForSearch"),this.minimumResultsForSearch<0&&(this.minimumResultsForSearch=1/0),a.call(this,b,c,d)}return b.prototype.showSearch=function(b,c){return a(c.data.results)<this.minimumResultsForSearch?!1:b.call(this,c)},b}),b.define("select2/dropdown/selectOnClose",[],function(){function a(){}return a.prototype.bind=function(a,b,c){var d=this;a.call(this,b,c),b.on("close",function(a){d._handleSelectOnClose(a)})},a.prototype._handleSelectOnClose=function(a,b){if(b&&null!=b.originalSelect2Event){var c=b.originalSelect2Event;if("select"===c._type||"unselect"===c._type)return}var d=this.getHighlightedResults();if(!(d.length<1)){var e=d.data("data");null!=e.element&&e.element.selected||null==e.element&&e.selected||this.trigger("select",{data:e})}},a}),b.define("select2/dropdown/closeOnSelect",[],function(){function a(){}return a.prototype.bind=function(a,b,c){var d=this;a.call(this,b,c),b.on("select",function(a){d._selectTriggered(a)}),b.on("unselect",function(a){d._selectTriggered(a)})},a.prototype._selectTriggered=function(a,b){var c=b.originalEvent;c&&c.ctrlKey||this.trigger("close",{originalEvent:c,originalSelect2Event:b})},a}),b.define("select2/i18n/en",[],function(){return{errorLoading:function(){return"The results could not be loaded."},inputTooLong:function(a){var b=a.input.length-a.maximum,c="Please delete "+b+" character";return 1!=b&&(c+="s"),c},inputTooShort:function(a){var b=a.minimum-a.input.length,c="Please enter "+b+" or more characters";return c},loadingMore:function(){return"Loading more results…"},maximumSelected:function(a){var b="You can only select "+a.maximum+" item";return 1!=a.maximum&&(b+="s"),b},noResults:function(){return"No results found"},searching:function(){return"Searching…"}}}),b.define("select2/defaults",["jquery","require","./results","./selection/single","./selection/multiple","./selection/placeholder","./selection/allowClear","./selection/search","./selection/eventRelay","./utils","./translation","./diacritics","./data/select","./data/array","./data/ajax","./data/tags","./data/tokenizer","./data/minimumInputLength","./data/maximumInputLength","./data/maximumSelectionLength","./dropdown","./dropdown/search","./dropdown/hidePlaceholder","./dropdown/infiniteScroll","./dropdown/attachBody","./dropdown/minimumResultsForSearch","./dropdown/selectOnClose","./dropdown/closeOnSelect","./i18n/en"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C){function D(){this.reset()}D.prototype.apply=function(l){if(l=a.extend(!0,{},this.defaults,l),null==l.dataAdapter){if(null!=l.ajax?l.dataAdapter=o:null!=l.data?l.dataAdapter=n:l.dataAdapter=m,l.minimumInputLength>0&&(l.dataAdapter=j.Decorate(l.dataAdapter,r)),l.maximumInputLength>0&&(l.dataAdapter=j.Decorate(l.dataAdapter,s)),l.maximumSelectionLength>0&&(l.dataAdapter=j.Decorate(l.dataAdapter,t)),l.tags&&(l.dataAdapter=j.Decorate(l.dataAdapter,p)),(null!=l.tokenSeparators||null!=l.tokenizer)&&(l.dataAdapter=j.Decorate(l.dataAdapter,q)),null!=l.query){var C=b(l.amdBase+"compat/query");l.dataAdapter=j.Decorate(l.dataAdapter,C)}if(null!=l.initSelection){var D=b(l.amdBase+"compat/initSelection");l.dataAdapter=j.Decorate(l.dataAdapter,D)}}if(null==l.resultsAdapter&&(l.resultsAdapter=c,null!=l.ajax&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,x)),null!=l.placeholder&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,w)),l.selectOnClose&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,A))),null==l.dropdownAdapter){if(l.multiple)l.dropdownAdapter=u;else{var E=j.Decorate(u,v);l.dropdownAdapter=E}if(0!==l.minimumResultsForSearch&&(l.dropdownAdapter=j.Decorate(l.dropdownAdapter,z)),l.closeOnSelect&&(l.dropdownAdapter=j.Decorate(l.dropdownAdapter,B)),null!=l.dropdownCssClass||null!=l.dropdownCss||null!=l.adaptDropdownCssClass){var F=b(l.amdBase+"compat/dropdownCss");l.dropdownAdapter=j.Decorate(l.dropdownAdapter,F)}l.dropdownAdapter=j.Decorate(l.dropdownAdapter,y)}if(null==l.selectionAdapter){if(l.multiple?l.selectionAdapter=e:l.selectionAdapter=d,null!=l.placeholder&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,f)),l.allowClear&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,g)),l.multiple&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,h)),null!=l.containerCssClass||null!=l.containerCss||null!=l.adaptContainerCssClass){var G=b(l.amdBase+"compat/containerCss");l.selectionAdapter=j.Decorate(l.selectionAdapter,G)}l.selectionAdapter=j.Decorate(l.selectionAdapter,i)}if("string"==typeof l.language)if(l.language.indexOf("-")>0){var H=l.language.split("-"),I=H[0];l.language=[l.language,I]}else l.language=[l.language];if(a.isArray(l.language)){var J=new k;l.language.push("en");for(var K=l.language,L=0;L<K.length;L++){var M=K[L],N={};try{N=k.loadPath(M)}catch(O){try{M=this.defaults.amdLanguageBase+M,N=k.loadPath(M)}catch(P){l.debug&&window.console&&console.warn&&console.warn('Select2: The language file for "'+M+'" could not be automatically loaded. A fallback will be used instead.');continue}}J.extend(N)}l.translations=J}else{var Q=k.loadPath(this.defaults.amdLanguageBase+"en"),R=new k(l.language);R.extend(Q),l.translations=R}return l},D.prototype.reset=function(){function b(a){function b(a){return l[a]||a}return a.replace(/[^\u0000-\u007E]/g,b)}function c(d,e){if(""===a.trim(d.term))return e;if(e.children&&e.children.length>0){for(var f=a.extend(!0,{},e),g=e.children.length-1;g>=0;g--){var h=e.children[g],i=c(d,h);null==i&&f.children.splice(g,1)}return f.children.length>0?f:c(d,f)}var j=b(e.text).toUpperCase(),k=b(d.term).toUpperCase();return j.indexOf(k)>-1?e:null}this.defaults={amdBase:"./",amdLanguageBase:"./i18n/",closeOnSelect:!0,debug:!1,dropdownAutoWidth:!1,escapeMarkup:j.escapeMarkup,language:C,matcher:c,minimumInputLength:0,maximumInputLength:0,maximumSelectionLength:0,minimumResultsForSearch:0,selectOnClose:!1,sorter:function(a){return a},templateResult:function(a){return a.text},templateSelection:function(a){return a.text},theme:"default",width:"resolve"}},D.prototype.set=function(b,c){var d=a.camelCase(b),e={};e[d]=c;var f=j._convertData(e);a.extend(this.defaults,f)};var E=new D;return E}),b.define("select2/options",["require","jquery","./defaults","./utils"],function(a,b,c,d){function e(b,e){if(this.options=b,null!=e&&this.fromElement(e),this.options=c.apply(this.options),e&&e.is("input")){var f=a(this.get("amdBase")+"compat/inputData");this.options.dataAdapter=d.Decorate(this.options.dataAdapter,f)}}return e.prototype.fromElement=function(a){var c=["select2"];null==this.options.multiple&&(this.options.multiple=a.prop("multiple")),null==this.options.disabled&&(this.options.disabled=a.prop("disabled")),null==this.options.language&&(a.prop("lang")?this.options.language=a.prop("lang").toLowerCase():a.closest("[lang]").prop("lang")&&(this.options.language=a.closest("[lang]").prop("lang"))),null==this.options.dir&&(a.prop("dir")?this.options.dir=a.prop("dir"):a.closest("[dir]").prop("dir")?this.options.dir=a.closest("[dir]").prop("dir"):this.options.dir="ltr"),a.prop("disabled",this.options.disabled),a.prop("multiple",this.options.multiple),a.data("select2Tags")&&(this.options.debug&&window.console&&console.warn&&console.warn('Select2: The `data-select2-tags` attribute has been changed to use the `data-data` and `data-tags="true"` attributes and will be removed in future versions of Select2.'),a.data("data",a.data("select2Tags")),a.data("tags",!0)),a.data("ajaxUrl")&&(this.options.debug&&window.console&&console.warn&&console.warn("Select2: The `data-ajax-url` attribute has been changed to `data-ajax--url` and support for the old attribute will be removed in future versions of Select2."),a.attr("ajax--url",a.data("ajaxUrl")),a.data("ajax--url",a.data("ajaxUrl")));var e={};e=b.fn.jquery&&"1."==b.fn.jquery.substr(0,2)&&a[0].dataset?b.extend(!0,{},a[0].dataset,a.data()):a.data();var f=b.extend(!0,{},e);f=d._convertData(f);for(var g in f)b.inArray(g,c)>-1||(b.isPlainObject(this.options[g])?b.extend(this.options[g],f[g]):this.options[g]=f[g]);return this},e.prototype.get=function(a){return this.options[a]},e.prototype.set=function(a,b){this.options[a]=b},e}),b.define("select2/core",["jquery","./options","./utils","./keys"],function(a,b,c,d){var e=function(a,c){null!=a.data("select2")&&a.data("select2").destroy(),this.$element=a,this.id=this._generateId(a),c=c||{},this.options=new b(c,a),e.__super__.constructor.call(this);var d=a.attr("tabindex")||0;a.data("old-tabindex",d),a.attr("tabindex","-1");var f=this.options.get("dataAdapter");this.dataAdapter=new f(a,this.options);var g=this.render();this._placeContainer(g);var h=this.options.get("selectionAdapter");this.selection=new h(a,this.options),this.$selection=this.selection.render(),this.selection.position(this.$selection,g);var i=this.options.get("dropdownAdapter");this.dropdown=new i(a,this.options),this.$dropdown=this.dropdown.render(),this.dropdown.position(this.$dropdown,g);var j=this.options.get("resultsAdapter");this.results=new j(a,this.options,this.dataAdapter),this.$results=this.results.render(),this.results.position(this.$results,this.$dropdown);var k=this;this._bindAdapters(),this._registerDomEvents(),this._registerDataEvents(),this._registerSelectionEvents(),this._registerDropdownEvents(),this._registerResultsEvents(),this._registerEvents(),this.dataAdapter.current(function(a){k.trigger("selection:update",{data:a})}),a.addClass("select2-hidden-accessible"),a.attr("aria-hidden","true"),this._syncAttributes(),a.data("select2",this)};return c.Extend(e,c.Observable),e.prototype._generateId=function(a){var b="";return b=null!=a.attr("id")?a.attr("id"):null!=a.attr("name")?a.attr("name")+"-"+c.generateChars(2):c.generateChars(4),b=b.replace(/(:|\.|\[|\]|,)/g,""),b="select2-"+b},e.prototype._placeContainer=function(a){a.insertAfter(this.$element);var b=this._resolveWidth(this.$element,this.options.get("width"));null!=b&&a.css("width",b)},e.prototype._resolveWidth=function(a,b){var c=/^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i;if("resolve"==b){var d=this._resolveWidth(a,"style");return null!=d?d:this._resolveWidth(a,"element")}if("element"==b){var e=a.outerWidth(!1);return 0>=e?"auto":e+"px"}if("style"==b){var f=a.attr("style");if("string"!=typeof f)return null;for(var g=f.split(";"),h=0,i=g.length;i>h;h+=1){var j=g[h].replace(/\s/g,""),k=j.match(c);if(null!==k&&k.length>=1)return k[1]}return null}return b},e.prototype._bindAdapters=function(){this.dataAdapter.bind(this,this.$container),this.selection.bind(this,this.$container),this.dropdown.bind(this,this.$container),this.results.bind(this,this.$container)},e.prototype._registerDomEvents=function(){var b=this;this.$element.on("change.select2",function(){b.dataAdapter.current(function(a){b.trigger("selection:update",{data:a})})}),this.$element.on("focus.select2",function(a){b.trigger("focus",a)}),this._syncA=c.bind(this._syncAttributes,this),this._syncS=c.bind(this._syncSubtree,this),this.$element[0].attachEvent&&this.$element[0].attachEvent("onpropertychange",this._syncA);var d=window.MutationObserver||window.WebKitMutationObserver||window.MozMutationObserver;null!=d?(this._observer=new d(function(c){a.each(c,b._syncA),a.each(c,b._syncS)}),this._observer.observe(this.$element[0],{attributes:!0,childList:!0,subtree:!1})):this.$element[0].addEventListener&&(this.$element[0].addEventListener("DOMAttrModified",b._syncA,!1),this.$element[0].addEventListener("DOMNodeInserted",b._syncS,!1),this.$element[0].addEventListener("DOMNodeRemoved",b._syncS,!1))},e.prototype._registerDataEvents=function(){var a=this;this.dataAdapter.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerSelectionEvents=function(){var b=this,c=["toggle","focus"];this.selection.on("toggle",function(){b.toggleDropdown()}),this.selection.on("focus",function(a){b.focus(a)}),this.selection.on("*",function(d,e){-1===a.inArray(d,c)&&b.trigger(d,e)})},e.prototype._registerDropdownEvents=function(){var a=this;this.dropdown.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerResultsEvents=function(){var a=this;this.results.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerEvents=function(){var a=this;this.on("open",function(){a.$container.addClass("select2-container--open")}),this.on("close",function(){a.$container.removeClass("select2-container--open")}),this.on("enable",function(){a.$container.removeClass("select2-container--disabled")}),this.on("disable",function(){a.$container.addClass("select2-container--disabled")}),this.on("blur",function(){a.$container.removeClass("select2-container--focus")}),this.on("query",function(b){a.isOpen()||a.trigger("open",{}),this.dataAdapter.query(b,function(c){a.trigger("results:all",{data:c,query:b})})}),this.on("query:append",function(b){this.dataAdapter.query(b,function(c){a.trigger("results:append",{data:c,query:b})})}),this.on("keypress",function(b){var c=b.which;a.isOpen()?c===d.ESC||c===d.TAB||c===d.UP&&b.altKey?(a.close(),b.preventDefault()):c===d.ENTER?(a.trigger("results:select",{}),b.preventDefault()):c===d.SPACE&&b.ctrlKey?(a.trigger("results:toggle",{}),b.preventDefault()):c===d.UP?(a.trigger("results:previous",{}),b.preventDefault()):c===d.DOWN&&(a.trigger("results:next",{}),b.preventDefault()):(c===d.ENTER||c===d.SPACE||c===d.DOWN&&b.altKey)&&(a.open(),b.preventDefault())})},e.prototype._syncAttributes=function(){this.options.set("disabled",this.$element.prop("disabled")),this.options.get("disabled")?(this.isOpen()&&this.close(),this.trigger("disable",{})):this.trigger("enable",{})},e.prototype._syncSubtree=function(a,b){var c=!1,d=this;if(!a||!a.target||"OPTION"===a.target.nodeName||"OPTGROUP"===a.target.nodeName){if(b)if(b.addedNodes&&b.addedNodes.length>0)for(var e=0;e<b.addedNodes.length;e++){var f=b.addedNodes[e];f.selected&&(c=!0)}else b.removedNodes&&b.removedNodes.length>0&&(c=!0);else c=!0;c&&this.dataAdapter.current(function(a){d.trigger("selection:update",{data:a})})}},e.prototype.trigger=function(a,b){var c=e.__super__.trigger,d={open:"opening",close:"closing",select:"selecting",unselect:"unselecting"};if(void 0===b&&(b={}),a in d){var f=d[a],g={prevented:!1,name:a,args:b};if(c.call(this,f,g),g.prevented)return void(b.prevented=!0)}c.call(this,a,b)},e.prototype.toggleDropdown=function(){this.options.get("disabled")||(this.isOpen()?this.close():this.open())},e.prototype.open=function(){this.isOpen()||this.trigger("query",{})},e.prototype.close=function(){this.isOpen()&&this.trigger("close",{})},e.prototype.isOpen=function(){return this.$container.hasClass("select2-container--open")},e.prototype.hasFocus=function(){return this.$container.hasClass("select2-container--focus")},e.prototype.focus=function(a){this.hasFocus()||(this.$container.addClass("select2-container--focus"),this.trigger("focus",{}))},e.prototype.enable=function(a){this.options.get("debug")&&window.console&&console.warn&&console.warn('Select2: The `select2("enable")` method has been deprecated and will be removed in later Select2 versions. Use $element.prop("disabled") instead.'),(null==a||0===a.length)&&(a=[!0]);var b=!a[0];this.$element.prop("disabled",b)},e.prototype.data=function(){this.options.get("debug")&&arguments.length>0&&window.console&&console.warn&&console.warn('Select2: Data can no longer be set using `select2("data")`. You should consider setting the value instead using `$element.val()`.');var a=[];return this.dataAdapter.current(function(b){a=b}),a},e.prototype.val=function(b){if(this.options.get("debug")&&window.console&&console.warn&&console.warn('Select2: The `select2("val")` method has been deprecated and will be removed in later Select2 versions. Use $element.val() instead.'),null==b||0===b.length)return this.$element.val();var c=b[0];a.isArray(c)&&(c=a.map(c,function(a){return a.toString()})),this.$element.val(c).trigger("change")},e.prototype.destroy=function(){this.$container.remove(),this.$element[0].detachEvent&&this.$element[0].detachEvent("onpropertychange",this._syncA),null!=this._observer?(this._observer.disconnect(),this._observer=null):this.$element[0].removeEventListener&&(this.$element[0].removeEventListener("DOMAttrModified",this._syncA,!1),this.$element[0].removeEventListener("DOMNodeInserted",this._syncS,!1),this.$element[0].removeEventListener("DOMNodeRemoved",this._syncS,!1)),this._syncA=null,this._syncS=null,this.$element.off(".select2"),this.$element.attr("tabindex",this.$element.data("old-tabindex")),this.$element.removeClass("select2-hidden-accessible"),this.$element.attr("aria-hidden","false"),this.$element.removeData("select2"),this.dataAdapter.destroy(),this.selection.destroy(),this.dropdown.destroy(),this.results.destroy(),this.dataAdapter=null,this.selection=null,this.dropdown=null,this.results=null;
},e.prototype.render=function(){var b=a('<span class="select2 select2-container"><span class="selection"></span><span class="dropdown-wrapper" aria-hidden="true"></span></span>');return b.attr("dir",this.options.get("dir")),this.$container=b,this.$container.addClass("select2-container--"+this.options.get("theme")),b.data("element",this.$element),b},e}),b.define("jquery-mousewheel",["jquery"],function(a){return a}),b.define("jquery.select2",["jquery","jquery-mousewheel","./select2/core","./select2/defaults"],function(a,b,c,d){if(null==a.fn.select2){var e=["open","close","destroy"];a.fn.select2=function(b){if(b=b||{},"object"==typeof b)return this.each(function(){var d=a.extend(!0,{},b);new c(a(this),d)}),this;if("string"==typeof b){var d,f=Array.prototype.slice.call(arguments,1);return this.each(function(){var c=a(this).data("select2");null==c&&window.console&&console.error&&console.error("The select2('"+b+"') method was called on an element that is not using Select2."),d=c[b].apply(c,f)}),a.inArray(b,e)>-1?this:d}throw new Error("Invalid arguments for Select2: "+b)}}return null==a.fn.select2.defaults&&(a.fn.select2.defaults=d),c}),{define:b.define,require:b.require}}(),c=b.require("jquery.select2");return a.fn.select2.amd=b,c});dist/js/wp-2fa.js000064400000066321150755130600007567 0ustar00
try{
jQuery(document).ready(function(){MicroModal.init();function updateStepTitles(){if(jQuery('[data-step-title]').length){jQuery('.step-title-wrapper').remove();jQuery('.wp2fa-setup-content').prepend('<div class="step-title-wrapper"></div>');var counter=1;jQuery('[data-step-title]:not(.hidden)').each(function(){var stepLabel=jQuery(this).attr('data-step-title');if(jQuery(this).hasClass('active')){jQuery('.step-title-wrapper').append(`<span class="step-title active-step-title"><span>${counter}</span> ${stepLabel}</span>`);}else{jQuery('.step-title-wrapper').append(`<span class="step-title"><span>${counter}</span> ${stepLabel}</span>`);}
counter++;});}
checkWizardOffset();setTimeout(function(){jQuery('.step-setting-wrapper.active input[type="tel"] ').focus();},200);}
updateStepTitles();jQuery('body').on('click','.step-title',function(e){var currentLabel=jQuery(this).text().substr(2);jQuery('[data-step-title]:not(.hidden)').each(function(){var currentStep=jQuery(this);jQuery('[data-step-title]').removeClass('active');jQuery('.step-title').removeClass('active-step-title');var stepLabel=jQuery(this).attr('data-step-title');jQuery(`[data-step-title="${currentLabel}"]`).addClass('active');});updateStepTitles();});jQuery('[data-unhide-when-checked]').each(function(){if(jQuery(this).is(':checked')){const thingToShow=jQuery(this).attr('data-unhide-when-checked');jQuery(thingToShow).show(0);}});jQuery('body').on('click','[for="all-users"], [for="certain-roles-only"]',function(e){jQuery('.step-setting-wrapper.hidden').removeClass('hidden').addClass('un-hidden');updateStepTitles();});jQuery('body').on('click','[for="do-not-enforce"]',function(e){jQuery('.step-setting-wrapper.un-hidden').removeClass('un-hidden').addClass('hidden');updateStepTitles();});jQuery('body').on('click','.modal__btn',function(e){e.preventDefault();});jQuery('body').on('keypress','.wp2fa-modal',function(event){var keycode=(event.keyCode?event.keyCode:event.which);if('13'==keycode){return false;}});jQuery(document).on('click','[data-open-configure-2fa-wizard]',function(event){event.preventDefault();wp2fa_fireWizard();});jQuery(document).on('click','.step-setting-wrapper.active .option-pill input[type="checkbox"]',function(e){if('backup-codes'!==this.id&&!jQuery(this).hasClass('disabled')){if(true!==jQuery('#geek').prop('checked')&&true!==jQuery('#basic').prop('checked')){jQuery('#backup-codes').addClass('disabled');jQuery('label[for=\'backup-codes\']').addClass('disabled');window.backupCodes=jQuery('#backup-codes').prop('checked');jQuery('#backup-codes').prop('checked',false);if(jQuery('[name="next_step_setting"]').length){jQuery('[name="next_step_setting"]').addClass('disabled').attr('name','next_step_setting_disabled');}}else{jQuery('#backup-codes').removeClass('disabled');jQuery('label[for=\'backup-codes\']').removeClass('disabled');if('undefined'!==window.backupCodes){jQuery('#backup-codes').prop('checked',window.backupCodes);}
if(jQuery('[name="next_step_setting_disabled"]').length){jQuery('[name="next_step_setting_disabled"]').removeClass('disabled').attr('name','next_step_setting');}}}else{if(!jQuery(this).hasClass('disabled')){window.backupCodes=jQuery('#backup-codes').prop('checked');}else{jQuery('#backup-codes').prop('checked',false);}}});jQuery(document).on('click touchend','.radio-cells label, .radio-cells input[type="radio"]',function(e){jQuery('.option-pill').removeClass('isSelected');jQuery('.radio-cells input[type="radio"]:checked').closest('.option-pill').addClass('isSelected');});jQuery('.wizard-tooltip').each(function(){var contentDiv=jQuery(this).attr('data-tooltip-content');var ourItem=jQuery(this);if(jQuery('['+contentDiv+']').length){var content=jQuery('['+contentDiv+']').clone();jQuery(ourItem).append(content);}});jQuery(document).on('click touchend','.wizard-tooltip',function(e){var contentDiv=jQuery(this).attr('data-tooltip-content');var ourItem=jQuery(this);if(jQuery(this).hasClass('isOpen')){jQuery('.inline-helper').slideDown();}else{jQuery('[data-tooltip-content]').removeClass('isOpen');setTimeout(function(){if(jQuery('.inline-helper').length>2){jQuery('.inline-helper').not('['+contentDiv+']').slideUp();}},100);setTimeout(function(){if(jQuery('.inline-helper').length>2){jQuery('.inline-helper').not('['+contentDiv+']').remove();}},600);if(jQuery('['+contentDiv+']').length){var content=jQuery('['+contentDiv+']').html();jQuery('<div class="inline-helper" '+contentDiv+'>'+content+'</div>').insertAfter('.radio-cells');jQuery('.inline-helper').slideDown();}
jQuery(ourItem).addClass('isOpen');}});jQuery(document).on('click','.wp-2fa-method-select input[type="checkbox"]',function(e){let role_suffix=('global'===jQuery(this).data('role'))?'':'-'+jQuery(this).data('role');if('backup-codes'+role_suffix!==this.id&&!jQuery(this).hasClass('disabled')){let backDisabled=false;if(true!==jQuery('#totp'+role_suffix).prop('checked')&&true!==jQuery('#hotp'+role_suffix).prop('checked')){backDisabled=true;if(jQuery('#oob'+role_suffix).length){if(true===jQuery('#oob'+role_suffix).prop('checked')){backDisabled=false;}}
if(jQuery('#authy'+role_suffix).length){setTimeout(function(){},2000);if(true===jQuery('#authy'+role_suffix).prop('checked')){backDisabled=false;}}
if(jQuery('#twilio'+role_suffix).length){setTimeout(function(){},2000);if(true===jQuery('#twilio'+role_suffix).prop('checked')){backDisabled=false;}}}
if(backDisabled){jQuery('#backup-codes'+role_suffix).addClass('disabled');jQuery('label[for="backup-codes'+role_suffix+'"]').addClass('disabled');window.backupCodes=jQuery('#backup-codes'+role_suffix).prop('checked');jQuery('#backup-codes'+role_suffix).prop('checked',false);jQuery('[for="all-users"], [for="certain-roles-only"]').addClass('disabled');}else{jQuery('[for="all-users"], [for="certain-roles-only"]').removeClass('disabled');jQuery('#backup-codes'+role_suffix).removeClass('disabled');jQuery('label[for="backup-codes'+role_suffix+'"]').removeClass('disabled');if('undefined'!==window.backupCodes){jQuery('#backup-codes'+role_suffix).prop('checked',window.backupCodes);}}}else{if(!jQuery(this).hasClass('disabled')){window.backupCodes=jQuery('#backup-codes'+role_suffix).prop('checked');}else{jQuery('#backup-codes'+role_suffix).prop('checked',false);}}});jQuery(document).on('click','.wp2fa-setup-form .wp-2fa-method-select input[type="checkbox"]',function(e){let twilio=false;let authy=false;if(jQuery('#twilio').length){if(jQuery('#twilio[data-sid-setup-wizard]').length&&jQuery('#wizard-sid-key').is(':visible')){twilio=false;}else{twilio=jQuery('#twilio').prop('checked');}}
if(jQuery('#authy').length){if(jQuery('#authy[data-api-setup-wizard]').length&&jQuery('#wizard-api-key').is(':visible')){authy=false;}else{authy=jQuery('#authy').prop('checked');}}
if(true!==jQuery('#totp').prop('checked')&&true!==jQuery('#hotp').prop('checked')&&true!==jQuery('#oob').prop('checked')&&true!==authy&&true!==twilio){let showAlert=true;if(jQuery(this).is(jQuery('input#authy.disabled'))&&jQuery('#wizard-api-key').is(':visible')){showAlert=false;}
if(jQuery(this).is(jQuery('input#twilio.disabled'))&&jQuery('#wizard-sid-key').is(':visible')){showAlert=false;}
if(showAlert){alert('Please select at least one 2FA method');}
jQuery('a.button[name="next_step_setting"]').prop('disabled',true);jQuery('a.button[name="next_step_setting"]').addClass('disabled');}else{jQuery('a.button[name="next_step_setting"]').prop('disabled',false);jQuery('a.button[name="next_step_setting"]').removeClass('disabled');}});jQuery(document).on('click','[data-close-2fa-modal]',function(e){e.preventDefault();var modalToClose=`#${  jQuery( this ).closest( '.wp2fa-modal' ).attr( 'id' )}`;jQuery(modalToClose).removeClass('is-open').attr('aria-hidden','true');if('reLogin'in wp2faWizardData&&wp2faWizardData.reLoginEnabled==jQuery.trim(wp2faWizardData.reLogin)){jQuery.ajax({type:'POST',url:wp2faData.ajaxURL,data:{action:'custom_ajax_logout',_wpnonce:wp2faWizardData.nonce,},success:function(r){if('redirectToUrl'in wp2faWizardData&&''!=jQuery.trim(wp2faWizardData.redirectToUrl)){window.location.replace(wp2faWizardData.redirectToUrl);}else{removeShowParam();}}});}});jQuery(document).on('click','[data-close-2fa-modal-and-refresh]',function(e){e.preventDefault();var modalToClose=`#${  jQuery( this ).closest( '.wp2fa-modal' ).attr( 'id' )}`;jQuery(modalToClose).removeClass('is-open').attr('aria-hidden','true');if('reLogin'in wp2faWizardData&&wp2faWizardData.reLoginEnabled==jQuery.trim(wp2faWizardData.reLogin)){jQuery.ajax({type:'POST',url:wp2faData.ajaxURL,data:{action:'custom_ajax_logout',_wpnonce:wp2faWizardData.nonce,},success:function(r){if('redirectToUrl'in wp2faWizardData&&''!=jQuery.trim(wp2faWizardData.redirectToUrl)){window.location.replace(wp2faWizardData.redirectToUrl);}else{removeShowParam();}}});}else{removeShowParam();}});jQuery(document).on('click','[data-validate-authcode-ajax]',function(e){e.preventDefault();const thisButton=jQuery(this);let actionToRun='validate_authcode_via_ajax';let authcode=false;if(jQuery('#wp-2fa-totp-authcode').length&&jQuery('#wp-2fa-totp-authcode').val().length){authcode=true;}
if(typeof jQuery(this).data('oob-test')!=='undefined'){actionToRun='validate_oob_authcode_via_ajax';}
const nonceValue=jQuery(this).attr('data-nonce');var values={};jQuery.each(jQuery('.wp-2fa-user-profile-form :input, .wp2fa-modal :input').serializeArray(),function(i,field){values[field.name]=field.value;});const currentPageURL=window.location.href;const form=values;jQuery.ajax({type:'POST',dataType:'json',url:wp2faData.ajaxURL,data:{action:actionToRun,form:values,_wpnonce:nonceValue,},complete:function(data){if(false===data.responseJSON.success){jQuery(thisButton).parent().find('.verification-response').html(`<span style="color:red">${data.responseJSON.data['error']}</span>`);}
if(true===data.responseJSON.success){let nextSubStep=jQuery('#2fa-wizard-config-backup-codes');if(authcode){if(jQuery('#2fa-wizard-backup-methods').length){nextSubStep=jQuery('#2fa-wizard-backup-methods');}else if(jQuery('#2fa-wizard-email-backup-selected').length){nextSubStep=jQuery('#2fa-wizard-email-backup-selected');}}
jQuery(this).parent().parent().find('.active').not('.step-setting-wrapper').removeClass('active');jQuery('.wizard-step.active').removeClass('active');jQuery(nextSubStep).addClass('active');jQuery(document).on('click','#select-backup-method',function(e){e.preventDefault();var backupRadio=jQuery("input[name=backup_method_select]:checked");jQuery('.wizard-step.active').removeClass('active');jQuery('#'+backupRadio.data('step')).addClass('active');});jQuery(document).on('click','[name="save_step"], [data-close-2fa-modal]',function(){if('reLogin'in wp2faWizardData&&wp2faWizardData.reLoginEnabled==jQuery.trim(wp2faWizardData.reLogin)){jQuery.ajax({type:'POST',url:wp2faData.ajaxURL,data:{action:'custom_ajax_logout',_wpnonce:nonceValue,},success:function(r){if('redirectToUrl'in wp2faWizardData&&''!=jQuery.trim(wp2faWizardData.redirectToUrl)){window.location.replace(wp2faWizardData.redirectToUrl);}else{removeShowParam();}}});}else{if('redirectToUrl'in wp2faWizardData&&''!=jQuery.trim(wp2faWizardData.redirectToUrl)){window.location.replace(wp2faWizardData.redirectToUrl);}else{removeShowParam();}}});}}},);});jQuery('body').on('click','.contains-hidden-inputs input[type="radio"]',function(e){if(jQuery(this).hasClass('js-nested')){return;}
jQuery(this).closest('.contains-hidden-inputs').find('.hidden').hide(200);if(jQuery(this).is('[data-unhide-when-checked]')){const thingToShow=jQuery(this).attr('data-unhide-when-checked');if(jQuery(this).is(':checked')){jQuery(thingToShow).slideDown(200);}}});jQuery(document).on('click','.dismiss-user-configure-nag',function(){const thisNotice=jQuery(this).closest('.notice');jQuery.ajax({url:wp2faData.ajaxURL,data:{action:'dismiss_nag'},complete:function(){jQuery(thisNotice).slideUp();},});});jQuery(document).on('click','.dismiss-user-reconfigure-nag',function(){const thisNotice=jQuery(this).closest('.notice');jQuery.ajax({url:wp2faData.ajaxURL,data:{action:'wp2fa_dismiss_reconfigure_nag'},complete:function(data){jQuery(thisNotice).slideUp();},});});jQuery(document).on('click','[data-trigger-account-unlock]',function(){const nonce=jQuery(this).attr('data-nonce');const account=jQuery(this).attr('data-account-to-unlock');jQuery.ajax({url:wp2faData.ajaxURL,data:{action:'unlock_account',user_id:account,wp_2fa_nonce:nonce}});});jQuery(document).on('click','.remove-2fa',function(e){e.preventDefault();});jQuery('body').on('click','#2fa-wizard-totp .button[name="next_step_setting"]',function(e){e.preventDefault;const currentSubStep=jQuery(this).closest('.step-setting-wrapper.active');const nextSubStep=jQuery(currentSubStep).nextAll('div:not(.hidden)').filter(':first');jQuery(currentSubStep).removeClass('active');jQuery(nextSubStep).addClass('active');updateStepTitles();});jQuery('body').on('click','.wp2fa-first-time-wizard .button[name="next_step_setting"]',function(e){e.preventDefault;const currentSubStep=jQuery(this).closest('.step-setting-wrapper.active');const nextSubStep=jQuery(currentSubStep).nextAll('div:not(.hidden)').filter(':first');jQuery(currentSubStep).removeClass('active');jQuery(nextSubStep).addClass('active');updateStepTitles();});jQuery(document).on('click','.modal_cancel',function(e){e.preventDefault();if(jQuery('#notify-users').length){MicroModal.show('notify-users');jQuery('.button-confirm').blur();}});jQuery(document).on('click touchend','.button-confirm',function(e){e.preventDefault();MicroModal.close('configure-2fa');MicroModal.close('notify-users');jQuery('.inline-helper').remove();});jQuery(document).on('click touchend','.button-decline',function(e){e.preventDefault();});jQuery(document).on('click','#close-settings',function(e){e.preventDefault();MicroModal.close('notify-admin-settings-page');window.location.replace(jQuery(this).data('redirect-url'));});jQuery(document).on('click','.first-time-wizard',function(e){e.preventDefault();MicroModal.show('notify-admin-settings-page');});jQuery(document).on('click','[data-trigger-remove-2fa]',function(){const nonce=jQuery(this).attr('data-nonce');const account=jQuery(this).attr('data-user-id');jQuery.ajax({url:wp2faData.ajaxURL,data:{action:'remove_user_2fa',user_id:account,wp_2fa_nonce:nonce},complete:function(data){location.reload();},});});jQuery(document).on('click','[data-trigger-remove-2fa-backup-email]',function(){const nonce=jQuery(this).attr('data-nonce');const account=jQuery(this).attr('data-user-id');jQuery.ajax({url:wp2faData.ajaxURL,data:{action:'remove_backup_email',user_id:account,wp_2fa_nonce:nonce},complete:function(data){location.reload();},});});jQuery(document).on('click','[data-submit-2fa-form]',function(e){jQuery('#submit').click();});function validateEmail(email){var re=/\S+@\S+\.\S+/;return re.test(email);}
jQuery(document).on('click','[data-trigger-setup-email]',function(e){let actionToRun='send_authentication_setup_email';var emailAddress=false;var inputUsed='';if(jQuery('#use_custom_email').prop('checked')){emailAddress=jQuery('#custom-email-address').val();inputUsed=jQuery('#custom-email-address');}else{emailAddress=jQuery('#use_wp_email').val();inputUsed=jQuery('#use_wp_email');}
if(typeof jQuery(this).data('oob-test')!=='undefined'){actionToRun='send_authentication_oob_setup_email';emailAddress=jQuery('#use_wp_oob_email').val();if(jQuery('#use_custom_oob_email').prop('checked')){emailAddress=jQuery('#custom-oob-email-address').val();inputUsed=jQuery('#custom-oob-email-address');}}
if(!validateEmail(emailAddress)||false==emailAddress){e.preventDefault();let errMsg=jQuery('#2fa-error-msg');if(errMsg.length){errMsg.remove();}
if(jQuery(this).hasClass('resend-email-code')){if(jQuery('#wp-2fa-email-authcode').is(":visible")){inputUsed=jQuery('#wp-2fa-email-authcode');}else if(jQuery('#wp-2fa-oob-authcode').is(":visible")){inputUsed=jQuery('#wp-2fa-oob-authcode');}}
jQuery('<span id="2fa-error-msg" style="color:red;">'+wp2faData.invalidEmail+'</span>').insertAfter(inputUsed);return false;}
if(jQuery(this).hasClass('resend-email-code')){var updateBtnText=true;var originalBtnText=jQuery(this).text();if(jQuery('#wp-2fa-email-authcode').is(":visible")){inputUsed=jQuery('#wp-2fa-email-authcode');}else if(jQuery('#wp-2fa-oob-authcode').is(":visible")){inputUsed=jQuery('#wp-2fa-oob-authcode');}}else{const currentSubStep=jQuery(this).closest('.step-setting-wrapper.active');const nextSubStep=currentSubStep.nextAll('div:not(.hidden)').filter(':first');jQuery(currentSubStep).removeClass('active');nextSubStep.addClass('active');updateStepTitles();}
const userID=jQuery(this).attr('data-user-id');const nonce=jQuery(this).attr('data-nonce');const thisBtn=jQuery(this);jQuery.ajax({type:'POST',dataType:'json',url:wp2faData.ajaxURL,data:{action:actionToRun,email_address:emailAddress,user_id:userID,nonce:nonce},error:function(jqXHR,textStatus,errorThrown){if(false===jqXHR.responseJSON.success){let errMsg=jQuery('#2fa-error-msg');if(errMsg.length){errMsg.remove();}
jQuery('<span id="2fa-error-msg" style="color:red;">'+jqXHR.responseJSON.data[0].message+'</span>').insertAfter(inputUsed);}},complete:function(data){},success:function(data){let errMsg=jQuery('#2fa-error-msg');if(errMsg.length){errMsg.remove();}
if(updateBtnText){jQuery(thisBtn).find('span').fadeTo(100,0,function(){jQuery(thisBtn).find('span').delay(100);jQuery(thisBtn).find('span').text(wp2faWizardData.codeReSentText);jQuery(thisBtn).find('span').fadeTo(100,1);});setTimeout(function(){jQuery(thisBtn).find('span').fadeTo(100,0,function(){jQuery(thisBtn).find('span').delay(100);jQuery(thisBtn).find('span').text(originalBtnText);jQuery(thisBtn).find('span').fadeTo(100,1);});},2500);}}});});jQuery(document).on('change','[name="wp_2fa_enabled_methods"]',function(event){var step=jQuery('[name="wp_2fa_enabled_methods"]:checked').val();if(undefined===step){jQuery('.2fa-choose-method[data-name]').removeAttr('data-next-step');}else{jQuery('.2fa-choose-method[data-name]').attr('data-next-step',`2fa-wizard-${step}`);}});jQuery('body').on('click','.button[data-name="next_step_setting_modal_wizard"]',function(e){e.preventDefault;var nextStep=jQuery(this).attr('data-next-step');if(undefined===nextStep){var nextStep=jQuery('[name="wp_2fa_enabled_methods"]:checked').val();jQuery('.2fa-choose-method[data-name]').attr('data-next-step',`2fa-wizard-${nextStep}`);}
if(nextStep){const currentSubStep=jQuery(this).parent().parent().find('.active').not('.step-setting-wrapper');const nextSubStep=jQuery(`#${nextStep}`);jQuery(this).parent().parent().find('.active').not('.step-setting-wrapper').removeClass('active');jQuery('.wizard-step.active').removeClass('active');jQuery(nextSubStep).addClass('active');var in_el=jQuery(nextSubStep).find("input[type=text]");if(!in_el.length){in_el=jQuery(nextSubStep).find("input[type=password]");}
if(in_el.length){in_el.focus();}}else{const currentSubStep=jQuery(this).parent().parent().find('.active').not('.step-setting-wrapper');const nextSubStep=jQuery(currentSubStep).next();jQuery('.wizard-step.active').removeClass('active');jQuery(nextSubStep).addClass('active');}
jQuery('.inline-helper').remove();});jQuery('body').on('click','.button[data-trigger-generate-backup-codes]',function(e){e.preventDefault();const actionToRun='wp2fa_run_ajax_generate_json';const nonceValue=jQuery(this).attr('data-nonce');const currentSubStep=jQuery(this).closest('.step-setting-wrapper.active');const nextSubStep=jQuery(currentSubStep).nextAll('div:not(.hidden)').filter(':first');jQuery(currentSubStep).removeClass('active');jQuery(nextSubStep).addClass('active');updateStepTitles();jQuery.ajax({type:'POST',dataType:'json',url:wp2faData.ajaxURL,data:{action:actionToRun,_wpnonce:nonceValue},complete:function(data){jQuery('#backup-codes-wrapper').slideUp(0);jQuery('.wp2fa-modal.is-open #backup-codes-wrapper, .wp2fa-setup-content #backup-codes-wrapper').val('');var codes=jQuery.parseJSON(data.responseText);var codes=codes.data['codes'];jQuery.each(codes,function(index,value){var oldValue=jQuery('.wp2fa-modal.is-open #backup-codes-wrapper, .wp2fa-setup-content #backup-codes-wrapper').val();var counter=index+1;jQuery('.wp2fa-modal.is-open #backup-codes-wrapper, .wp2fa-setup-content #backup-codes-wrapper').val(oldValue+counter+': '+`${value} \n`);});jQuery('#backup-codes-wrapper').slideDown(500);jQuery('.close-wizard-link').text(wp2faWizardData.readyText).fadeIn(50);}},);});jQuery('body').on('click','.button[data-trigger-reset-key]',function(e){e.preventDefault();if(jQuery('.qr-code-wrapper').length){jQuery('.qr-code-wrapper').addClass('regenerating');}
var doReload=jQuery(this).attr('data-trigger-reset-key');const thisButton=jQuery(this);const actionToRun='regenerate_authentication_key';const nonceValue=jQuery(this).attr('data-nonce');const userID=jQuery(this).attr('data-user-id');jQuery.ajax({type:'POST',dataType:'json',url:wp2faData.ajaxURL,data:{action:actionToRun,_wpnonce:nonceValue,user_id:userID},complete:function(data){if(jQuery('.change-2fa-confirm.hidden').length){jQuery('.change-2fa-confirm.hidden').trigger('click');}
if(jQuery('.app-key').length){jQuery('#wp-2fa-totp-qrcode').attr('src',data.responseJSON.data['qr']);jQuery('.app-key').val(data.responseJSON.data['key']);jQuery('[name="wp-2fa-totp-key"]').val(data.responseJSON.data['key']);setTimeout(function(){jQuery('.qr-code-wrapper').removeClass('regenerating');},500);}}},);});jQuery('body').on('click','.button[data-trigger-backup-code-email]',function(e){e.preventDefault();const thisButton=jQuery(this);const actionToRun='send_backup_codes_email';const nonceValue=jQuery(this).attr('data-nonce');const userID=jQuery(this).attr('data-user-id');const codesWrapper=JSON.stringify(jQuery('.active #backup-codes-wrapper').val());jQuery.ajax({type:'POST',dataType:'json',url:wp2faData.ajaxURL,data:{action:actionToRun,_wpnonce:nonceValue,user_id:userID,codes:codesWrapper},complete:function(data){jQuery('.button[data-trigger-backup-code-email]').text(wp2faWizardData.backupCodesSent).attr('value',wp2faWizardData.backupCodesSent);}},);});jQuery('body').on('click','.click-to-copy',function(e){var copyText=jQuery(this).prev();copyText.select();if(typeof copyText.setSelectionRange!=="undefined"){copyText.setSelectionRange(0,99999);}
navigator.clipboard.writeText(copyText[0].value);jQuery(this).addClass('done').html('Copied');});jQuery('body').on('click','.button[data-trigger-backup-code-copy]',function(e){e.preventDefault();var copyText=jQuery('.active #backup-codes-wrapper');copyText.select();if(typeof copyText.setSelectionRange!=="undefined"){copyText.setSelectionRange(0,99999);}
navigator.clipboard.writeText(copyText[0].value);jQuery(this).addClass('done').html('Copied');});jQuery('body').on('click','.button[data-trigger-backup-code-download]',function(e){e.preventDefault();const userName=jQuery(this).attr('data-user');const websiteURL=jQuery(this).attr('data-website-url');const preamble=`${wp2faWizardData.codesPreamble} ${userName} on the website ${websiteURL}:\n\n`;var codesWrapper=jQuery('.active #backup-codes-wrapper').val().split(' ').join('\n');download('backup_codes.txt',preamble+codesWrapper);});jQuery('body').on('click','.button[data-trigger-print]',function(e){e.preventDefault();const userName=jQuery(this).attr('data-user-id');const websiteURL=jQuery(this).attr('data-website-url');const preamble=`${wp2faWizardData.codesPreamble} ${userName} on the website ${websiteURL}:\n\n`;const divToPrint=jQuery('.active #backup-codes-wrapper').val();const newWin=window.open('','Print-Window');newWin.document.open();newWin.document.write(`<html><body onload="window.print()">${preamble}</br></br>${divToPrint}</body></html>`);newWin.document.close();setTimeout(function(){newWin.close();},10);});function download(filename,text){const element=document.createElement('a');element.setAttribute('href',`data:text/plain;charset=utf-8,${encodeURIComponent( text )}`);element.setAttribute('download',filename);element.style.display='none';document.body.appendChild(element);element.click();document.body.removeChild(element);}
jQuery(document).on('click','#custom-email-address',function(){jQuery('#use_custom_email').prop('checked',true);});jQuery(document).on('click','#custom-oob-email-address',function(){jQuery('#use_custom_oob_email').prop('checked',true);});jQuery(document).on('click','[data-check-on-click]',function(){const thingToCheck=jQuery(this).attr('data-check-on-click');jQuery(thingToCheck).prop('checked',true);});jQuery(document).on('click','[data-trigger-submit-form]',function(e){e.preventDefault();const thingToSubmit=jQuery(this).attr('data-trigger-submit-form');jQuery('.change-2fa-confirm').trigger('click');});jQuery(document).on('click','[data-reload]',function(e){removeShowParam();});window.removeShowParam=function(){let url=new URL(location.href);let params=new URLSearchParams(url.search);params.delete('show');location.replace(`${location.pathname}?${params}`);}
jQuery('[name="wp_2fa_settings[enforcement-policy]"]').on("input",function(){if(jQuery('input[name="wp_2fa_settings[enforcement-policy]"]:checked').val()!=='do-not-enforce'){jQuery('[data-step-title="Exclude users"]').removeClass('hidden');updateStepTitles();}else{jQuery('[data-step-title="Exclude users"]').addClass('hidden');updateStepTitles();}});jQuery('body').on('click','.iti__flag-container',function(e){var isExpand=(jQuery('.iti__selected-flag').attr('aria-expanded'))?'expand-panel':'';jQuery(this).closest('.wizard-step.active').toggleClass(isExpand);});jQuery('body').on('click','.step-setting-wrapper #all-users, .step-setting-wrapper #certain-roles-only',function(e){jQuery('.step-setting-wrapper.active .continue-wizard').removeClass('hidden');jQuery('.step-setting-wrapper.active .save-wizard').addClass('hidden');});jQuery('body').on('click','.step-setting-wrapper #do-not-enforce',function(e){jQuery('.step-setting-wrapper.active .continue-wizard').addClass('hidden');jQuery('.step-setting-wrapper.active .save-wizard').removeClass('hidden');});});window.onresize=function(){checkWizardOffset();}
function checkWizardOffset(){var elem=document.querySelector('.setup-wizard-wrapper');if(elem){var bounding=elem.getBoundingClientRect();if(bounding.top<0){var clientHeight=bounding.height / 2;elem.style.cssText+='top: '+clientHeight+'px';}}}
window.wp2fa_fireWizard=function(){jQuery('.verification-response span').remove();jQuery('#configure-2fa .wizard-step.active, #configure-2fa .step-setting-wrapper.active').removeClass('active');jQuery('#configure-2fa .wizard-step:first-of-type, #configure-2fa .step-setting-wrapper:first-of-type').addClass('active');jQuery('.modal__content input:not([type="radio"]):not([type="hidden"])').not('.app-key').val('');MicroModal.show('configure-2fa');if(jQuery('input#basic').is(':visible')){jQuery('input#basic').trigger("click");}else{if(jQuery('input#geek').is(':visible')){jQuery('input#geek').trigger("click");}else{if(jQuery('input#oob').length){jQuery('input#oob').trigger("click");}else if(jQuery('input#authy').length){jQuery('input#authy').trigger("click");}else if(jQuery('input#twilio').length){jQuery('input#twilio').trigger("click");}}}
jQuery('[name="wp_2fa_enabled_methods"]').change();if(1===jQuery('.wizard-step.active .option-pill').length){jQuery('.wp-2fa-button-primary.2fa-choose-method').trigger("click");jQuery('#configure-2fa .wizard-step:first-of-type, #configure-2fa input:radio[name=wp_2fa_enabled_methods]:first').attr("checked",true);}else{jQuery('#configure-2fa .wizard-step:first-of-type, #configure-2fa input:radio[name=wp_2fa_enabled_methods]:first').prop("checked",true);jQuery('[name="wp_2fa_enabled_methods"]').change();jQuery('#configure-2fa .wizard-step:first-of-type, #configure-2fa input:radio[name=wp_2fa_enabled_methods]:first').trigger("click");}};
}
catch(e){console.error("An error has occurred common.js: "+e.stack);}
dist/css/setup-wizard.css000064400000120423150755130600011453 0ustar00@charset "UTF-8";
#excluded_users_buttons,#excluded_roles_buttons,#enforced_roles_buttons,#enforced_users_buttons,#excluded_sites_buttons{display:inline}.wp2fa-setup-content #excluded_users_buttons,.wp2fa-setup-content #excluded_roles_buttons{display:block;margin-top:20px}.user-btn{position:relative;margin-left:10px !important}.user-btn .remove-item{background:red;height:14px;width:14px;position:absolute;color:#fff;border-radius:7px;line-height:11px;text-align:center;font-size:10px;display:block;right:-7px;top:-5px}.mt-5px{margin-top:7px;display:inline-block}label.radio-inline{padding-left:8px}.danger-zone-wrapper{padding:15px;border:1px solid red;border-radius:3px;margin-top:15px}.learn_more_link{display:inline-block;margin-left:10px;margin-top:6px}.wp-2fa-settings-wrapper .disabled{opacity:0.5;pointer-events:none}.wp-2fa-settings-wrapper .disabled *{pointer-events:none}.wp-2fa-settings-wrapper{max-width:1010px}.wp-2fa-settings-wrapper h2{margin:3px 0 0}.wp-2fa-settings-wrapper .method-title em{font-style:normal;font-weight:500;position:relative;top:6px}.wp-2fa-settings-wrapper p.description{font-size:13px}.wp-2fa-settings-wrapper:not(.setup-wizard-wrapper) .description fieldset label+label{padding-left:10px !important}label.disabled{opacity:0.5;cursor:default}.button.has-spinner{padding-right:0 !important}.notice-after-button{margin-left:0px !important;padding:5px 10px !important;clear:left;display:block}input.error{border-color:#dc3232;background:#ffebee}.wp-2fa-user-profile-form .button{margin-right:5px}.wp-submenu a[href="wp-2fa-setup"]{display:none !important}@media (min-width:1200px){.min-input-width{min-width:360px}}.wp2fa-form-styles .select2-selection--multiple{min-height:36px;height:auto;overflow-y:auto}.wp2fa-form-styles .select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:4px;margin-bottom:0px;padding:0 5px}.wp2fa-form-styles .select2-container .select2-search--inline{float:left;height:15px}.wp2fa-form-styles .select2-container--default .select2-search--inline .select2-search__field{height:13px;margin-top:0px}.wp2fa-form-styles .select2-container--default .select2-selection--multiple{padding-top:3px}.wp2fa-form-styles .wp2fa-setup-actions .button-secondary{color:#555 !important}.wp2fa-form-styles .wp2fa-setup-actions .button-secondary:hover{color:#fff !important}#exclusion_settings_wrapper.disabled{height:0;overflow:hidden}#notify-admin-settings-page{text-align:justify}.wp2fa-quota-exceeded-notice .button{margin-right:3px}.method-wrapper{margin-bottom:3px;border-bottom:1px solid #c3c4c7;padding-bottom:20px}.certain-users-only-inputs{margin-bottom:15px}a[href*="wp-2fa-premium-features"]{color:#ADFF2F !important}.wp-2fa-nag br{display:none}#wp-2fa-side-banner{background:#fff;padding:24px 4px;border:1px solid #bbb;position:fixed;width:280px;right:40px;bottom:40px;text-align:center}#wp-2fa-side-banner p{font-size:16px;font-weight:700}#wp-2fa-side-banner ul{margin-bottom:20px;padding:0 15px}#wp-2fa-side-banner li{padding-left:25px;overflow:hidden;position:relative;margin-bottom:10px;text-align:left}#wp-2fa-side-banner a.link{position:relative;top:5px;margin-left:15px}#wp-2fa-side-banner .dashicons{position:absolute;left:0;color:#4776ff}#wp-2fa-side-banner .button-primary{background:#4776ff;border-color:#4776ff;color:#fff;text-decoration:none;text-shadow:none}[dir="rtl"] #wp-2fa-side-banner{right:auto;left:40px}@media (max-width:1320px){.wp-2fa-settings-wrapper{max-width:580px}}@media (max-width:1530px) and (min-width:1320px){.wp-2fa-settings-wrapper{max-width:800px}}@media (max-width:1040px){#wp-2fa-side-banner{display:none}}@media (min-width:1040px){.ui-dialog{min-width:355px}}.wp-2fa-user-profile-form .qr-btn,.wp-2fa-user-profile-form .click-to-copy{text-decoration:none;font-size:13px;line-height:2.15384615;min-height:30px;margin:0;padding:0 10px;cursor:pointer;border-width:1px;border-style:solid;-webkit-appearance:none;border-radius:3px;white-space:nowrap;box-sizing:border-box;background:#2271b1;border-color:#2271b1;color:#fff;text-decoration:none;text-shadow:none;display:list-item;width:140px}.wp-2fa-user-profile-form .qr-btn:hover,.wp-2fa-user-profile-form .click-to-copy:hover{background:#135e96;border-color:#135e96;color:#fff}.wp-2fa-user-profile-form #app-key-input{min-width:320px}.mt-5px{margin-top:7px;display:inline-block}label.radio-inline{padding-left:8px}.wp2fa-modal{font-family:helvetica}.modal__overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0, 0, 0, 0.6);display:flex;justify-content:center;align-items:center;z-index:9999}.modal__container{background-color:#fff;padding:40px 40px 30px;max-width:500px;max-height:100vh;border-radius:4px;overflow-y:auto;box-sizing:border-box;z-index:1500}.modal__header{display:flex;justify-content:space-between;align-items:center}.modal__title{margin-top:0;margin-bottom:0;font-weight:600;font-size:1.25rem;line-height:1.25;color:#222;box-sizing:border-box}.modal__close{border:0;outline:none;z-index:10}.modal__close:hover{cursor:pointer}.modal__header .modal__close:before,.wp2fa-modal .modal__close:before{content:"✕"}.modal__content{font-size:13px;font-weight:500;height:100%;line-height:24px;margin-bottom:2px;color:#222;margin-top:0;word-break:keep-all;width:100%}.modal__content p{font-size:13px;font-weight:500;line-height:24px;margin-bottom:2px;color:#222;margin-top:0;word-break:keep-all;width:100%}.modal__content label{font-size:14px;line-height:28px;color:#222;margin-top:0;word-break:keep-all;width:100%;display:block}.modal__content p.description{font-size:12px;line-height:24px;opacity:0.9}.modal__content .apps-wrapper{position:relative;overflow:hidden;display:flex}.modal__content .apps-wrapper .app-logo{display:flex;align-self:center;flex:1;padding:0 13px}.modal__content .apps-wrapper .app-logo img{margin:0 auto;width:auto;max-width:100%}.modal__content .wp2fa-setup-actions{clear:both}.modal__content .iti--allow-dropdown{width:100%}.modal__btn-primary{background-color:#00449e;color:#fff}.enable_styling .modal__content input[type="radio"]{margin-left:0;appearance:auto;width:auto;height:auto;background:transparent}.enable_styling .modal__content input[type="radio"]:after{display:none}.enable_styling .modal__content input[type="radio"]:focus{border-color:transparent;box-shadow:none;outline:none}.enable_styling .modal__content input:not([type="radio"]):not(.app-key){line-height:1;margin-bottom:10px;min-height:2pc;min-width:15pc;padding:5px;margin:0px;border:none;border-bottom:3px solid #d0e5ff;width:100%;padding:10px 0;font-size:16px;font-weight:700;color:#4498ff;box-shadow:none}.enable_styling .modal__content input:not([type="radio"]):focus-visible{border-bottom:3px solid #4498ff;outline:none}.enable_styling .modal__content input:not([type="radio"]):-internal-autofill-selected{background-color:transparent}.wizard-step .iti--allow-dropdown input[type=tel]{z-index:100;background:transparent}.wizard-step .iti__flag-container{width:auto;z-index:101}.wizard-step.expand-panel .iti__flag-container{width:100%}.mepr-form .iti__country-list,.mepr-form .iti__flag-container{width:auto}@keyframes mmfadeIn{from{opacity:0}to{opacity:1}}@keyframes mmfadeOut{from{opacity:1}to{opacity:0}}@keyframes mmslideIn{from{transform:translateY(15%)}to{transform:translateY(0)}}@keyframes mmslideOut{from{transform:translateY(0)}to{transform:translateY(-10%)}}.micromodal-slide{display:none}.micromodal-slide.is-open{display:block}.micromodal-slide[aria-hidden="false"] .modal__overlay{animation:mmfadeIn 0.3s cubic-bezier(0, 0, 0.2, 1)}.micromodal-slide[aria-hidden="false"] .modal__container{animation:mmslideIn 0.3s cubic-bezier(0, 0, 0.2, 1)}.micromodal-slide[aria-hidden="true"] .modal__overlay{animation:mmfadeOut 0.3s cubic-bezier(0, 0, 0.2, 1)}.micromodal-slide[aria-hidden="true"] .modal__container{animation:mmslideOut 0.3s cubic-bezier(0, 0, 0.2, 1)}.micromodal-slide .modal__container,.micromodal-slide .modal__overlay{will-change:transform}.danger-zone-wrapper{padding:15px;border:1px solid red;border-radius:3px;margin-top:15px}.learn_more_link{display:inline-block;margin-left:10px;margin-top:6px}.wp-2fa-settings-wrapper .disabled{opacity:0.5;pointer-events:none}.wp-2fa-settings-wrapper .disabled *{pointer-events:none}.wp2fa-modal .modal__container{max-height:70vh;max-width:872px;min-width:30vw;position:relative}.wp2fa-modal .modal__close{background:transparent;color:#4498ff !important;font-size:15px;font-weight:700;position:absolute;right:20px;text-decoration:none;top:20px;background-color:transparent !important}.wp2fa-modal .modal__close :hover{color:#888;cursor:pointer}.wp2fa-modal input[type=radio]:checked:before{display:none}.wizard-step:not(.active),.step-setting-wrapper:not(.active){display:none}.wizard-step.active,.step-setting-wrapper.active{-webkit-animation:fadein 0.5s;-moz-animation:fadein 0.5s;-ms-animation:fadein 0.5s;-o-animation:fadein 0.5s;animation:fadein 0.5s}#configure-2fa .wp2fa-setup-actions,#configure-2fa-backup-codes .wp2fa-setup-actions{margin-top:25px}.modal__btn+.modal__btn{margin-left:10px}.wp-2fa-configuration-form td.backup-methods-label,.wp-2fa-configuration-form th{display:none}.wp2fa-modal h4,.wp2fa-modal h3{word-wrap:break-word;font-family:helvetica;font-size:22px;font-weight:700;margin-bottom:15px;margin-top:0;margin:0 0 10px 0}.wp2fa-modal.enable_styling h4,.wp2fa-modal.enable_styling h3{font-family:helvetica !important}.wp2fa-modal fieldset{padding:0;border:0}.wp2fa-modal ol{margin:0 0 15px;padding-left:17px}.wp2fa-modal ol li{position:relative;padding:6px}.wp2fa-modal .modal__content code.app-key{font-size:16px;padding:7px 12px;word-break:break-all;background:#efefef;color:#222}label+.verification-response{margin-top:20px}.verification-response:not(:empty){border:3px solid red;font-size:9pt;margin-bottom:15px;padding:10px;background:#ffe4e4;border-radius:10px;font-size:9pt;line-height:24px;width:100%}.default_styling .verification-response:not(:empty){width:calc(100% - 30px)}.wp-2fa-configuration-form .button.enable_styling{background-color:#fff !important;color:#3e6bff !important;font-size:14px;line-height:14px;margin-bottom:10px;outline:none;padding:13px 13pt;text-align:center;text-decoration:none;font-weight:800;display:inline-block;border:3px solid #3e6bff !important;border-radius:30px;text-transform:uppercase;letter-spacing:0.3px}.wp-2fa-configuration-form .button.enable_styling:hover{background-color:#3e6bff !important;color:#fff !important;border:3px solid #3e6bff !important}.wp2fa-modal.enable_styling .modal__content .wp2fa-setup-actions .button:focus,.wp2fa-modal.enable_styling .modal__content .modal__btn:focus,.wp2fa-modal.enable_styling .modal__content .modal__btn.button-confirm:focus,.wp2fa-modal.enable_styling .modal__content .modal__btn.button-decline:focus{box-shadow:none}.wp2fa-modal.enable_styling .button+.button{margin-left:10px}.modal__footer{margin-top:20px}.enable_styling .wp-2fa-button-primary,.enable_styling .wp-2fa-button-secondary,.enable_styling #wizard-api-key button,.enable_styling #wizard-sid-key button{background-color:#fff;color:#3e6bff;font-size:14px;line-height:14px;margin-bottom:10px;outline:none;padding:13px 13pt;text-align:center;text-decoration:none;font-weight:800;display:inline-block;border:3px solid #3e6bff;border-radius:30px;text-transform:uppercase;letter-spacing:0.3px;transition:all 0.2s ease-in-out}.enable_styling .wp-2fa-button-secondary{color:#555;border:3px solid #555}.enable_styling .wp-2fa-configuration-form .button.wp-2fa-button-secondary{color:#555 !important;border:3px solid #555 !important}.enable_styling .wp-2fa-button-primary:focus,.enable_styling .wp-2fa-button-secondary:focus{box-shadow:none}.enable_styling .wp-2fa-button-primary:hover{background-color:#3e6bff;color:#fff;border:3px solid #3e6bff;transition:all 0.4s ease-in-out}.enable_styling .wp-2fa-button-secondary:hover{background-color:#555;color:#fff;border:3px solid #555;transition:all 0.4s ease-in-out}.enable_styling .wp-2fa-configuration-form .button.wp-2fa-button-secondary:hover,.enable_styling #wizard-api-key button:hover,.enable_styling #wizard-sid-key button:hover{background-color:#555 !important;color:#fff !important;border:3px solid #555 !important}.wp-2fa-configuration-form .button{margin-right:10px}.wp-2fa-configuration-form .button:hover{cursor:pointer}.qr-code-wrapper{position:relative;overflow:hidden;float:right}#notify-users .modal__container *{opacity:1;transition:all 0.3s ease-in-out}#notify-users .modal__container.saving *{opacity:0;transition:all 0.3s ease-in-out}.qr-code-wrapper.regenerating img{visibility:hidden}#backup-codes-wrapper{border:none;min-height:2pc;min-width:15pc;padding:10px 0;width:100%;background-color:transparent !important}#backup-codes-wrapper :focus-visible{outline:none}#backup-codes-wrapper:focus-visible{outline:none}.qr-code-wrapper.regenerating:before,#notify-users .modal__container.saving:before{content:"";box-sizing:border-box;position:absolute;top:50%;left:50%;width:20px;height:20px;margin-top:-10px;margin-left:-10px;border-radius:50%;border:2px solid #ccc;border-top-color:#000;animation:spinner 0.6s linear infinite}@keyframes spinner{to{transform:rotate(360deg)}}.default_styling .radio-cells .option-pill{position:relative;margin-bottom:11px}.default_styling .radio-cells .option-pill label{padding-right:40px;font-size:12px}.default_styling .wizard-tooltip{display:none !important}.default_styling input[type=checkbox],.default_styling input[type=radio]{-webkit-appearance:auto}@media screen and (max-width:1299px){.wp2fa-modal .modal__container{max-width:96vw}#wp-2fa-totp-qrcode{max-width:100%;text-align:center;margin:15px auto;display:block}.qr-code-wrapper{float:none}.step-setting-wrapper br{display:none}.enable_styling .radio-cells{display:flex;flex-wrap:wrap;margin:0px 0 20px;width:calc(100% + 10px);position:relative;left:-5px;flex-wrap:wrap}.enable_styling .radio-cells input:not([type="radio"]){margin-top:5px}.enable_styling .radio-cells .option-pill{flex-basis:0;flex-grow:1;border:3px solid #eee;border-radius:10px;padding:10px;margin:5px;font-size:11px;position:relative}.enable_styling .radio-cells .option-pill p{height:100%}.enable_styling .radio-cells.max-3 .option-pill{flex:33.333%;display:flex;flex-direction:column}.enable_styling .radio-cells.max-3 .option-pill p{flex:1}.enable_styling .radio-cells .option-pill.isSelected{border:3px solid #007cba}.enable_styling .radio-cells .option-pill label{display:block;font-size:13px;line-height:24px;font-weight:500;margin-bottom:2px;height:100%;max-width:calc(100% - 20px)}.enable_styling .radio-cells .option-pill p{font-size:12px;line-height:23px;opacity:0.9;height:auto}.enable_styling .option-pill{margin-bottom:15px}.enable_styling .tooltip-content-wrapper{display:none}.wp2fa-modal .modal__content code br{display:block !important}.wp2fa-modal .modal__content code.app-key{width:100%;display:block;text-align:center;font-size:16px;margin-bottom:30px;background:#efefef;padding:5px;word-break:break-all}.wp2fa-modal .modal__content .apps-wrapper{margin:10px 0}.wp2fa-modal .modal__content a.app-logo{padding:0 5px}.wp2fa-modal .modal__content .wizard-tooltip{background:#3e6bff;color:white;height:18px;display:inline-block;width:18px;border-radius:50%;position:absolute;overflow:hidden;text-align:center;line-height:18px;font-weight:700;margin-left:4px;margin-top:3px;bottom:10px;right:17px}.wp2fa-modal .modal__content .inline-helper{background:#eee;padding:10px;border-radius:10px;display:block;margin-top:10px;font-size:12px;line-height:24px;display:none}.wp2fa-modal .modal__content .click-to-copy{border:2px solid #3e6bff;border-radius:14px;display:inline-block;font-size:10px;padding:0 9px;font-weight:700;line-height:16px}.wp2fa-modal .modal__content .click-to-copy.done{background-color:green !important;border:2px solid green;color:white}.wp2fa-modal .modal__content .click-to-copy:hover{background-color:#3e6bff;color:white;cursor:pointer;opacity:0.5}.wp2fa-modal .modal__content .app-key-wrapper{background:#eee;border-radius:4px;margin:10px 0 5px;overflow:hidden;padding:3px 7px}.wp2fa-modal .modal__content .app-key-wrapper input{background:transparent;border:none;display:inline-block;font-size:9pt;margin:0 10px 0 0;max-width:15pc;min-height:0;min-width:200px;padding:0;color:#888}.wp2fa-modal .modal__content .app-key-wrapper input:focus-visible,.wp2fa-modal .modal__content .app-key-wrapper input:focus{border:none;background-color:transparent;outline:none}.wp-2fa-configuration-form .button{width:auto;display:block;margin-bottom:10px;margin-left:0px;margin-right:0px;text-align:center}.wp2fa-modal .modal__content .wp2fa-setup-actions .button,.wp2fa-modal .modal__content .modal__btn{width:auto;display:inline-block;margin-bottom:10px;margin-left:0px;margin-right:0px;text-align:center}.modal__content input:not([type="radio"]){width:100%;margin-bottom:10px}.hide-on-mobile{display:none}.wp2fa-modal h4,.wp2fa-modal h3{font-size:20px}.wp2fa-modal h4{clear:both}}@media screen and (min-width:1300px){.wp2fa-modal .clear-both{clear:both;display:block;overflow:hidden}.wp2fa-modal .modal-50{width:50%;display:inline-block;float:left}.wp2fa-modal .modal-60{width:70%;display:inline-block;float:left}.wp2fa-modal .modal-60 .radio-cells{width:100%;padding-left:10px}.wp2fa-modal .modal-40{width:30%;float:left;display:flex;justify-content:center}.wp2fa-modal .mb-20{margin-bottom:20px}.wp2fa-modal.enable_styling p+.radio-cells,.wp2fa-modal.enable_styling p+fieldset{margin:20px 0;display:flex;flex-wrap:wrap}.wp2fa-modal.enable_styling .radio-cells{display:flex;flex-wrap:wrap;margin:0px 0 20px;width:calc(100% + 10px);position:relative;left:-5px;flex-wrap:wrap}.wp2fa-modal.enable_styling .radio-cells input:not([type="radio"]){margin-top:5px}.wp2fa-modal.enable_styling .radio-cells .option-pill{flex-basis:0;flex-grow:1;border:3px solid #eee;border-radius:10px;padding:10px;margin:5px;font-size:11px;position:relative}.wp2fa-modal.enable_styling .radio-cells .option-pill p{height:100%}.wp2fa-modal.enable_styling .radio-cells.max-3 .option-pill{flex:33.333%;display:flex;flex-direction:column}.wp2fa-modal.enable_styling .radio-cells.max-3 .option-pill p{flex:1}.wp2fa-modal.enable_styling .radio-cells .option-pill.isSelected{border:3px solid #007cba}.wp2fa-modal.enable_styling .radio-cells .option-pill label{display:block;font-size:13px;line-height:24px;font-weight:500;margin-bottom:2px;height:100%;max-width:calc(100% - 20px)}.wp2fa-modal.enable_styling .radio-cells .option-pill p{font-size:12px;line-height:23px;opacity:0.9;height:auto}.wp2fa-modal .wizard-tooltip{background:#3e6bff;color:white;height:18px;display:inline-block;width:18px;border-radius:50%;position:absolute;overflow:hidden;text-align:center;line-height:18px;font-weight:700;margin-left:4px;margin-top:3px;bottom:10px;right:17px}.wp2fa-modal .inline-helper{background:#eee;padding:10px;border-radius:10px;display:block;margin-top:10px;font-size:12px;line-height:24px;display:none}.wp2fa-modal.enable_styling .tooltip-content-wrapper{display:none}.wp2fa-modal .click-to-copy{border:2px solid #3e6bff;border-radius:14px;display:inline-block;font-size:10px;padding:0 9px;font-weight:700;line-height:16px}.wp2fa-modal .click-to-copy.done{background-color:green !important;border:2px solid green;color:white}.wp2fa-modal .click-to-copy:hover{background-color:#3e6bff;color:white;cursor:pointer;opacity:0.5}.wp2fa-modal .app-key-wrapper{background:#eee;border-radius:4px;margin:10px 0 5px;overflow:hidden;padding:3px 7px}.wp2fa-modal .app-key-wrapper input{background:transparent;border:none;display:inline-block;font-size:9pt;margin:0 10px 0 0;max-width:15pc;min-height:0;min-width:200px;padding:0;color:#888}.wp2fa-modal .app-key-wrapper input:focus-visible,.wp2fa-modal .app-key-wrapper input:focus{border:none;background-color:transparent;outline:none}.wp2fa-modal .option-pill{margin-bottom:15px}.wp2fa-modal .qr-code{float:left;width:100%;position:relative;left:-2%}.wp2fa-modal .mb-30{margin-bottom:30px}.show-on-mobile{display:none}}@media (max-width:500px){.modal__container{padding:25px}.option-pill:not(last-of-type){margin-bottom:40px}.wp2fa-modal .modal__content .apps-wrapper{display:block}.wp2fa-modal .modal__content a.app-logo{width:calc(33.33333% - 13px);display:inline-block;margin-bottom:5px}.wp2fa-modal h4,.wp2fa-modal h3{font-size:18px;margin-bottom:9px}#configure-2fa-backup-codes .wp2fa-setup-actions,#configure-2fa .wp2fa-setup-actions{margin-top:15px}.wp2fa-modal .modal__content .modal__btn,.wp2fa-modal .modal__content .wp2fa-setup-actions .button,.wp-2fa-configuration-form .button{display:block;margin-bottom:10px;margin-left:0;margin-right:0;text-align:center;width:auto;padding:10px 15px;font-size:12px}.wp2fa-modal .modal__content .radio-cells .option-pill{flex-basis:unset;flex-grow:1}.wp2fa-modal .modal__content p+.radio-cells{margin-top:20px}}@media screen and (min-width:801px) and (max-width:1299px){.wp2fa-modal .modal__content p+.radio-cells{margin-top:20px}.modal-50{width:50%;display:inline-block;float:left}.mb-20{margin-bottom:20px}.modal-60{width:70%;display:inline-block;float:left}.modal-60 .radio-cells{width:100%;padding-left:10px}.modal-40{width:30%;float:left;display:flex;justify-content:center}}@media screen and (min-width:1024px){.wp2fa-modal .modal__container{min-width:768px}}@media screen and (min-width:501px) and (max-width:800px){.modal-50{width:50%;display:inline-block;float:left}.modal-60{width:60%;display:inline-block;float:left}.modal-60 .radio-cells{width:100%;padding-left:10px}.mb-20{margin-bottom:20px}.modal-40{width:40%;float:left;display:flex;justify-content:center}}@media screen and (max-width:800px){.wp2fa-modal .modal__container{max-width:95vw;min-width:80vw}.wp2fa-modal .modal__content .wp2fa-setup-actions .button,.wp2fa-modal .modal__content .modal__btn{width:auto;display:inline-block;margin-bottom:10px;margin-left:0px;margin-right:0px;text-align:center}.wp2fa-modal .modal__close{right:15px;top:15px}}@media screen and (max-width:500px){.mb-20{margin-bottom:15px}.wp2fa-modal .modal__content .app-key-wrapper input{max-width:200px}.enable_styling .wp-2fa-button-primary,.enable_styling .wp-2fa-button-secondary{display:block !important;margin-left:0 !important;margin-right:0;width:100% !important}.wp2fa-modal .modal__content .wizard-tooltip{right:5px;bottom:5px}.verification-response:not(:empty){width:auto}}.wizard-custom-counter{counter-reset:step-counter;list-style:none;margin-bottom:0 !important}.wizard-custom-counter li{counter-increment:step-counter}.wizard-custom-counter li:last-of-type{margin-bottom:0 !important}.enable_styling .wizard-custom-counter li::before{content:counter(step-counter);background:#3e6bff;width:20px;height:20px;color:white;text-align:center;display:inline-block;line-height:20px;position:absolute;left:-20px;top:8px;font-size:14px;font-weight:700;border-radius:50px}.enable_styling #backup-codes-wrapper{border:none;border-bottom:3px solid #d0e5ff;color:#4498ff;font-size:1pc;font-weight:700;line-height:1;margin:0;min-height:2pc;min-width:15pc;padding:10px 0;width:100%;line-height:26px;background-color:transparent !important}.enable_styling #backup-codes-wrapper :focus-visible{border-bottom:3px solid #4498ff;outline:none}.enable_styling #backup-codes-wrapper:focus-visible{border-bottom:3px solid #4498ff;outline:none}.default_styling .wizard-custom-counter li::before{content:counter(step-counter);width:20px;height:20px;text-align:center;display:inline-block;line-height:20px;position:absolute;left:-20px;top:8px;font-size:14px;font-weight:700;border-radius:50px}.mb-0{margin-bottom:0 !important}.wp-2fa-user-profile-form{margin:0}.wp-2fa-user-profile-form:first-of-type{margin-top:20px}.wp-2fa-user-profile-form:last-of-type{margin-bottom:20px}.wp-2fa-user-profile-form tr+tr th,.wp-2fa-user-profile-form tr+tr td{padding-top:0}.remove-tr-padding th,.remove-tr-padding td{padding-bottom:0}body:not(.wp-admin) .wp-2fa-user-profile-form td,body:not(.wp-admin) .wp-2fa-user-profile-form th{padding:0;border:none}.wp-2fa-user-profile-form{border:none}.wp-2fa-button-primary+.wp-2fa-button-secondary{margin-left:10px}.modal-logo-wrapper{text-align:center;padding:0;margin:0}.wizard-step label[for="authy-token"]{min-height:70px;-webkit-transition:min-height 0.2s ease-in-out;-moz-transition:min-height 0.2s ease-in-out;-ms-transition:min-height 0.2s ease-in-out;-o-transition:min-height 0.2s ease-in-out;transition:min-height 0.2s ease-in-out}.wizard-step.expand-panel label[for="authy-token"]{min-height:300px;-webkit-transition:min-height 0.2s ease-in-out;-moz-transition:min-height 0.2s ease-in-out;-ms-transition:min-height 0.2s ease-in-out;-o-transition:min-height 0.2s ease-in-out;transition:min-height 0.2s ease-in-out}.wizard-step.expand-panel .authy-step-setting-wrapper{overflow:hidden}.wizard-step.expand-panel .iti__country-list{width:100%;max-height:240px}[aria-describedby="authy-diag"],[aria-describedby="twilio-diag"]{min-width:400px;padding:10px}[aria-describedby="authy-diag"] .ui-dialog-titlebar,[aria-describedby="twilio-diag"] .ui-dialog-titlebar{background:#fff;border-bottom:none;height:auto;font-size:18px;font-weight:600;line-height:2;padding:10px 36px 0 16px;word-wrap:break-word;font-family:helvetica;font-size:22px;font-weight:700;margin-bottom:15px;margin-top:0;margin:0 0 10px 0}[aria-describedby="authy-diag"] .ui-dialog-buttonpane,[aria-describedby="twilio-diag"] .ui-dialog-buttonpane{background:#fff;border-top:none;padding:16px}[aria-describedby="authy-diag"] .ui-dialog-buttonset .ui-button,[aria-describedby="twilio-diag"] .ui-dialog-buttonset .ui-button{background-color:#fff;color:#3e6bff;font-size:14px;line-height:14px;margin-bottom:10px;outline:none;padding:13px 13pt;text-align:center;text-decoration:none;font-weight:800;display:inline-block;border:3px solid #3e6bff;border-radius:30px;text-transform:uppercase;letter-spacing:0.3px;-webkit-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out;height:auto;margin-left:0}[aria-describedby="authy-diag"] .ui-dialog-buttonset .ui-button:hover,[aria-describedby="twilio-diag"] .ui-dialog-buttonset .ui-button:hover{background-color:#3e6bff;color:#fff}[aria-describedby="authy-diag"] .ui-dialog-buttonpane .ui-dialog-buttonset,[aria-describedby="twilio-diag"] .ui-dialog-buttonpane .ui-dialog-buttonset{float:left}[aria-describedby="authy-diag"] .ui-dialog-content,[aria-describedby="twilio-diag"] .ui-dialog-content{padding:0 16px 9px;overflow:auto}body{min-height:100vh}a{color:#0073aa}a:hover,a:active{color:#00a0d2}a:focus{color:#124964;box-shadow:0 0 0 1px #5b9dd9, 0 0 2px 1px rgba(30, 140, 190, 0.8)}.ie8 a:focus{outline:#5b9dd9 solid 1px}h1,h2{border-bottom:1px solid #ddd;clear:both;color:#666;font-size:24px;padding:0;padding-bottom:7px;font-weight:400}h3{font-size:16px}p,li,dd,dt{padding-bottom:2px;color:#222;font-size:13px;line-height:24px}code,.code{font-family:Consolas, Monaco, monospace}ul,ol,dl{padding:5px 5px 5px 22px}a img{border:0}abbr{border:0;font-variant:normal}fieldset{border:0;padding:0;margin:0}label{cursor:pointer}#logo{margin:6px 0 14px 0;padding:0 0 7px 0;border-bottom:none;text-align:center}#logo a{background-image:url(../images/w-logo-blue.png?ver=20131202);background-image:none, url(../images/wordpress-logo.svg?ver=20131107);background-size:84px;background-position:center top;background-repeat:no-repeat;color:#444;height:84px;font-size:20px;font-weight:400;line-height:1.3;margin:-130px auto 25px;padding:0;text-decoration:none;width:84px;text-indent:-9999px;outline:none;overflow:hidden;display:block}.step{margin:20px 0 15px;text-align:left;padding:0}th{text-align:left;padding:0}.language-chooser.wp-core-ui .step .button.button-large{height:36px;font-size:14px;line-height:2.35714285;vertical-align:middle}textarea{border:1px solid #ddd;font-family:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;width:100%;box-sizing:border-box}.form-table{border-collapse:collapse;margin-top:1em;width:100%}.form-table td{margin-bottom:9px;padding:10px 20px 10px 0;font-size:14px;vertical-align:top}.form-table th{font-size:14px;text-align:left;padding:10px 20px 10px 0;width:140px;vertical-align:top}.form-table code{line-height:1.28571428;font-size:14px}.form-table p{margin:4px 0 0 0;font-size:11px}.form-table input{line-height:1.33333333;font-size:15px;padding:3px 5px;border:1px solid #ddd;box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.07)}input,submit{font-family:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif}.form-table input[type=text],.form-table input[type=email],.form-table input[type=url],.form-table input[type=password]{width:218px}#pass-strength-result{width:218px}.form-table th p{font-weight:400}.form-table.install-success th{vertical-align:middle;padding:16px 20px 16px 0}.form-table.install-success td{vertical-align:middle;padding:16px 20px 16px 0}.form-table.install-success td p{margin:0;font-size:14px}.form-table.install-success td code{margin:0;font-size:18px}#error-page{margin-top:50px}#error-page p{font-size:14px;line-height:1.28571428;margin:25px 0 20px}#error-page code{font-family:Consolas, Monaco, monospace}.code{font-family:Consolas, Monaco, monospace}.message{border-left:4px solid #dc3232;padding:0.7em 0.6em;background-color:#fbeaea}#dbname,#uname,#pwd,#dbhost,#prefix,#user_login,#admin_email,#pass1,#pass2{direction:ltr}body.rtl{font-family:Tahoma, sans-serif}.rtl textarea,.rtl input,.rtl submit{font-family:Tahoma, sans-serif}:lang(he-il) body.rtl{font-family:Arial, sans-serif}:lang(he-il) .rtl textarea,:lang(he-il) .rtl input,:lang(he-il) .rtl submit{font-family:Arial, sans-serif}@media only screen and (max-width:799px){body{margin-top:115px}#logo a{margin:-125px auto 30px}}@media screen and (max-width:782px){.form-table{margin-top:0}.form-table th,.form-table td{display:block;width:auto;vertical-align:middle}.form-table th{padding:20px 0 0}.form-table td{padding:5px 0;border:0;margin:0}textarea,input{font-size:16px}.form-table td input[type="text"],.form-table td input[type="email"],.form-table td input[type="url"],.form-table td input[type="password"]{width:100%;font-size:16px;line-height:1.5;padding:7px 10px;display:block;max-width:none;box-sizing:border-box}.form-table td select,.form-table td textarea{width:100%;font-size:16px;line-height:1.5;padding:7px 10px;display:block;max-width:none;box-sizing:border-box}.form-table span.description{width:100%;font-size:16px;line-height:1.5;padding:7px 10px;display:block;max-width:none;box-sizing:border-box}.wp-pwd #pass1{padding-right:50px}.wp-pwd .button.wp-hide-pw{right:0}#pass-strength-result{width:100%}}body.language-chooser{max-width:300px}.language-chooser select{padding:8px;width:100%;display:block;border:1px solid #ddd;background:#fff;color:#32373c;font-size:16px;font-family:Arial, sans-serif;font-weight:400}.language-chooser select:focus{color:#32373c}.language-chooser select option:hover,.language-chooser select option:focus{color:#016087}.language-chooser p{text-align:right}.screen-reader-input,.screen-reader-text{border:0;clip:rect(1px, 1px, 1px, 1px);-webkit-clip-path:inset(50%);clip-path:inset(50%);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;word-wrap:normal !important}.spinner{background:url(../images/spinner.gif) no-repeat;background-size:20px 20px;visibility:hidden;opacity:0.7;filter:alpha(opacity=70);width:20px;height:20px;margin:2px 5px 0}.step .spinner{display:inline-block;vertical-align:middle;margin-right:15px}.button.hide-if-no-js,.hide-if-no-js{display:none}@media print,(-webkit-min-device-pixel-ratio:1.25),(min-resolution:120dpi){.spinner{background-image:url(../images/spinner-2x.gif)}}body{-webkit-box-shadow:none;box-shadow:none;background:#F8F9FB;padding:0}#wp2fa-logo{border:0;margin:0 0 24px;padding:0;text-align:center}#wp2fa-logo a{color:#3e6bff;text-decoration:none;font-weight:700;font-size:28px;margin-bottom:35px;display:block}#wp2fa-logo img{width:auto;max-width:80px;position:relative;left:-10px}.steps{display:-ms-flexbox;display:flex;list-style-type:none;margin:0;padding:0 0 25px;text-align:center}.steps li{-ms-flex:1 0 auto;flex:1 0 auto;font-weight:700;margin:0 0 5px;color:#b4b9be;padding-bottom:15px;position:relative}.steps li.is-active{color:#3e6bff}.steps li.is-active::before{border:4px solid #3e6bff;background:#3e6bff}.steps li::before{content:"";border:4px solid #b4b9be;border-radius:100%;width:4px;height:4px;position:absolute;bottom:0;left:50%;margin-left:-6px;margin-bottom:-8px;background:#b4b9be}.wp2fa-setup-content{-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.13);box-shadow:0 1px 3px rgba(0, 0, 0, 0.13);padding:30px 40px;margin:0 0 20px;background:#fff;overflow:hidden;zoom:1;border-radius:10px}.wp2fa-setup-content h3{font-size:24px;text-align:center;margin-bottom:30px;margin-top:5px;line-height:36px}.wp2fa-setup-content a{color:#3e6bff !important;text-decoration:none !important}.wp2fa-setup-content h4,.wp2fa-setup-content fieldset{line-height:1.5}.wp2fa-setup-actions{text-align:center;margin:40px auto 10px}.wp2fa-setup-actions .button,.wp2fa-setup-actions .button[data-check-authy-2fa]{background-color:#fff;border:3px solid #3e6bff;border-radius:30px;color:#3e6bff;display:inline-block;font-size:14px;font-weight:800;letter-spacing:0.3px;line-height:14px;margin-bottom:10px;outline:none;padding:13px 13pt;text-align:center;text-decoration:none;text-transform:uppercase;-webkit-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out}.wp2fa-setup-actions .button-primary{font-weight:700}.wp2fa-setup-actions .button-secondary{font-weight:700;background-color:#fff;border:2px solid #555;color:#555}.wp2fa-setup-actions .button-secondary:hover,.wp2fa-setup-actions .button-secondary:focus{background:#555;border-color:#555;-webkit-box-shadow:none;box-shadow:none;color:#fff}.wp2fa-setup-actions .button-primary:hover,.wp2fa-setup-actions .button-primary:focus{background:#3e6bff;border-color:#3e6bff;-webkit-box-shadow:none;box-shadow:none;color:#fff !important}.wp2fa-setup-footer{text-align:center}.wp2fa-setup-footer a{color:#3e6bff;font-size:14px;text-decoration:none}.wp2fa-setup-form label[for="editor-users-box"],.wp2fa-setup-form label[for="editor-roles-box"],.wp2fa-setup-form label[for="exuser-query-box"],.wp2fa-setup-form label[for="exrole-query-box"],.wp2fa-setup-form label[for="ipaddr-query-box"]{display:inline-block;margin:5px 0}.wp2fa-setup-form label[for="editor-users-box"] span,.wp2fa-setup-form label[for="editor-roles-box"] span,.wp2fa-setup-form label[for="exuser-query-box"] span,.wp2fa-setup-form label[for="exrole-query-box"] span,.wp2fa-setup-form label[for="ipaddr-query-box"] span{display:inline-block;min-width:100px}.sectoken-user,.sectoken-role,.sectoken-ip,.sectoken-other{display:inline-block;border-width:1px;border-style:solid;padding:2px 4px;margin:2px 0 0 2px;border-radius:3px;cursor:default;line-height:1.3;font-size:14px}.sectoken-user a,.sectoken-role a,.sectoken-ip a,.sectoken-other a{text-decoration:none;font-size:12px;font-weight:bold;color:#FFF;margin-left:2px;background:#BBB;border-radius:25px;height:14px;display:inline-block;vertical-align:middle;width:14px;text-align:center;line-height:12px}.sectoken-user a:hover,.sectoken-role a:hover,.sectoken-ip a:hover{background:#FB9}.sectoken-other{display:table;border-collapse:separate}.sectoken-other a:hover{background:#FB9}.sectoken-role{background:#EFE;border-color:#5B5}.sectoken-user{background:#EFF;border-color:#5BE}.sectoken-ip,.sectoken-other{background:#FFE;border-color:#ED5}p.description{font-size:13px;font-style:normal;margin:6px 0 10px}p.description:empty{display:none}p.description+p{margin:6px 0 10px}.setup-wizard-wrapper{font-family:helvetica;margin:0 auto;padding:20px 20px 10px 20px;-webkit-font-smoothing:subpixel-antialiased;max-width:780px;min-width:780px;top:50%;-ms-transform:translateY(-50%) translateX(-50%);transform:translateY(-50%) translateX(-50%);position:absolute;left:50%;-webkit-animation:fadein 0.5s;color:#222;font-family:helvetica;color:#222;font-size:13px;font-weight:500;line-height:24px;-moz-animation:fadein 0.5s;-ms-animation:fadein 0.5s;-o-animation:fadein 0.5s;animation:fadein 0.5s}.wp-2fa-settings-wrapper .method-title em{font-weight:700 !important}.wp-2fa-settings-wrapper .disabled ~,.wp-2fa-settings-wrapper .disabled~*{opacity:0.5 !important}.wp-2fa-settings-wrapper label{font-size:15px}.wp-2fa-settings-wrapper label+p.description{font-size:12px !important;color:#8c8c8c}.wp-2fa-settings-wrapper .method-title{display:none}@keyframes fadein{from{opacity:0}to{opacity:1}}@-moz-keyframes fadein{from{opacity:0}to{opacity:1}}@-webkit-keyframes fadein{from{opacity:0}to{opacity:1}}@-ms-keyframes fadein{from{opacity:0}to{opacity:1}}@-o-keyframes fadein{from{opacity:0}to{opacity:1}}.wp2fa-setup-content .step-setting-wrapper:not(.active){display:none}.wp2fa-setup-content .step-setting-wrapper.active{-webkit-animation:fadein 0.5s;-moz-animation:fadein 0.5s;-ms-animation:fadein 0.5s;-o-animation:fadein 0.5s;animation:fadein 0.5s}.radio-cells .option-pill{text-align:left}.option-pill{text-align:left;padding:0;margin:5px 0px 15px;overflow:visible}.option-pill:last-of-type{margin-bottom:0}.option-pill label{display:block;clear:both}#wp-2fa-totp-qrcode{float:right}.input.wide{margin-top:20px;min-width:50%}.description.error{color:red}.align-center{text-align:center}.wp2fa-setup-content .radio-inline{margin-left:10px}.option-pill ol{margin:0;padding:0 0 0 20px}.resend-email-code{min-width:195px !important}.step-title-wrapper{text-align:center}.step-title{display:inline-block;font-weight:700;margin:0 10px 5px;color:#b4b9be;padding-bottom:15px;position:relative}.step-title:hover{cursor:pointer}.step-title.active-step-title{color:#3e6bff}.step-title span{border:1px solid;width:15px;height:15px;display:inline-block;line-height:15px;font-size:9px;border-radius:50%;position:relative;top:-1px}.app-logo:focus{box-shadow:none !important}.hide-overflow{overflow:hidden}@media (min-width:601px){.apps-wrapper{position:relative;overflow:hidden;display:flex}.apps-wrapper .app-logo{display:flex;align-self:center;flex:1;padding:0 13px}.apps-wrapper .app-logo img{margin:0 auto;width:auto;max-width:100%}}@media (max-width:991px){.setup-wizard-wrapper{min-width:0;position:relative;transform:none;left:0;top:0;padding:15px}#wp2fa-logo img{max-width:240px;width:70%;height:auto}.wp2fa-setup-content{padding:20px 15px}.wp2fa-setup-content h3{font-size:18px;margin-bottom:20px;margin-top:5px;line-height:1.5}body.wp2fa-setup{margin-top:25px;min-width:0}.wp2fa-setup-content *,p.description{font-size:12px}.steps li{padding-bottom:7px;position:relative;font-size:12px}}@media (max-width:600px){#wp-2fa-totp-qrcode{float:none;width:100%;margin:0 auto 10px}.apps-wrapper{display:block}.apps-wrapper a.app-logo{width:calc(100% / 3 - 5px);display:inline-block;margin-bottom:5px}.apps-wrapper a.app-logo img{margin:0 auto}}@media (max-width:480px){.steps li:not(.is-active){display:none}.wp2fa-setup-content br{clear:both;margin-bottom:15px}input[type="email"]{margin-top:10px}}.wp2fa-setup-content .select2-selection__choice{color:#3c434a !important}.method-wrapper{border-bottom:none !important}.wp2fa-setup-content input[type=checkbox]:checked::before{content:"";font-family:dashicons;display:inline-block;line-height:1;font-weight:400;font-style:normal;speak:never;text-decoration:inherit;text-transform:none;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;width:20px;height:20px;font-size:20px;vertical-align:top;text-align:center;transition:color 0.1s ease-in;color:#3e6bff;position:relative;left:1px}.wp2fa-setup-content input[type=checkbox].disabled:focus{border-color:#b12222;box-shadow:0 0 0 1px #b12222}.wp2fa-setup-content input[type=checkbox].disabled{border-color:rgba(21, 21, 21, 0.75)}.wp2fa-setup-content input[type=checkbox]{border-radius:50%;margin-top:-2px}input[type=radio]:checked::before{content:"";background-color:#3e6bff !important}label[for="manual-block"]{margin-bottom:-9px;display:block}.setup-wizard-wrapper .button[data-check-authy-2fa],.setup-wizard-wrapper .button[data-check-twilio-2fa],.setup-wizard-wrapper .button[data-check-clickatell-2fa]{background-color:#fff;border:2px solid #3e6bff;border-radius:30px;color:#3e6bff;display:inline-block;font-size:12px;font-weight:800;letter-spacing:0.3px;line-height:14px;margin-bottom:10px;outline:none;padding:6px 7pt 4px;text-align:center;text-decoration:none;text-transform:uppercase;-webkit-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out}.setup-wizard-wrapper .button[data-check-authy-2fa]:hover,.setup-wizard-wrapper .button[data-check-twilio-2fa]:hover,.setup-wizard-wrapper .button[data-check-clickatell-2fa]:hover{background-color:#3e6bff;border:2px solid #3e6bff;color:#fff}#wizard-sid-key tr{width:50%;display:inline-grid}#wizard-sid-key tr th{width:100%;padding-bottom:0}#wizard-sid-key tr input{width:100%}.sub-setting-indent{margin-left:20px}.sub-setting-indent+.sub-setting-indent{margin-top:20px}dist/css/admin-style.css000064400000071552150755130600011253 0ustar00@charset "UTF-8";
#excluded_users_buttons,#excluded_roles_buttons,#enforced_roles_buttons,#enforced_users_buttons,#excluded_sites_buttons{display:inline}.wp2fa-setup-content #excluded_users_buttons,.wp2fa-setup-content #excluded_roles_buttons{display:block;margin-top:20px}.user-btn{position:relative;margin-left:10px !important}.user-btn .remove-item{background:red;height:14px;width:14px;position:absolute;color:#fff;border-radius:7px;line-height:11px;text-align:center;font-size:10px;display:block;right:-7px;top:-5px}.mt-5px{margin-top:7px;display:inline-block}label.radio-inline{padding-left:8px}.danger-zone-wrapper{padding:15px;border:1px solid red;border-radius:3px;margin-top:15px}.learn_more_link{display:inline-block;margin-left:10px;margin-top:6px}.wp-2fa-settings-wrapper .disabled{opacity:0.5;pointer-events:none}.wp-2fa-settings-wrapper .disabled *{pointer-events:none}.wp-2fa-settings-wrapper{max-width:1010px}.wp-2fa-settings-wrapper h2{margin:3px 0 0}.wp-2fa-settings-wrapper .method-title em{font-style:normal;font-weight:500;position:relative;top:6px}.wp-2fa-settings-wrapper p.description{font-size:13px}.wp-2fa-settings-wrapper:not(.setup-wizard-wrapper) .description fieldset label+label{padding-left:10px !important}label.disabled{opacity:0.5;cursor:default}.button.has-spinner{padding-right:0 !important}.notice-after-button{margin-left:0px !important;padding:5px 10px !important;clear:left;display:block}input.error{border-color:#dc3232;background:#ffebee}.wp-2fa-user-profile-form .button{margin-right:5px}.wp-submenu a[href="wp-2fa-setup"]{display:none !important}@media (min-width:1200px){.min-input-width{min-width:360px}}.wp2fa-form-styles .select2-selection--multiple{min-height:36px;height:auto;overflow-y:auto}.wp2fa-form-styles .select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:4px;margin-bottom:0px;padding:0 5px}.wp2fa-form-styles .select2-container .select2-search--inline{float:left;height:15px}.wp2fa-form-styles .select2-container--default .select2-search--inline .select2-search__field{height:13px;margin-top:0px}.wp2fa-form-styles .select2-container--default .select2-selection--multiple{padding-top:3px}.wp2fa-form-styles .wp2fa-setup-actions .button-secondary{color:#555 !important}.wp2fa-form-styles .wp2fa-setup-actions .button-secondary:hover{color:#fff !important}#exclusion_settings_wrapper.disabled{height:0;overflow:hidden}#notify-admin-settings-page{text-align:justify}.wp2fa-quota-exceeded-notice .button{margin-right:3px}.method-wrapper{margin-bottom:3px;border-bottom:1px solid #c3c4c7;padding-bottom:20px}.certain-users-only-inputs{margin-bottom:15px}a[href*="wp-2fa-premium-features"]{color:#ADFF2F !important}.wp-2fa-nag br{display:none}#wp-2fa-side-banner{background:#fff;padding:24px 4px;border:1px solid #bbb;position:fixed;width:280px;right:40px;bottom:40px;text-align:center}#wp-2fa-side-banner p{font-size:16px;font-weight:700}#wp-2fa-side-banner ul{margin-bottom:20px;padding:0 15px}#wp-2fa-side-banner li{padding-left:25px;overflow:hidden;position:relative;margin-bottom:10px;text-align:left}#wp-2fa-side-banner a.link{position:relative;top:5px;margin-left:15px}#wp-2fa-side-banner .dashicons{position:absolute;left:0;color:#4776ff}#wp-2fa-side-banner .button-primary{background:#4776ff;border-color:#4776ff;color:#fff;text-decoration:none;text-shadow:none}[dir="rtl"] #wp-2fa-side-banner{right:auto;left:40px}@media (max-width:1320px){.wp-2fa-settings-wrapper{max-width:580px}}@media (max-width:1530px) and (min-width:1320px){.wp-2fa-settings-wrapper{max-width:800px}}@media (max-width:1040px){#wp-2fa-side-banner{display:none}}@media (min-width:1040px){.ui-dialog{min-width:355px}}.wp-2fa-user-profile-form .qr-btn,.wp-2fa-user-profile-form .click-to-copy{text-decoration:none;font-size:13px;line-height:2.15384615;min-height:30px;margin:0;padding:0 10px;cursor:pointer;border-width:1px;border-style:solid;-webkit-appearance:none;border-radius:3px;white-space:nowrap;box-sizing:border-box;background:#2271b1;border-color:#2271b1;color:#fff;text-decoration:none;text-shadow:none;display:list-item;width:140px}.wp-2fa-user-profile-form .qr-btn:hover,.wp-2fa-user-profile-form .click-to-copy:hover{background:#135e96;border-color:#135e96;color:#fff}.wp-2fa-user-profile-form #app-key-input{min-width:320px}.mt-5px{margin-top:7px;display:inline-block}label.radio-inline{padding-left:8px}.wp2fa-modal{font-family:helvetica}.modal__overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0, 0, 0, 0.6);display:flex;justify-content:center;align-items:center;z-index:9999}.modal__container{background-color:#fff;padding:40px 40px 30px;max-width:500px;max-height:100vh;border-radius:4px;overflow-y:auto;box-sizing:border-box;z-index:1500}.modal__header{display:flex;justify-content:space-between;align-items:center}.modal__title{margin-top:0;margin-bottom:0;font-weight:600;font-size:1.25rem;line-height:1.25;color:#222;box-sizing:border-box}.modal__close{border:0;outline:none;z-index:10}.modal__close:hover{cursor:pointer}.modal__header .modal__close:before,.wp2fa-modal .modal__close:before{content:"✕"}.modal__content{font-size:13px;font-weight:500;height:100%;line-height:24px;margin-bottom:2px;color:#222;margin-top:0;word-break:keep-all;width:100%}.modal__content p{font-size:13px;font-weight:500;line-height:24px;margin-bottom:2px;color:#222;margin-top:0;word-break:keep-all;width:100%}.modal__content label{font-size:14px;line-height:28px;color:#222;margin-top:0;word-break:keep-all;width:100%;display:block}.modal__content p.description{font-size:12px;line-height:24px;opacity:0.9}.modal__content .apps-wrapper{position:relative;overflow:hidden;display:flex}.modal__content .apps-wrapper .app-logo{display:flex;align-self:center;flex:1;padding:0 13px}.modal__content .apps-wrapper .app-logo img{margin:0 auto;width:auto;max-width:100%}.modal__content .wp2fa-setup-actions{clear:both}.modal__content .iti--allow-dropdown{width:100%}.modal__btn-primary{background-color:#00449e;color:#fff}.enable_styling .modal__content input[type="radio"]{margin-left:0;appearance:auto;width:auto;height:auto;background:transparent}.enable_styling .modal__content input[type="radio"]:after{display:none}.enable_styling .modal__content input[type="radio"]:focus{border-color:transparent;box-shadow:none;outline:none}.enable_styling .modal__content input:not([type="radio"]):not(.app-key){line-height:1;margin-bottom:10px;min-height:2pc;min-width:15pc;padding:5px;margin:0px;border:none;border-bottom:3px solid #d0e5ff;width:100%;padding:10px 0;font-size:16px;font-weight:700;color:#4498ff;box-shadow:none}.enable_styling .modal__content input:not([type="radio"]):focus-visible{border-bottom:3px solid #4498ff;outline:none}.enable_styling .modal__content input:not([type="radio"]):-internal-autofill-selected{background-color:transparent}.wizard-step .iti--allow-dropdown input[type=tel]{z-index:100;background:transparent}.wizard-step .iti__flag-container{width:auto;z-index:101}.wizard-step.expand-panel .iti__flag-container{width:100%}.mepr-form .iti__country-list,.mepr-form .iti__flag-container{width:auto}@keyframes mmfadeIn{from{opacity:0}to{opacity:1}}@keyframes mmfadeOut{from{opacity:1}to{opacity:0}}@keyframes mmslideIn{from{transform:translateY(15%)}to{transform:translateY(0)}}@keyframes mmslideOut{from{transform:translateY(0)}to{transform:translateY(-10%)}}.micromodal-slide{display:none}.micromodal-slide.is-open{display:block}.micromodal-slide[aria-hidden="false"] .modal__overlay{animation:mmfadeIn 0.3s cubic-bezier(0, 0, 0.2, 1)}.micromodal-slide[aria-hidden="false"] .modal__container{animation:mmslideIn 0.3s cubic-bezier(0, 0, 0.2, 1)}.micromodal-slide[aria-hidden="true"] .modal__overlay{animation:mmfadeOut 0.3s cubic-bezier(0, 0, 0.2, 1)}.micromodal-slide[aria-hidden="true"] .modal__container{animation:mmslideOut 0.3s cubic-bezier(0, 0, 0.2, 1)}.micromodal-slide .modal__container,.micromodal-slide .modal__overlay{will-change:transform}.danger-zone-wrapper{padding:15px;border:1px solid red;border-radius:3px;margin-top:15px}.learn_more_link{display:inline-block;margin-left:10px;margin-top:6px}.wp-2fa-settings-wrapper .disabled{opacity:0.5;pointer-events:none}.wp-2fa-settings-wrapper .disabled *{pointer-events:none}.wp2fa-modal .modal__container{max-height:70vh;max-width:872px;min-width:30vw;position:relative}.wp2fa-modal .modal__close{background:transparent;color:#4498ff !important;font-size:15px;font-weight:700;position:absolute;right:20px;text-decoration:none;top:20px;background-color:transparent !important}.wp2fa-modal .modal__close :hover{color:#888;cursor:pointer}.wp2fa-modal input[type=radio]:checked:before{display:none}.wizard-step:not(.active),.step-setting-wrapper:not(.active){display:none}.wizard-step.active,.step-setting-wrapper.active{-webkit-animation:fadein 0.5s;-moz-animation:fadein 0.5s;-ms-animation:fadein 0.5s;-o-animation:fadein 0.5s;animation:fadein 0.5s}#configure-2fa .wp2fa-setup-actions,#configure-2fa-backup-codes .wp2fa-setup-actions{margin-top:25px}.modal__btn+.modal__btn{margin-left:10px}.wp-2fa-configuration-form td.backup-methods-label,.wp-2fa-configuration-form th{display:none}.wp2fa-modal h4,.wp2fa-modal h3{word-wrap:break-word;font-family:helvetica;font-size:22px;font-weight:700;margin-bottom:15px;margin-top:0;margin:0 0 10px 0}.wp2fa-modal.enable_styling h4,.wp2fa-modal.enable_styling h3{font-family:helvetica !important}.wp2fa-modal fieldset{padding:0;border:0}.wp2fa-modal ol{margin:0 0 15px;padding-left:17px}.wp2fa-modal ol li{position:relative;padding:6px}.wp2fa-modal .modal__content code.app-key{font-size:16px;padding:7px 12px;word-break:break-all;background:#efefef;color:#222}label+.verification-response{margin-top:20px}.verification-response:not(:empty){border:3px solid red;font-size:9pt;margin-bottom:15px;padding:10px;background:#ffe4e4;border-radius:10px;font-size:9pt;line-height:24px;width:100%}.default_styling .verification-response:not(:empty){width:calc(100% - 30px)}.wp-2fa-configuration-form .button.enable_styling{background-color:#fff !important;color:#3e6bff !important;font-size:14px;line-height:14px;margin-bottom:10px;outline:none;padding:13px 13pt;text-align:center;text-decoration:none;font-weight:800;display:inline-block;border:3px solid #3e6bff !important;border-radius:30px;text-transform:uppercase;letter-spacing:0.3px}.wp-2fa-configuration-form .button.enable_styling:hover{background-color:#3e6bff !important;color:#fff !important;border:3px solid #3e6bff !important}.wp2fa-modal.enable_styling .modal__content .wp2fa-setup-actions .button:focus,.wp2fa-modal.enable_styling .modal__content .modal__btn:focus,.wp2fa-modal.enable_styling .modal__content .modal__btn.button-confirm:focus,.wp2fa-modal.enable_styling .modal__content .modal__btn.button-decline:focus{box-shadow:none}.wp2fa-modal.enable_styling .button+.button{margin-left:10px}.modal__footer{margin-top:20px}.enable_styling .wp-2fa-button-primary,.enable_styling .wp-2fa-button-secondary,.enable_styling #wizard-api-key button,.enable_styling #wizard-sid-key button{background-color:#fff;color:#3e6bff;font-size:14px;line-height:14px;margin-bottom:10px;outline:none;padding:13px 13pt;text-align:center;text-decoration:none;font-weight:800;display:inline-block;border:3px solid #3e6bff;border-radius:30px;text-transform:uppercase;letter-spacing:0.3px;transition:all 0.2s ease-in-out}.enable_styling .wp-2fa-button-secondary{color:#555;border:3px solid #555}.enable_styling .wp-2fa-configuration-form .button.wp-2fa-button-secondary{color:#555 !important;border:3px solid #555 !important}.enable_styling .wp-2fa-button-primary:focus,.enable_styling .wp-2fa-button-secondary:focus{box-shadow:none}.enable_styling .wp-2fa-button-primary:hover{background-color:#3e6bff;color:#fff;border:3px solid #3e6bff;transition:all 0.4s ease-in-out}.enable_styling .wp-2fa-button-secondary:hover{background-color:#555;color:#fff;border:3px solid #555;transition:all 0.4s ease-in-out}.enable_styling .wp-2fa-configuration-form .button.wp-2fa-button-secondary:hover,.enable_styling #wizard-api-key button:hover,.enable_styling #wizard-sid-key button:hover{background-color:#555 !important;color:#fff !important;border:3px solid #555 !important}.wp-2fa-configuration-form .button{margin-right:10px}.wp-2fa-configuration-form .button:hover{cursor:pointer}.qr-code-wrapper{position:relative;overflow:hidden;float:right}#notify-users .modal__container *{opacity:1;transition:all 0.3s ease-in-out}#notify-users .modal__container.saving *{opacity:0;transition:all 0.3s ease-in-out}.qr-code-wrapper.regenerating img{visibility:hidden}#backup-codes-wrapper{border:none;min-height:2pc;min-width:15pc;padding:10px 0;width:100%;background-color:transparent !important}#backup-codes-wrapper :focus-visible{outline:none}#backup-codes-wrapper:focus-visible{outline:none}.qr-code-wrapper.regenerating:before,#notify-users .modal__container.saving:before{content:"";box-sizing:border-box;position:absolute;top:50%;left:50%;width:20px;height:20px;margin-top:-10px;margin-left:-10px;border-radius:50%;border:2px solid #ccc;border-top-color:#000;animation:spinner 0.6s linear infinite}@keyframes spinner{to{transform:rotate(360deg)}}.default_styling .radio-cells .option-pill{position:relative;margin-bottom:11px}.default_styling .radio-cells .option-pill label{padding-right:40px;font-size:12px}.default_styling .wizard-tooltip{display:none !important}.default_styling input[type=checkbox],.default_styling input[type=radio]{-webkit-appearance:auto}@media screen and (max-width:1299px){.wp2fa-modal .modal__container{max-width:96vw}#wp-2fa-totp-qrcode{max-width:100%;text-align:center;margin:15px auto;display:block}.qr-code-wrapper{float:none}.step-setting-wrapper br{display:none}.enable_styling .radio-cells{display:flex;flex-wrap:wrap;margin:0px 0 20px;width:calc(100% + 10px);position:relative;left:-5px;flex-wrap:wrap}.enable_styling .radio-cells input:not([type="radio"]){margin-top:5px}.enable_styling .radio-cells .option-pill{flex-basis:0;flex-grow:1;border:3px solid #eee;border-radius:10px;padding:10px;margin:5px;font-size:11px;position:relative}.enable_styling .radio-cells .option-pill p{height:100%}.enable_styling .radio-cells.max-3 .option-pill{flex:33.333%;display:flex;flex-direction:column}.enable_styling .radio-cells.max-3 .option-pill p{flex:1}.enable_styling .radio-cells .option-pill.isSelected{border:3px solid #007cba}.enable_styling .radio-cells .option-pill label{display:block;font-size:13px;line-height:24px;font-weight:500;margin-bottom:2px;height:100%;max-width:calc(100% - 20px)}.enable_styling .radio-cells .option-pill p{font-size:12px;line-height:23px;opacity:0.9;height:auto}.enable_styling .option-pill{margin-bottom:15px}.enable_styling .tooltip-content-wrapper{display:none}.wp2fa-modal .modal__content code br{display:block !important}.wp2fa-modal .modal__content code.app-key{width:100%;display:block;text-align:center;font-size:16px;margin-bottom:30px;background:#efefef;padding:5px;word-break:break-all}.wp2fa-modal .modal__content .apps-wrapper{margin:10px 0}.wp2fa-modal .modal__content a.app-logo{padding:0 5px}.wp2fa-modal .modal__content .wizard-tooltip{background:#3e6bff;color:white;height:18px;display:inline-block;width:18px;border-radius:50%;position:absolute;overflow:hidden;text-align:center;line-height:18px;font-weight:700;margin-left:4px;margin-top:3px;bottom:10px;right:17px}.wp2fa-modal .modal__content .inline-helper{background:#eee;padding:10px;border-radius:10px;display:block;margin-top:10px;font-size:12px;line-height:24px;display:none}.wp2fa-modal .modal__content .click-to-copy{border:2px solid #3e6bff;border-radius:14px;display:inline-block;font-size:10px;padding:0 9px;font-weight:700;line-height:16px}.wp2fa-modal .modal__content .click-to-copy.done{background-color:green !important;border:2px solid green;color:white}.wp2fa-modal .modal__content .click-to-copy:hover{background-color:#3e6bff;color:white;cursor:pointer;opacity:0.5}.wp2fa-modal .modal__content .app-key-wrapper{background:#eee;border-radius:4px;margin:10px 0 5px;overflow:hidden;padding:3px 7px}.wp2fa-modal .modal__content .app-key-wrapper input{background:transparent;border:none;display:inline-block;font-size:9pt;margin:0 10px 0 0;max-width:15pc;min-height:0;min-width:200px;padding:0;color:#888}.wp2fa-modal .modal__content .app-key-wrapper input:focus-visible,.wp2fa-modal .modal__content .app-key-wrapper input:focus{border:none;background-color:transparent;outline:none}.wp-2fa-configuration-form .button{width:auto;display:block;margin-bottom:10px;margin-left:0px;margin-right:0px;text-align:center}.wp2fa-modal .modal__content .wp2fa-setup-actions .button,.wp2fa-modal .modal__content .modal__btn{width:auto;display:inline-block;margin-bottom:10px;margin-left:0px;margin-right:0px;text-align:center}.modal__content input:not([type="radio"]){width:100%;margin-bottom:10px}.hide-on-mobile{display:none}.wp2fa-modal h4,.wp2fa-modal h3{font-size:20px}.wp2fa-modal h4{clear:both}}@media screen and (min-width:1300px){.wp2fa-modal .clear-both{clear:both;display:block;overflow:hidden}.wp2fa-modal .modal-50{width:50%;display:inline-block;float:left}.wp2fa-modal .modal-60{width:70%;display:inline-block;float:left}.wp2fa-modal .modal-60 .radio-cells{width:100%;padding-left:10px}.wp2fa-modal .modal-40{width:30%;float:left;display:flex;justify-content:center}.wp2fa-modal .mb-20{margin-bottom:20px}.wp2fa-modal.enable_styling p+.radio-cells,.wp2fa-modal.enable_styling p+fieldset{margin:20px 0;display:flex;flex-wrap:wrap}.wp2fa-modal.enable_styling .radio-cells{display:flex;flex-wrap:wrap;margin:0px 0 20px;width:calc(100% + 10px);position:relative;left:-5px;flex-wrap:wrap}.wp2fa-modal.enable_styling .radio-cells input:not([type="radio"]){margin-top:5px}.wp2fa-modal.enable_styling .radio-cells .option-pill{flex-basis:0;flex-grow:1;border:3px solid #eee;border-radius:10px;padding:10px;margin:5px;font-size:11px;position:relative}.wp2fa-modal.enable_styling .radio-cells .option-pill p{height:100%}.wp2fa-modal.enable_styling .radio-cells.max-3 .option-pill{flex:33.333%;display:flex;flex-direction:column}.wp2fa-modal.enable_styling .radio-cells.max-3 .option-pill p{flex:1}.wp2fa-modal.enable_styling .radio-cells .option-pill.isSelected{border:3px solid #007cba}.wp2fa-modal.enable_styling .radio-cells .option-pill label{display:block;font-size:13px;line-height:24px;font-weight:500;margin-bottom:2px;height:100%;max-width:calc(100% - 20px)}.wp2fa-modal.enable_styling .radio-cells .option-pill p{font-size:12px;line-height:23px;opacity:0.9;height:auto}.wp2fa-modal .wizard-tooltip{background:#3e6bff;color:white;height:18px;display:inline-block;width:18px;border-radius:50%;position:absolute;overflow:hidden;text-align:center;line-height:18px;font-weight:700;margin-left:4px;margin-top:3px;bottom:10px;right:17px}.wp2fa-modal .inline-helper{background:#eee;padding:10px;border-radius:10px;display:block;margin-top:10px;font-size:12px;line-height:24px;display:none}.wp2fa-modal.enable_styling .tooltip-content-wrapper{display:none}.wp2fa-modal .click-to-copy{border:2px solid #3e6bff;border-radius:14px;display:inline-block;font-size:10px;padding:0 9px;font-weight:700;line-height:16px}.wp2fa-modal .click-to-copy.done{background-color:green !important;border:2px solid green;color:white}.wp2fa-modal .click-to-copy:hover{background-color:#3e6bff;color:white;cursor:pointer;opacity:0.5}.wp2fa-modal .app-key-wrapper{background:#eee;border-radius:4px;margin:10px 0 5px;overflow:hidden;padding:3px 7px}.wp2fa-modal .app-key-wrapper input{background:transparent;border:none;display:inline-block;font-size:9pt;margin:0 10px 0 0;max-width:15pc;min-height:0;min-width:200px;padding:0;color:#888}.wp2fa-modal .app-key-wrapper input:focus-visible,.wp2fa-modal .app-key-wrapper input:focus{border:none;background-color:transparent;outline:none}.wp2fa-modal .option-pill{margin-bottom:15px}.wp2fa-modal .qr-code{float:left;width:100%;position:relative;left:-2%}.wp2fa-modal .mb-30{margin-bottom:30px}.show-on-mobile{display:none}}@media (max-width:500px){.modal__container{padding:25px}.option-pill:not(last-of-type){margin-bottom:40px}.wp2fa-modal .modal__content .apps-wrapper{display:block}.wp2fa-modal .modal__content a.app-logo{width:calc(33.33333% - 13px);display:inline-block;margin-bottom:5px}.wp2fa-modal h4,.wp2fa-modal h3{font-size:18px;margin-bottom:9px}#configure-2fa-backup-codes .wp2fa-setup-actions,#configure-2fa .wp2fa-setup-actions{margin-top:15px}.wp2fa-modal .modal__content .modal__btn,.wp2fa-modal .modal__content .wp2fa-setup-actions .button,.wp-2fa-configuration-form .button{display:block;margin-bottom:10px;margin-left:0;margin-right:0;text-align:center;width:auto;padding:10px 15px;font-size:12px}.wp2fa-modal .modal__content .radio-cells .option-pill{flex-basis:unset;flex-grow:1}.wp2fa-modal .modal__content p+.radio-cells{margin-top:20px}}@media screen and (min-width:801px) and (max-width:1299px){.wp2fa-modal .modal__content p+.radio-cells{margin-top:20px}.modal-50{width:50%;display:inline-block;float:left}.mb-20{margin-bottom:20px}.modal-60{width:70%;display:inline-block;float:left}.modal-60 .radio-cells{width:100%;padding-left:10px}.modal-40{width:30%;float:left;display:flex;justify-content:center}}@media screen and (min-width:1024px){.wp2fa-modal .modal__container{min-width:768px}}@media screen and (min-width:501px) and (max-width:800px){.modal-50{width:50%;display:inline-block;float:left}.modal-60{width:60%;display:inline-block;float:left}.modal-60 .radio-cells{width:100%;padding-left:10px}.mb-20{margin-bottom:20px}.modal-40{width:40%;float:left;display:flex;justify-content:center}}@media screen and (max-width:800px){.wp2fa-modal .modal__container{max-width:95vw;min-width:80vw}.wp2fa-modal .modal__content .wp2fa-setup-actions .button,.wp2fa-modal .modal__content .modal__btn{width:auto;display:inline-block;margin-bottom:10px;margin-left:0px;margin-right:0px;text-align:center}.wp2fa-modal .modal__close{right:15px;top:15px}}@media screen and (max-width:500px){.mb-20{margin-bottom:15px}.wp2fa-modal .modal__content .app-key-wrapper input{max-width:200px}.enable_styling .wp-2fa-button-primary,.enable_styling .wp-2fa-button-secondary{display:block !important;margin-left:0 !important;margin-right:0;width:100% !important}.wp2fa-modal .modal__content .wizard-tooltip{right:5px;bottom:5px}.verification-response:not(:empty){width:auto}}.wizard-custom-counter{counter-reset:step-counter;list-style:none;margin-bottom:0 !important}.wizard-custom-counter li{counter-increment:step-counter}.wizard-custom-counter li:last-of-type{margin-bottom:0 !important}.enable_styling .wizard-custom-counter li::before{content:counter(step-counter);background:#3e6bff;width:20px;height:20px;color:white;text-align:center;display:inline-block;line-height:20px;position:absolute;left:-20px;top:8px;font-size:14px;font-weight:700;border-radius:50px}.enable_styling #backup-codes-wrapper{border:none;border-bottom:3px solid #d0e5ff;color:#4498ff;font-size:1pc;font-weight:700;line-height:1;margin:0;min-height:2pc;min-width:15pc;padding:10px 0;width:100%;line-height:26px;background-color:transparent !important}.enable_styling #backup-codes-wrapper :focus-visible{border-bottom:3px solid #4498ff;outline:none}.enable_styling #backup-codes-wrapper:focus-visible{border-bottom:3px solid #4498ff;outline:none}.default_styling .wizard-custom-counter li::before{content:counter(step-counter);width:20px;height:20px;text-align:center;display:inline-block;line-height:20px;position:absolute;left:-20px;top:8px;font-size:14px;font-weight:700;border-radius:50px}.mb-0{margin-bottom:0 !important}.wp-2fa-user-profile-form{margin:0}.wp-2fa-user-profile-form:first-of-type{margin-top:20px}.wp-2fa-user-profile-form:last-of-type{margin-bottom:20px}.wp-2fa-user-profile-form tr+tr th,.wp-2fa-user-profile-form tr+tr td{padding-top:0}.remove-tr-padding th,.remove-tr-padding td{padding-bottom:0}body:not(.wp-admin) .wp-2fa-user-profile-form td,body:not(.wp-admin) .wp-2fa-user-profile-form th{padding:0;border:none}.wp-2fa-user-profile-form{border:none}.wp-2fa-button-primary+.wp-2fa-button-secondary{margin-left:10px}.modal-logo-wrapper{text-align:center;padding:0;margin:0}.wizard-step label[for="authy-token"]{min-height:70px;-webkit-transition:min-height 0.2s ease-in-out;-moz-transition:min-height 0.2s ease-in-out;-ms-transition:min-height 0.2s ease-in-out;-o-transition:min-height 0.2s ease-in-out;transition:min-height 0.2s ease-in-out}.wizard-step.expand-panel label[for="authy-token"]{min-height:300px;-webkit-transition:min-height 0.2s ease-in-out;-moz-transition:min-height 0.2s ease-in-out;-ms-transition:min-height 0.2s ease-in-out;-o-transition:min-height 0.2s ease-in-out;transition:min-height 0.2s ease-in-out}.wizard-step.expand-panel .authy-step-setting-wrapper{overflow:hidden}.wizard-step.expand-panel .iti__country-list{width:100%;max-height:240px}[aria-describedby="authy-diag"],[aria-describedby="twilio-diag"]{min-width:400px;padding:10px}[aria-describedby="authy-diag"] .ui-dialog-titlebar,[aria-describedby="twilio-diag"] .ui-dialog-titlebar{background:#fff;border-bottom:none;height:auto;font-size:18px;font-weight:600;line-height:2;padding:10px 36px 0 16px;word-wrap:break-word;font-family:helvetica;font-size:22px;font-weight:700;margin-bottom:15px;margin-top:0;margin:0 0 10px 0}[aria-describedby="authy-diag"] .ui-dialog-buttonpane,[aria-describedby="twilio-diag"] .ui-dialog-buttonpane{background:#fff;border-top:none;padding:16px}[aria-describedby="authy-diag"] .ui-dialog-buttonset .ui-button,[aria-describedby="twilio-diag"] .ui-dialog-buttonset .ui-button{background-color:#fff;color:#3e6bff;font-size:14px;line-height:14px;margin-bottom:10px;outline:none;padding:13px 13pt;text-align:center;text-decoration:none;font-weight:800;display:inline-block;border:3px solid #3e6bff;border-radius:30px;text-transform:uppercase;letter-spacing:0.3px;-webkit-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out;height:auto;margin-left:0}[aria-describedby="authy-diag"] .ui-dialog-buttonset .ui-button:hover,[aria-describedby="twilio-diag"] .ui-dialog-buttonset .ui-button:hover{background-color:#3e6bff;color:#fff}[aria-describedby="authy-diag"] .ui-dialog-buttonpane .ui-dialog-buttonset,[aria-describedby="twilio-diag"] .ui-dialog-buttonpane .ui-dialog-buttonset{float:left}[aria-describedby="authy-diag"] .ui-dialog-content,[aria-describedby="twilio-diag"] .ui-dialog-content{padding:0 16px 9px;overflow:auto}.our-wordpress-plugins{width:100%;max-width:250px;padding:0 20px;box-sizing:border-box;background:#ffffff}.our-wordpress-plugins h3{font-size:17px;line-height:28px;color:#23282D;font-weight:500;margin:0;padding:15px 4px 0px;text-align:center}.our-wordpress-plugins ul{list-style:none;margin:0;padding:20px 0 0;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap}.our-wordpress-plugins ul li{width:100%;margin-bottom:20px}.our-wordpress-plugins ul li .plugin-box{width:100%;background:#F7F7F7;border:1px solid #D9D9D9}.our-wordpress-plugins ul li .plugin-box .plugin-img img{width:100%;display:block}.our-wordpress-plugins h4{font-size:16px;line-height:19px;color:#23282D;font-weight:600;margin:0;padding-bottom:10px}.plugin-desc{padding:20px 15px;font-size:14px;line-height:22px;font-weight:400;color:#23282D;text-align:center;border-top:1px solid #D9D9D9}.plugin-desc p{margin:0 0 15px 0}.cta-btn a{font-size:14px;line-height:24px;color:#fff;font-weight:700;background:#2B597A;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;display:inline-block;text-decoration:none;padding:5px 15px}.our-wordpress-plugins.full{width:100%;max-width:880px}.our-wordpress-plugins.full ul{margin-left:-15px;margin-right:-15px}.our-wordpress-plugins.full ul li{width:25%;padding:0 10px;box-sizing:border-box}.our-wordpress-plugins.full ul li .plugin-box{height:100%}.our-wordpress-plugins.side-bar{margin-bottom:30px;width:30%;padding:0 20px;box-sizing:border-box;background:#ffffff}.wp2fa-help-section{display:-ms-flexbox;display:flex;-ms-flex-direction:row-reverse;flex-direction:row-reverse;-ms-flex-wrap:wrap;flex-wrap:wrap;justify-content:flex-end;padding-bottom:20px;max-width:1170px}.wp2fa-help-section.nav-tabs{display:-ms-flexbox;display:flex;-ms-flex-direction:row-reverse;flex-direction:row-reverse;-ms-flex-wrap:wrap;flex-wrap:wrap;justify-content:flex-end;padding-bottom:20px;max-width:1170px}.wp2fa-help-main{width:60%;padding:10px 20px}.wp2fa-logo{text-align:center}.wp2fa-logo img{width:100%;max-width:350px}.wp2fa-about p{word-break:break-all}.title h2{font-size:23px;font-weight:400;margin:0;padding:9px 0;line-height:29px}#system-info-textarea{font-family:monospace;white-space:pre;overflow:auto;width:100%;height:400px;margin:0}@media (max-width:991px){.our-wordpress-plugins.full ul li{width:100%;max-width:255px;margin:0 auto;margin-bottom:20px}}@media (max-width:767px){.wp2fa-help-main{width:100%;padding-top:40px}.our-wordpress-plugins.side-bar{width:100%;max-width:100%;margin:0 auto}.wp2fa-help-section.nav-tabs{padding-top:30px}.our-wordpress-plugins ul{margin-right:-15px;margin-left:-15px}.our-wordpress-plugins ul li{width:33.33%;padding:0 15px;box-sizing:border-box}.our-wordpress-plugins ul li .plugin-box{height:100%}}@media (max-width:580px){.our-wordpress-plugins ul li{width:50%;margin:0 auto;margin-bottom:30px}}@media (max-width:400px){.our-wordpress-plugins ul li{width:100%;margin:0 auto;margin-bottom:30px}}dist/css/select2.min.css000064400000035534150755130600011150 0ustar00.select2-container{box-sizing:border-box;display:inline-block;margin:0;position:relative;vertical-align:middle}.select2-container .select2-selection--single{box-sizing:border-box;cursor:pointer;display:block;height:28px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--single .select2-selection__rendered{display:block;padding-left:8px;padding-right:20px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.select2-container .select2-selection--single .select2-selection__clear{position:relative}.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered{padding-right:8px;padding-left:20px}.select2-container .select2-selection--multiple{box-sizing:border-box;cursor:pointer;display:block;min-height:32px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--multiple .select2-selection__rendered{display:inline-block;overflow:hidden;padding-left:8px;text-overflow:ellipsis;white-space:nowrap}.select2-container .select2-search--inline{float:left}.select2-container .select2-search--inline .select2-search__field{box-sizing:border-box;border:none;font-size:100%;margin-top:5px;padding:0}.select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select2-dropdown{background-color:white;border:1px solid #aaa;border-radius:4px;box-sizing:border-box;display:block;position:absolute;left:-100000px;width:100%;z-index:1051}.select2-results{display:block}.select2-results__options{list-style:none;margin:0;padding:0}.select2-results__option{padding:6px;user-select:none;-webkit-user-select:none}.select2-results__option[aria-selected]{cursor:pointer}.select2-container--open .select2-dropdown{left:0}.select2-container--open .select2-dropdown--above{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--open .select2-dropdown--below{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select2-search--dropdown{display:block;padding:4px}.select2-search--dropdown .select2-search__field{padding:4px;width:100%;box-sizing:border-box}.select2-search--dropdown .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select2-search--dropdown.select2-search--hide{display:none}.select2-close-mask{border:0;margin:0;padding:0;display:block;position:fixed;left:0;top:0;min-height:100%;min-width:100%;height:auto;width:auto;opacity:0;z-index:99;background-color:#fff;filter:alpha(opacity=0)}.select2-hidden-accessible{border:0 !important;clip:rect(0 0 0 0) !important;height:1px !important;margin:-1px !important;overflow:hidden !important;padding:0 !important;position:absolute !important;width:1px !important}.select2-container--default .select2-selection--single{background-color:#fff;border:1px solid #aaa;border-radius:4px}.select2-container--default .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px}.select2-container--default .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold}.select2-container--default .select2-selection--single .select2-selection__placeholder{color:#999}.select2-container--default .select2-selection--single .select2-selection__arrow{height:26px;position:absolute;top:1px;right:1px;width:20px}.select2-container--default .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow{left:1px;right:auto}.select2-container--default.select2-container--disabled .select2-selection--single{background-color:#eee;cursor:default}.select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear{display:none}.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px}.select2-container--default .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text}.select2-container--default .select2-selection--multiple .select2-selection__rendered{box-sizing:border-box;list-style:none;margin:0;padding:0 5px;width:100%}.select2-container--default .select2-selection--multiple .select2-selection__rendered li{list-style:none}.select2-container--default .select2-selection--multiple .select2-selection__placeholder{color:#999;margin-top:5px;float:left}.select2-container--default .select2-selection--multiple .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-top:5px;margin-right:10px}.select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove{color:#999;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover{color:#333}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice,.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder,.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-search--inline{float:right}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice{margin-left:5px;margin-right:auto}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove{margin-left:2px;margin-right:auto}.select2-container--default.select2-container--focus .select2-selection--multiple{border:solid black 1px;outline:0}.select2-container--default.select2-container--disabled .select2-selection--multiple{background-color:#eee;cursor:default}.select2-container--default.select2-container--disabled .select2-selection__choice__remove{display:none}.select2-container--default.select2-container--open.select2-container--above .select2-selection--single,.select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple{border-top-left-radius:0;border-top-right-radius:0}.select2-container--default.select2-container--open.select2-container--below .select2-selection--single,.select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple{border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--default .select2-search--dropdown .select2-search__field{border:1px solid #aaa}.select2-container--default .select2-search--inline .select2-search__field{background:transparent;border:none;outline:0;box-shadow:none;-webkit-appearance:textfield}.select2-container--default .select2-results>.select2-results__options{max-height:200px;overflow-y:auto}.select2-container--default .select2-results__option[role=group]{padding:0}.select2-container--default .select2-results__option[aria-disabled=true]{color:#999}.select2-container--default .select2-results__option[aria-selected=true]{background-color:#ddd}.select2-container--default .select2-results__option .select2-results__option{padding-left:1em}.select2-container--default .select2-results__option .select2-results__option .select2-results__group{padding-left:0}.select2-container--default .select2-results__option .select2-results__option .select2-results__option{margin-left:-1em;padding-left:2em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-2em;padding-left:3em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-3em;padding-left:4em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-4em;padding-left:5em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-5em;padding-left:6em}.select2-container--default .select2-results__option--highlighted[aria-selected]{background-color:#5897fb;color:white}.select2-container--default .select2-results__group{cursor:default;display:block;padding:6px}.select2-container--classic .select2-selection--single{background-color:#f7f7f7;border:1px solid #aaa;border-radius:4px;outline:0;background-image:-webkit-linear-gradient(top, #fff 50%, #eee 100%);background-image:-o-linear-gradient(top, #fff 50%, #eee 100%);background-image:linear-gradient(to bottom, #fff 50%, #eee 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0)}.select2-container--classic .select2-selection--single:focus{border:1px solid #5897fb}.select2-container--classic .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px}.select2-container--classic .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-right:10px}.select2-container--classic .select2-selection--single .select2-selection__placeholder{color:#999}.select2-container--classic .select2-selection--single .select2-selection__arrow{background-color:#ddd;border:none;border-left:1px solid #aaa;border-top-right-radius:4px;border-bottom-right-radius:4px;height:26px;position:absolute;top:1px;right:1px;width:20px;background-image:-webkit-linear-gradient(top, #eee 50%, #ccc 100%);background-image:-o-linear-gradient(top, #eee 50%, #ccc 100%);background-image:linear-gradient(to bottom, #eee 50%, #ccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFCCCCCC', GradientType=0)}.select2-container--classic .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0}.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left}.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow{border:none;border-right:1px solid #aaa;border-radius:0;border-top-left-radius:4px;border-bottom-left-radius:4px;left:1px;right:auto}.select2-container--classic.select2-container--open .select2-selection--single{border:1px solid #5897fb}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow{background:transparent;border:none}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--single{border-top:none;border-top-left-radius:0;border-top-right-radius:0;background-image:-webkit-linear-gradient(top, #fff 0%, #eee 50%);background-image:-o-linear-gradient(top, #fff 0%, #eee 50%);background-image:linear-gradient(to bottom, #fff 0%, #eee 50%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0)}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--single{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0;background-image:-webkit-linear-gradient(top, #eee 50%, #fff 100%);background-image:-o-linear-gradient(top, #eee 50%, #fff 100%);background-image:linear-gradient(to bottom, #eee 50%, #fff 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFFFFFFF', GradientType=0)}.select2-container--classic .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text;outline:0}.select2-container--classic .select2-selection--multiple:focus{border:1px solid #5897fb}.select2-container--classic .select2-selection--multiple .select2-selection__rendered{list-style:none;margin:0;padding:0 5px}.select2-container--classic .select2-selection--multiple .select2-selection__clear{display:none}.select2-container--classic .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove{color:#888;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover{color:#555}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice{float:right}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice{margin-left:5px;margin-right:auto}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove{margin-left:2px;margin-right:auto}.select2-container--classic.select2-container--open .select2-selection--multiple{border:1px solid #5897fb}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--classic .select2-search--dropdown .select2-search__field{border:1px solid #aaa;outline:0}.select2-container--classic .select2-search--inline .select2-search__field{outline:0;box-shadow:none}.select2-container--classic .select2-dropdown{background-color:#fff;border:1px solid transparent}.select2-container--classic .select2-dropdown--above{border-bottom:none}.select2-container--classic .select2-dropdown--below{border-top:none}.select2-container--classic .select2-results>.select2-results__options{max-height:200px;overflow-y:auto}.select2-container--classic .select2-results__option[role=group]{padding:0}.select2-container--classic .select2-results__option[aria-disabled=true]{color:grey}.select2-container--classic .select2-results__option--highlighted[aria-selected]{background-color:#3875d7;color:#fff}.select2-container--classic .select2-results__group{cursor:default;display:block;padding:6px}.select2-container--classic.select2-container--open .select2-dropdown{border-color:#5897fb}
dist/css/styles.css000064400000053237150755130600010350 0ustar00@charset "UTF-8";
.mt-5px{margin-top:7px;display:inline-block}label.radio-inline{padding-left:8px}.wp2fa-modal{font-family:helvetica}.modal__overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0, 0, 0, 0.6);display:flex;justify-content:center;align-items:center;z-index:9999}.modal__container{background-color:#fff;padding:40px 40px 30px;max-width:500px;max-height:100vh;border-radius:4px;overflow-y:auto;box-sizing:border-box;z-index:1500}.modal__header{display:flex;justify-content:space-between;align-items:center}.modal__title{margin-top:0;margin-bottom:0;font-weight:600;font-size:1.25rem;line-height:1.25;color:#222;box-sizing:border-box}.modal__close{border:0;outline:none;z-index:10}.modal__close:hover{cursor:pointer}.modal__header .modal__close:before,.wp2fa-modal .modal__close:before{content:"✕"}.modal__content{font-size:13px;font-weight:500;height:100%;line-height:24px;margin-bottom:2px;color:#222;margin-top:0;word-break:keep-all;width:100%}.modal__content p{font-size:13px;font-weight:500;line-height:24px;margin-bottom:2px;color:#222;margin-top:0;word-break:keep-all;width:100%}.modal__content label{font-size:14px;line-height:28px;color:#222;margin-top:0;word-break:keep-all;width:100%;display:block}.modal__content p.description{font-size:12px;line-height:24px;opacity:0.9}.modal__content .apps-wrapper{position:relative;overflow:hidden;display:flex}.modal__content .apps-wrapper .app-logo{display:flex;align-self:center;flex:1;padding:0 13px}.modal__content .apps-wrapper .app-logo img{margin:0 auto;width:auto;max-width:100%}.modal__content .wp2fa-setup-actions{clear:both}.modal__content .iti--allow-dropdown{width:100%}.modal__btn-primary{background-color:#00449e;color:#fff}.enable_styling .modal__content input[type="radio"]{margin-left:0;appearance:auto;width:auto;height:auto;background:transparent}.enable_styling .modal__content input[type="radio"]:after{display:none}.enable_styling .modal__content input[type="radio"]:focus{border-color:transparent;box-shadow:none;outline:none}.enable_styling .modal__content input:not([type="radio"]):not(.app-key){line-height:1;margin-bottom:10px;min-height:2pc;min-width:15pc;padding:5px;margin:0px;border:none;border-bottom:3px solid #d0e5ff;width:100%;padding:10px 0;font-size:16px;font-weight:700;color:#4498ff;box-shadow:none}.enable_styling .modal__content input:not([type="radio"]):focus-visible{border-bottom:3px solid #4498ff;outline:none}.enable_styling .modal__content input:not([type="radio"]):-internal-autofill-selected{background-color:transparent}.wizard-step .iti--allow-dropdown input[type=tel]{z-index:100;background:transparent}.wizard-step .iti__flag-container{width:auto;z-index:101}.wizard-step.expand-panel .iti__flag-container{width:100%}.mepr-form .iti__country-list,.mepr-form .iti__flag-container{width:auto}@keyframes mmfadeIn{from{opacity:0}to{opacity:1}}@keyframes mmfadeOut{from{opacity:1}to{opacity:0}}@keyframes mmslideIn{from{transform:translateY(15%)}to{transform:translateY(0)}}@keyframes mmslideOut{from{transform:translateY(0)}to{transform:translateY(-10%)}}.micromodal-slide{display:none}.micromodal-slide.is-open{display:block}.micromodal-slide[aria-hidden="false"] .modal__overlay{animation:mmfadeIn 0.3s cubic-bezier(0, 0, 0.2, 1)}.micromodal-slide[aria-hidden="false"] .modal__container{animation:mmslideIn 0.3s cubic-bezier(0, 0, 0.2, 1)}.micromodal-slide[aria-hidden="true"] .modal__overlay{animation:mmfadeOut 0.3s cubic-bezier(0, 0, 0.2, 1)}.micromodal-slide[aria-hidden="true"] .modal__container{animation:mmslideOut 0.3s cubic-bezier(0, 0, 0.2, 1)}.micromodal-slide .modal__container,.micromodal-slide .modal__overlay{will-change:transform}.danger-zone-wrapper{padding:15px;border:1px solid red;border-radius:3px;margin-top:15px}.learn_more_link{display:inline-block;margin-left:10px;margin-top:6px}.wp-2fa-settings-wrapper .disabled{opacity:0.5;pointer-events:none}.wp-2fa-settings-wrapper .disabled *{pointer-events:none}.wp2fa-modal .modal__container{max-height:70vh;max-width:872px;min-width:30vw;position:relative}.wp2fa-modal .modal__close{background:transparent;color:#4498ff !important;font-size:15px;font-weight:700;position:absolute;right:20px;text-decoration:none;top:20px;background-color:transparent !important}.wp2fa-modal .modal__close :hover{color:#888;cursor:pointer}.wp2fa-modal input[type=radio]:checked:before{display:none}.wizard-step:not(.active),.step-setting-wrapper:not(.active){display:none}.wizard-step.active,.step-setting-wrapper.active{-webkit-animation:fadein 0.5s;-moz-animation:fadein 0.5s;-ms-animation:fadein 0.5s;-o-animation:fadein 0.5s;animation:fadein 0.5s}#configure-2fa .wp2fa-setup-actions,#configure-2fa-backup-codes .wp2fa-setup-actions{margin-top:25px}.modal__btn+.modal__btn{margin-left:10px}.wp-2fa-configuration-form td.backup-methods-label,.wp-2fa-configuration-form th{display:none}.wp2fa-modal h4,.wp2fa-modal h3{word-wrap:break-word;font-family:helvetica;font-size:22px;font-weight:700;margin-bottom:15px;margin-top:0;margin:0 0 10px 0}.wp2fa-modal.enable_styling h4,.wp2fa-modal.enable_styling h3{font-family:helvetica !important}.wp2fa-modal fieldset{padding:0;border:0}.wp2fa-modal ol{margin:0 0 15px;padding-left:17px}.wp2fa-modal ol li{position:relative;padding:6px}.wp2fa-modal .modal__content code.app-key{font-size:16px;padding:7px 12px;word-break:break-all;background:#efefef;color:#222}label+.verification-response{margin-top:20px}.verification-response:not(:empty){border:3px solid red;font-size:9pt;margin-bottom:15px;padding:10px;background:#ffe4e4;border-radius:10px;font-size:9pt;line-height:24px;width:100%}.default_styling .verification-response:not(:empty){width:calc(100% - 30px)}.wp-2fa-configuration-form .button.enable_styling{background-color:#fff !important;color:#3e6bff !important;font-size:14px;line-height:14px;margin-bottom:10px;outline:none;padding:13px 13pt;text-align:center;text-decoration:none;font-weight:800;display:inline-block;border:3px solid #3e6bff !important;border-radius:30px;text-transform:uppercase;letter-spacing:0.3px}.wp-2fa-configuration-form .button.enable_styling:hover{background-color:#3e6bff !important;color:#fff !important;border:3px solid #3e6bff !important}.wp2fa-modal.enable_styling .modal__content .wp2fa-setup-actions .button:focus,.wp2fa-modal.enable_styling .modal__content .modal__btn:focus,.wp2fa-modal.enable_styling .modal__content .modal__btn.button-confirm:focus,.wp2fa-modal.enable_styling .modal__content .modal__btn.button-decline:focus{box-shadow:none}.wp2fa-modal.enable_styling .button+.button{margin-left:10px}.modal__footer{margin-top:20px}.enable_styling .wp-2fa-button-primary,.enable_styling .wp-2fa-button-secondary,.enable_styling #wizard-api-key button,.enable_styling #wizard-sid-key button{background-color:#fff;color:#3e6bff;font-size:14px;line-height:14px;margin-bottom:10px;outline:none;padding:13px 13pt;text-align:center;text-decoration:none;font-weight:800;display:inline-block;border:3px solid #3e6bff;border-radius:30px;text-transform:uppercase;letter-spacing:0.3px;transition:all 0.2s ease-in-out}.enable_styling .wp-2fa-button-secondary{color:#555;border:3px solid #555}.enable_styling .wp-2fa-configuration-form .button.wp-2fa-button-secondary{color:#555 !important;border:3px solid #555 !important}.enable_styling .wp-2fa-button-primary:focus,.enable_styling .wp-2fa-button-secondary:focus{box-shadow:none}.enable_styling .wp-2fa-button-primary:hover{background-color:#3e6bff;color:#fff;border:3px solid #3e6bff;transition:all 0.4s ease-in-out}.enable_styling .wp-2fa-button-secondary:hover{background-color:#555;color:#fff;border:3px solid #555;transition:all 0.4s ease-in-out}.enable_styling .wp-2fa-configuration-form .button.wp-2fa-button-secondary:hover,.enable_styling #wizard-api-key button:hover,.enable_styling #wizard-sid-key button:hover{background-color:#555 !important;color:#fff !important;border:3px solid #555 !important}.wp-2fa-configuration-form .button{margin-right:10px}.wp-2fa-configuration-form .button:hover{cursor:pointer}.qr-code-wrapper{position:relative;overflow:hidden;float:right}#notify-users .modal__container *{opacity:1;transition:all 0.3s ease-in-out}#notify-users .modal__container.saving *{opacity:0;transition:all 0.3s ease-in-out}.qr-code-wrapper.regenerating img{visibility:hidden}#backup-codes-wrapper{border:none;min-height:2pc;min-width:15pc;padding:10px 0;width:100%;background-color:transparent !important}#backup-codes-wrapper :focus-visible{outline:none}#backup-codes-wrapper:focus-visible{outline:none}.qr-code-wrapper.regenerating:before,#notify-users .modal__container.saving:before{content:"";box-sizing:border-box;position:absolute;top:50%;left:50%;width:20px;height:20px;margin-top:-10px;margin-left:-10px;border-radius:50%;border:2px solid #ccc;border-top-color:#000;animation:spinner 0.6s linear infinite}@keyframes spinner{to{transform:rotate(360deg)}}.default_styling .radio-cells .option-pill{position:relative;margin-bottom:11px}.default_styling .radio-cells .option-pill label{padding-right:40px;font-size:12px}.default_styling .wizard-tooltip{display:none !important}.default_styling input[type=checkbox],.default_styling input[type=radio]{-webkit-appearance:auto}@media screen and (max-width:1299px){.wp2fa-modal .modal__container{max-width:96vw}#wp-2fa-totp-qrcode{max-width:100%;text-align:center;margin:15px auto;display:block}.qr-code-wrapper{float:none}.step-setting-wrapper br{display:none}.enable_styling .radio-cells{display:flex;flex-wrap:wrap;margin:0px 0 20px;width:calc(100% + 10px);position:relative;left:-5px;flex-wrap:wrap}.enable_styling .radio-cells input:not([type="radio"]){margin-top:5px}.enable_styling .radio-cells .option-pill{flex-basis:0;flex-grow:1;border:3px solid #eee;border-radius:10px;padding:10px;margin:5px;font-size:11px;position:relative}.enable_styling .radio-cells .option-pill p{height:100%}.enable_styling .radio-cells.max-3 .option-pill{flex:33.333%;display:flex;flex-direction:column}.enable_styling .radio-cells.max-3 .option-pill p{flex:1}.enable_styling .radio-cells .option-pill.isSelected{border:3px solid #007cba}.enable_styling .radio-cells .option-pill label{display:block;font-size:13px;line-height:24px;font-weight:500;margin-bottom:2px;height:100%;max-width:calc(100% - 20px)}.enable_styling .radio-cells .option-pill p{font-size:12px;line-height:23px;opacity:0.9;height:auto}.enable_styling .option-pill{margin-bottom:15px}.enable_styling .tooltip-content-wrapper{display:none}.wp2fa-modal .modal__content code br{display:block !important}.wp2fa-modal .modal__content code.app-key{width:100%;display:block;text-align:center;font-size:16px;margin-bottom:30px;background:#efefef;padding:5px;word-break:break-all}.wp2fa-modal .modal__content .apps-wrapper{margin:10px 0}.wp2fa-modal .modal__content a.app-logo{padding:0 5px}.wp2fa-modal .modal__content .wizard-tooltip{background:#3e6bff;color:white;height:18px;display:inline-block;width:18px;border-radius:50%;position:absolute;overflow:hidden;text-align:center;line-height:18px;font-weight:700;margin-left:4px;margin-top:3px;bottom:10px;right:17px}.wp2fa-modal .modal__content .inline-helper{background:#eee;padding:10px;border-radius:10px;display:block;margin-top:10px;font-size:12px;line-height:24px;display:none}.wp2fa-modal .modal__content .click-to-copy{border:2px solid #3e6bff;border-radius:14px;display:inline-block;font-size:10px;padding:0 9px;font-weight:700;line-height:16px}.wp2fa-modal .modal__content .click-to-copy.done{background-color:green !important;border:2px solid green;color:white}.wp2fa-modal .modal__content .click-to-copy:hover{background-color:#3e6bff;color:white;cursor:pointer;opacity:0.5}.wp2fa-modal .modal__content .app-key-wrapper{background:#eee;border-radius:4px;margin:10px 0 5px;overflow:hidden;padding:3px 7px}.wp2fa-modal .modal__content .app-key-wrapper input{background:transparent;border:none;display:inline-block;font-size:9pt;margin:0 10px 0 0;max-width:15pc;min-height:0;min-width:200px;padding:0;color:#888}.wp2fa-modal .modal__content .app-key-wrapper input:focus-visible,.wp2fa-modal .modal__content .app-key-wrapper input:focus{border:none;background-color:transparent;outline:none}.wp-2fa-configuration-form .button{width:auto;display:block;margin-bottom:10px;margin-left:0px;margin-right:0px;text-align:center}.wp2fa-modal .modal__content .wp2fa-setup-actions .button,.wp2fa-modal .modal__content .modal__btn{width:auto;display:inline-block;margin-bottom:10px;margin-left:0px;margin-right:0px;text-align:center}.modal__content input:not([type="radio"]){width:100%;margin-bottom:10px}.hide-on-mobile{display:none}.wp2fa-modal h4,.wp2fa-modal h3{font-size:20px}.wp2fa-modal h4{clear:both}}@media screen and (min-width:1300px){.wp2fa-modal .clear-both{clear:both;display:block;overflow:hidden}.wp2fa-modal .modal-50{width:50%;display:inline-block;float:left}.wp2fa-modal .modal-60{width:70%;display:inline-block;float:left}.wp2fa-modal .modal-60 .radio-cells{width:100%;padding-left:10px}.wp2fa-modal .modal-40{width:30%;float:left;display:flex;justify-content:center}.wp2fa-modal .mb-20{margin-bottom:20px}.wp2fa-modal.enable_styling p+.radio-cells,.wp2fa-modal.enable_styling p+fieldset{margin:20px 0;display:flex;flex-wrap:wrap}.wp2fa-modal.enable_styling .radio-cells{display:flex;flex-wrap:wrap;margin:0px 0 20px;width:calc(100% + 10px);position:relative;left:-5px;flex-wrap:wrap}.wp2fa-modal.enable_styling .radio-cells input:not([type="radio"]){margin-top:5px}.wp2fa-modal.enable_styling .radio-cells .option-pill{flex-basis:0;flex-grow:1;border:3px solid #eee;border-radius:10px;padding:10px;margin:5px;font-size:11px;position:relative}.wp2fa-modal.enable_styling .radio-cells .option-pill p{height:100%}.wp2fa-modal.enable_styling .radio-cells.max-3 .option-pill{flex:33.333%;display:flex;flex-direction:column}.wp2fa-modal.enable_styling .radio-cells.max-3 .option-pill p{flex:1}.wp2fa-modal.enable_styling .radio-cells .option-pill.isSelected{border:3px solid #007cba}.wp2fa-modal.enable_styling .radio-cells .option-pill label{display:block;font-size:13px;line-height:24px;font-weight:500;margin-bottom:2px;height:100%;max-width:calc(100% - 20px)}.wp2fa-modal.enable_styling .radio-cells .option-pill p{font-size:12px;line-height:23px;opacity:0.9;height:auto}.wp2fa-modal .wizard-tooltip{background:#3e6bff;color:white;height:18px;display:inline-block;width:18px;border-radius:50%;position:absolute;overflow:hidden;text-align:center;line-height:18px;font-weight:700;margin-left:4px;margin-top:3px;bottom:10px;right:17px}.wp2fa-modal .inline-helper{background:#eee;padding:10px;border-radius:10px;display:block;margin-top:10px;font-size:12px;line-height:24px;display:none}.wp2fa-modal.enable_styling .tooltip-content-wrapper{display:none}.wp2fa-modal .click-to-copy{border:2px solid #3e6bff;border-radius:14px;display:inline-block;font-size:10px;padding:0 9px;font-weight:700;line-height:16px}.wp2fa-modal .click-to-copy.done{background-color:green !important;border:2px solid green;color:white}.wp2fa-modal .click-to-copy:hover{background-color:#3e6bff;color:white;cursor:pointer;opacity:0.5}.wp2fa-modal .app-key-wrapper{background:#eee;border-radius:4px;margin:10px 0 5px;overflow:hidden;padding:3px 7px}.wp2fa-modal .app-key-wrapper input{background:transparent;border:none;display:inline-block;font-size:9pt;margin:0 10px 0 0;max-width:15pc;min-height:0;min-width:200px;padding:0;color:#888}.wp2fa-modal .app-key-wrapper input:focus-visible,.wp2fa-modal .app-key-wrapper input:focus{border:none;background-color:transparent;outline:none}.wp2fa-modal .option-pill{margin-bottom:15px}.wp2fa-modal .qr-code{float:left;width:100%;position:relative;left:-2%}.wp2fa-modal .mb-30{margin-bottom:30px}.show-on-mobile{display:none}}@media (max-width:500px){.modal__container{padding:25px}.option-pill:not(last-of-type){margin-bottom:40px}.wp2fa-modal .modal__content .apps-wrapper{display:block}.wp2fa-modal .modal__content a.app-logo{width:calc(33.33333% - 13px);display:inline-block;margin-bottom:5px}.wp2fa-modal h4,.wp2fa-modal h3{font-size:18px;margin-bottom:9px}#configure-2fa-backup-codes .wp2fa-setup-actions,#configure-2fa .wp2fa-setup-actions{margin-top:15px}.wp2fa-modal .modal__content .modal__btn,.wp2fa-modal .modal__content .wp2fa-setup-actions .button,.wp-2fa-configuration-form .button{display:block;margin-bottom:10px;margin-left:0;margin-right:0;text-align:center;width:auto;padding:10px 15px;font-size:12px}.wp2fa-modal .modal__content .radio-cells .option-pill{flex-basis:unset;flex-grow:1}.wp2fa-modal .modal__content p+.radio-cells{margin-top:20px}}@media screen and (min-width:801px) and (max-width:1299px){.wp2fa-modal .modal__content p+.radio-cells{margin-top:20px}.modal-50{width:50%;display:inline-block;float:left}.mb-20{margin-bottom:20px}.modal-60{width:70%;display:inline-block;float:left}.modal-60 .radio-cells{width:100%;padding-left:10px}.modal-40{width:30%;float:left;display:flex;justify-content:center}}@media screen and (min-width:1024px){.wp2fa-modal .modal__container{min-width:768px}}@media screen and (min-width:501px) and (max-width:800px){.modal-50{width:50%;display:inline-block;float:left}.modal-60{width:60%;display:inline-block;float:left}.modal-60 .radio-cells{width:100%;padding-left:10px}.mb-20{margin-bottom:20px}.modal-40{width:40%;float:left;display:flex;justify-content:center}}@media screen and (max-width:800px){.wp2fa-modal .modal__container{max-width:95vw;min-width:80vw}.wp2fa-modal .modal__content .wp2fa-setup-actions .button,.wp2fa-modal .modal__content .modal__btn{width:auto;display:inline-block;margin-bottom:10px;margin-left:0px;margin-right:0px;text-align:center}.wp2fa-modal .modal__close{right:15px;top:15px}}@media screen and (max-width:500px){.mb-20{margin-bottom:15px}.wp2fa-modal .modal__content .app-key-wrapper input{max-width:200px}.enable_styling .wp-2fa-button-primary,.enable_styling .wp-2fa-button-secondary{display:block !important;margin-left:0 !important;margin-right:0;width:100% !important}.wp2fa-modal .modal__content .wizard-tooltip{right:5px;bottom:5px}.verification-response:not(:empty){width:auto}}.wizard-custom-counter{counter-reset:step-counter;list-style:none;margin-bottom:0 !important}.wizard-custom-counter li{counter-increment:step-counter}.wizard-custom-counter li:last-of-type{margin-bottom:0 !important}.enable_styling .wizard-custom-counter li::before{content:counter(step-counter);background:#3e6bff;width:20px;height:20px;color:white;text-align:center;display:inline-block;line-height:20px;position:absolute;left:-20px;top:8px;font-size:14px;font-weight:700;border-radius:50px}.enable_styling #backup-codes-wrapper{border:none;border-bottom:3px solid #d0e5ff;color:#4498ff;font-size:1pc;font-weight:700;line-height:1;margin:0;min-height:2pc;min-width:15pc;padding:10px 0;width:100%;line-height:26px;background-color:transparent !important}.enable_styling #backup-codes-wrapper :focus-visible{border-bottom:3px solid #4498ff;outline:none}.enable_styling #backup-codes-wrapper:focus-visible{border-bottom:3px solid #4498ff;outline:none}.default_styling .wizard-custom-counter li::before{content:counter(step-counter);width:20px;height:20px;text-align:center;display:inline-block;line-height:20px;position:absolute;left:-20px;top:8px;font-size:14px;font-weight:700;border-radius:50px}.mb-0{margin-bottom:0 !important}.wp-2fa-user-profile-form{margin:0}.wp-2fa-user-profile-form:first-of-type{margin-top:20px}.wp-2fa-user-profile-form:last-of-type{margin-bottom:20px}.wp-2fa-user-profile-form tr+tr th,.wp-2fa-user-profile-form tr+tr td{padding-top:0}.remove-tr-padding th,.remove-tr-padding td{padding-bottom:0}body:not(.wp-admin) .wp-2fa-user-profile-form td,body:not(.wp-admin) .wp-2fa-user-profile-form th{padding:0;border:none}.wp-2fa-user-profile-form{border:none}.wp-2fa-button-primary+.wp-2fa-button-secondary{margin-left:10px}.modal-logo-wrapper{text-align:center;padding:0;margin:0}.wizard-step label[for="authy-token"]{min-height:70px;-webkit-transition:min-height 0.2s ease-in-out;-moz-transition:min-height 0.2s ease-in-out;-ms-transition:min-height 0.2s ease-in-out;-o-transition:min-height 0.2s ease-in-out;transition:min-height 0.2s ease-in-out}.wizard-step.expand-panel label[for="authy-token"]{min-height:300px;-webkit-transition:min-height 0.2s ease-in-out;-moz-transition:min-height 0.2s ease-in-out;-ms-transition:min-height 0.2s ease-in-out;-o-transition:min-height 0.2s ease-in-out;transition:min-height 0.2s ease-in-out}.wizard-step.expand-panel .authy-step-setting-wrapper{overflow:hidden}.wizard-step.expand-panel .iti__country-list{width:100%;max-height:240px}[aria-describedby="authy-diag"],[aria-describedby="twilio-diag"]{min-width:400px;padding:10px}[aria-describedby="authy-diag"] .ui-dialog-titlebar,[aria-describedby="twilio-diag"] .ui-dialog-titlebar{background:#fff;border-bottom:none;height:auto;font-size:18px;font-weight:600;line-height:2;padding:10px 36px 0 16px;word-wrap:break-word;font-family:helvetica;font-size:22px;font-weight:700;margin-bottom:15px;margin-top:0;margin:0 0 10px 0}[aria-describedby="authy-diag"] .ui-dialog-buttonpane,[aria-describedby="twilio-diag"] .ui-dialog-buttonpane{background:#fff;border-top:none;padding:16px}[aria-describedby="authy-diag"] .ui-dialog-buttonset .ui-button,[aria-describedby="twilio-diag"] .ui-dialog-buttonset .ui-button{background-color:#fff;color:#3e6bff;font-size:14px;line-height:14px;margin-bottom:10px;outline:none;padding:13px 13pt;text-align:center;text-decoration:none;font-weight:800;display:inline-block;border:3px solid #3e6bff;border-radius:30px;text-transform:uppercase;letter-spacing:0.3px;-webkit-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out;height:auto;margin-left:0}[aria-describedby="authy-diag"] .ui-dialog-buttonset .ui-button:hover,[aria-describedby="twilio-diag"] .ui-dialog-buttonset .ui-button:hover{background-color:#3e6bff;color:#fff}[aria-describedby="authy-diag"] .ui-dialog-buttonpane .ui-dialog-buttonset,[aria-describedby="twilio-diag"] .ui-dialog-buttonpane .ui-dialog-buttonset{float:left}[aria-describedby="authy-diag"] .ui-dialog-content,[aria-describedby="twilio-diag"] .ui-dialog-content{padding:0 16px 9px;overflow:auto}dist/images/wp-activity-log.jpeg000064400000005132150755130600012665 0ustar00���JFIFHH��C		



          ��C

                                                 ��Z�����8!1A"Q�2aq#3br�B$DS�������%1!Q"2Aa#q���?� �+W��F�c0�A.8�~��&�^�Y3��C6�-�x��=(�\|R��7h�7o����Q|�e��jK�1��ˮ�w��?��,j]��*7:U�sǩ��\�z�%j�Y�9?]��0��zw�鳣q�L�
�u��TIDږ��Q�YA���ԌōO�M5�p֯;K%��_y3�>��Ć�C��-*$�L�8)�N7�)
xo���G�Ŝu���T�娲Yu���)�q��KB�$�G���L�1��L��5n�qiF}̀O�Ƭ��"�#T JR�KO�6����1GY���Ntr2<����UzQ�L����B?�з�Kl9&���o6_-D�}q�K�R��%�a<��ˈ-'�;ۀ�u�JJ氕u����w��T|�t}%��5�w��a�y���G�Ũ���n{��Кm;!��w�֥��Y�Ϩ	��Ti�qڔӒ�)ZMe��R<�%~C6n>�Zl��fI/s�yQ��͌�h��ډI�,��PR�,�u�\�*��H%�D�������oj��s��w��{�m��>�z`qF���
����ό�P�6�2x-T��$Z��I���C�V�V���ԩo)�
8��g��wB��T[`sr��o�6�q�c�t���v<��܌�^72?�0ֵk��<��u���ԭ�w.���{�5	���k�Ϳ�		�6]����9t�ʝS���o(�Y:�D�r��gӛ��C��]�J��Ի�;�(�iq�Sq����G�L�O��Y'������*ӎ7!�}��t���V<ڌ��t��m�j�.)L�]��r*M�b�	MSҵs�d�i9^�K2�ݷ�J���a�$����g�x?��E�G���f�>��\�mٴZ��c,�!�Ze��)h-��m��.��ƞڴJ��tU4������Z���2,��Ϡ�	hd�BϠZ�;:?�a�9ĩJ^W���3�B���>����s2��T�;:6vI���BK�ŭIԪ-ѝ.
�.ie�u��8Y��ۆfe��Q���q6ȶ��Ɵt��Z��)y�j���"?&t�Ivaf=.�83iJ�^�O�ȝW��z֥鐄s��g�̣#�c�\]un�㿪T��څl�WR�u%�hJU�$�}L�X�r�G��T;q��Hi���,$���j��uJ?v��2�Ǒ��۟�q���n�ku��޷xgL��3j��
��K����IvӜ�=7�3���A�hw��9KbZ�!\�u���=���[ʶ��LU��j����WI��xr&�S��T�g�yO�����6�Gr��H���ƣ������y}���i�L���rA���YQ��a�թ�(��=q��e��o�#�׎I����soT��H��]��%Ǝ�N�Zw#RR^l|�ש�6����E��+5�
�L�P��Rul[w3�>>���F+�8���G�xMC���$��m�\�q�I�%zӫ;���a&�O��:���q�)�mj�D�Z�h����Y���G�/�_���<&��/�����-N%JmIJ�(��+�?QUA�8+hR���MrL�y��9�)E����ə��f*ǧp6ۍB�P�I~]6{ɒ�jҕFu9-m(����y�N(h��B�ßR�L����R��oI�;o�zlA��_\>�w;JrL�c)�}�h�z���z�`L,Ëۇ���yr%�T���I��RU���	�a��^��yP�J��9���I=hQ�z}�a&�OX�*�aƳQ%�Т򴾒I�ͣ�L�y�s.\�9�^��Z�Lu��	�L��Mj�'Id�m���N��4����Zb��R��d�I9?aU]��}�t֮���ե�ad�J2�~S-Ϩa"<C��+�8��;��o!l�L��:�I�c-]'�#�:5Z�>�|�I�jB|Ģq�I�c'�/���C�&e^|�Te�f��Ț/�ݽpD'�^�)�n�E�kv�R��n,�fHZP��&]�=��jjY<+���ʕ"�Y}:>b�,�}I?\nfaEWQ�C&YH�!��_��m
jF-#
%�J��Z֬��6>�ǡ�4�1�߱�D��dist/images/wp-2fa-white-icon20x28.svg000064400000006076150755130600013354 0ustar00<svg width="1500" height="1983.1" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" version="1.1">
		<g id="Layer_1">
		 <title>Layer 1</title>
		 <g id="svg_9">
		  <g id="svg_13">
		   <path id="svg_11" fill="#FFFFFF" d="m802.2,1239.7l-33.6,-65.5l-19.8,-39c23.3,-12.9 39,-37.9 39,-66.4c0,-42 -34,-76 -76,-76c-42,0 -75.9,34 -75.9,75.9c0,14 3.7,27 10.2,38.3c6.5,11.2 15.9,20.7 27,27.3l-19.9,39l-33.6,65.6l-30.1,59.1c-8.3,16.3 3.6,35.7 22,35.7l198.5,0c18.4,0 30.1,-19.4 21.8,-35.7l-29.6,-58.3z" class="st1"/>
		   <path id="svg_12" fill="#FFFFFF" d="m1158.3,828.5c-288.8,-49 -443.1,-227.4 -447.5,-233.9c-4.4,6.3 -156.5,184.5 -442.5,233.1l-50,8.5l1.9,50.7c0.3,7.1 7.2,176.3 66.6,362.5c81.5,255.9 205.1,420.1 392.6,473.1l31.1,9.2l16,-4.1c442.1,-125 473.9,-811.9 475,-840.9l1.7,-50.4l-44.9,-7.8zm-297.9,511.2c-10.9,17.7 -29.7,28.3 -50.4,28.3l-198.4,0c-20.8,0 -39.7,-10.6 -50.5,-28.3c-10.9,-17.7 -11.8,-39.3 -2.3,-57.8l30.1,-59.1l33.6,-65.6l7.4,-14.3c-5.2,-5.7 -9.7,-11.9 -13.6,-18.6c-9.8,-16.9 -15,-36.1 -15,-55.6c0,-61 49.6,-110.6 110.6,-110.6c61,0 110.6,49.6 110.7,110.4c0,28.6 -11.1,55.6 -30.1,75.9l7,13.8l33.6,65.6l29.6,58.2c9.5,18.4 8.6,40 -2.3,57.7z" class="st1"/>
		  </g>
		  <g id="svg_15">
		   <path id="svg_14" fill="#FFFFFF" d="m1203.2,841.8l-108.4,0l0,-367.6c0,-196.7 -168.4,-356.7 -375.3,-356.7s-375.3,160 -375.3,356.7l0,211.4l-117.1,0l0,-211.4c0,-261.3 220.9,-473.9 492.5,-473.9c271.4,0 492.4,212.6 492.4,473.9l-8.8,367.6z" class="st1"/>
		  </g>
		  <path id="svg_16" fill="#FFFFFF" d="m710.8,737.8c79.7,64.5 210.5,145.6 384.1,184.1c-12.7,138.8 -77.7,602.6 -383.4,701.4l-2.6,-0.8l-0.5,-0.1l-0.5,-0.1c-71.2,-20.2 -131.3,-61.3 -183.5,-125.9c-54.1,-66.8 -100.7,-160.6 -138.3,-278.7c-38.9,-122 -53.6,-237.3 -58.9,-295.4c179.2,-38.8 307.1,-121.2 383.6,-184.5m0,-143.2c-4.4,6.3 -156.5,184.5 -442.5,233.1l-50,8.5l1.9,50.7c0.3,7.1 7.2,176.3 66.6,362.5c81.5,255.9 205.1,420.1 392.6,473.1l31.1,9.2l16,-4.1c442.1,-125 473.9,-811.9 475,-840.9l1.7,-50.4l-45,-7.6c-288.8,-49.2 -443.1,-227.6 -447.4,-234.1l0,0z" class="st1"/>
		  <g id="svg_18">
		   <path id="svg_17" fill="#FFFFFF" d="m1340.5,676.4c-11.5,-1.9 -26.6,-4.8 -37.9,-7l-4.5,100.7c0.3,0 -0.3,4.4 0,4.4c-3.9,63.2 -13.6,276.9 -81.8,511.7c-95.2,327.9 -277.2,522.3 -504.4,589.4l-17.1,-5l-0.5,-0.1l-0.5,-0.1c-112.3,-31.7 -206.5,-96.1 -288.2,-196.9c-81.8,-100.9 -151.6,-241.3 -207.7,-417.3c-68.7,-215.8 -86.3,-417.7 -90.2,-477.6c177.9,-32.8 348.8,-103.4 495.3,-204.8c43.3,-30 79.3,-58.9 108.2,-84.4c29.3,25.7 66,54.9 109.8,85c49.8,34.1 102.5,64.5 157.1,91.4l0,-117c-172.4,-94.6 -263.5,-200.3 -267.5,-206.1c-6.5,9.2 -225.8,266.3 -638.6,336.4l-72,12.3l2.7,73c0.4,10.2 10.5,254.4 96,523.1c117.7,369.3 296.1,606.2 566.7,682.7l45,13.2l23.1,-5.9c638,-180.3 667.8,-1175.6 669.4,-1217.3l2.5,-72.8l-64.9,-11z" class="st1"/>
		  </g>
		  <path id="svg_19" fill="#FFFFFF" d="m831.8,1297.7c8.3,16.3 -3.5,35.7 -21.8,35.7l-198.4,0c-18.4,0 -30.2,-19.4 -22,-35.7l30.1,-59.1l33.6,-65.6l19.9,-39c-11.1,-6.6 -20.5,-16 -27,-27.3c-6.5,-11.2 -10.2,-24.3 -10.2,-38.3c0,-42 34,-76 75.9,-76s76,34 76,76c0,28.6 -15.8,53.5 -39,66.4l19.8,39l33.6,65.5l29.5,58.4z" class="st1"/>
		 </g>
		</g>
	   </svg>dist/images/wp-security-audit-log-img.jpg000064400000006551150755130600014417 0ustar00���JFIF��C




��C		

��Z���	�����ץ�:���t�W�Xz��ڸe���hX����{��ې=A��:C���b/���j�Vª���o�����E��7��M<9Dx�������{�⾑��mqwW引����'�]��w��L�V2v��-�T��h�r5���\�-�*��t���FU����n�e���#�~O���y���%̠�s�T�r��L���zO��Нc~�`v�5�kX��-8v	2@`#16Qs����Xg�[b�.Kb�6��!�x�X͈�'N&w϶�rn�ם�:{L{�0ca�����>�D4����vj�p�85�o��m�C�����1��lz�SǕތ�"@M��"Y���G��G
�FZ�m�ܤ��\��I�0Xdx�b��a	�|\{clz~�*�m�����r��.(�]:�g;�[y#�y9J��i�d0�.D*��_������g��M�ʥ\�t:��%|�X�c&����Arc,�MMXh�W��^oV2�φ�\k�ޮējuЬr�(Ls��CgJ��E��nF�FW^k8T����HS�.�ޒ�h�m��Q�""�ݑZ���l��q�U�Wr�Z���k��9��G� ]_>C�G&�%�Y-o;�
�z���N��>e�D���@3-f���S/��p`�UQ�ai~,ZNt����;�%�����v*�wlٕL�J�Yk�g?��&�>N�(M
�Yq���{զ)�Α����Ϫ�O%}��#%h�i4��n��?�4�"]�ڋ�+Y����?�LH F6Nk��T�k�=f%�0�7�m~��E��a�񑈙����m��^7����#s����ɤ���t���h�Ut�Y��8_)�r��$C7>%0�L�h���.��j[=F��j�~��_�S�1���Wa�r[ū�r�
4I0I�?Q\7�G^�~�*���z�nuW{G�뷘m�N��$�E�'�H�����l�Ws�/=yqQ�x��D�ɮ?-��O���S�ˑ�@��1Eů�������4���\e�"���3����;�t���rZ͙�:ZU�+_Fց*� �P�|7���yJabB��}�y��%z�K��>�����x%����b61�4�?���|��]��a}a�M#�=q�qr���2�I�����g�N}\�u�t�Ef4}D%��wZCkog\�0�
��C�չ�n�{�Q&YNIɌe���@���4��.�USE;y�V&%o�p�d�N�\h3�%C&s�$1�ـĐ`���~D't��s}�v���љ5mI/�y*�dr4;��^s0�2��)3$_;��J3-�ȗ>���A<eE�����H2J���D�4��uY�n,���p��1��ZR�.B�D��l�q�E�}IY���+`��x�,�4:�H�MdüR餑o�u�N��q�K�4	3��K�\
�[-?C�5�ë�[I��c`�|�8W
�Lj�����B$�����>�M�Q��<
!1Aa"2QBq�#@R`���$3CSTcs�������?��<�"���2L��O��%/q�6�P��
#�-_�����@}R&�F�Zjư0�)n��uBn�1��u�ɞFTR�(��@�^��x�SL���`��4�0��I�Ơ�&#r��k�-�a�ۂ ���#�A �#Q�f���٘*""��3�@�&��x$�g�9�̋j��J�L��,��N���\�#[lj^�X��y7�OBb�,�����W�BPH��XXʕ�(��R�h�B����.`td �ɒ<a��I$'ڏ8ẽj��6f�B,v���܋^><�2���,Ev���6�����k:���"��C�ؾ9dG�|�٬t
�q|�j�9���YH��1�3�<09��Tߜvo�)�Pm���c]I�AZƼ�"`��s�S�(�V?��k���0��^�wKS��Wg29� Y�&.PcqH�>챘b�sԧ8���X��|�b�4ʋ4��U$سr~U�*�^��acKϏ�����8�6D\<
���~9{;�gXf��RD�}������}�Z�G�`'�!��y�(NF��aj���0��b����;0���Th�R��E�a�2F�dt��r�Ss�'�jce�9)'f��e�&�UE�@��I�=	��!k)>C*�¢�,���f<��L����3r}h���G6ݘ��w���*�M;�^���Ǚ�m�Ig�8�	�s

�cU��@ۅ%A#�<*g�7 ���$j*D��)�f^����F�Pۀ�㝅O`���q}�HG���f�9�$�MK��$�ڀu�"5,wca�j�SG�[�U�CՁ��*"��4zX]XlAph���_IzV#:�9ǰi�v�^�C�9ky_�?��6
!1 AB"02@QR`b�#$aq�����?�h¢���Sչ!:�BMS�#�5]�$������.�*2%�4E{	ϳ�|��[W^I�����D��QRFX���Ӹ�
dB��ddx�i���wbw�%���a��B[�x��	_���
HM��L��w�1�3����y����T��gyȁԏP>��{����8AX`�Xv��![*����*�DY�����5���gUn!D_��<C�}zG���QJ~#^s�����t�'l�\c�+�_.���^�
��G+I�G�{�Cõ�Ye2Iv�BU��1!"1@Aq�2QR`�3ab����?�����KJ�pW�b��d:��H��]"���,����]�~�4���(�R`�Y��/�{�O���]�~�f����W�p���od"C�>�n��R�63��E��J�2l
��XHrE]���F�:!	��b�(�]l��vi�✢����.U(;�p�oTX�����dist/images/password-policy-manager.png000064400000020276150755130600014242 0ustar00�PNG


IHDRr�ʂ
�sBIT|d� IDATx��ip\ו�羥_�
46�$6��	I�dK�e��Z=�;q&^z{Ɠ8�ʇDS��L9�fT��r�M�S�hJ�E�-��a,[�ER�$�N$b�}{�ݓ
P���t��BQB�����ι�\�
��Oׂ]��}�q�^�����	�1C@H&2�����	x�6��Y���<��V-��������WI�W�D7���@u����`�`�d�����\>�
���"n���%�;>4���U(��=l%"�f"������b�Lf^�K���s���Er�d�>�:�3:��U�|�aZ��ω+��l���&��D.���s߼�
�B7���ê��t� ���P�Z<'��^�E)�od�����Fܿ����#�9��:�PQ����"j��@�.D�M$��Y�7��g��5���[�0��vE���G 4�!)`)��A��0�o���������z�k���"��U��nV4�� !"�YD�(�E�!T
BQ\�0�434uѭZ@�̞]�q��us�ȡ��w��C!�E����Z5)м>xC��GZ�5C�z�B��Y��� �J �C&>�|:�,T\ե	��9&|%�����=���א�!�Ȉb��ޡ�+���Y�HD"���N4u�����?E�"\)b�$^������+�C&1���qĦ. _��+]-�E��t��z���|T��C��u&"P���?��~D��7Ch��pW	�•UF� m�l
��1�^x��˕	
Hb�,O'��'Se�T�{�$���nMQ�ݕ��h�v�B��w��g7�@�8�	��Dp��+?B@�x�onC����LV>W�@L"P���fp��0u��ƃ��ݟ�t�_�� R]�%�7E��w�s���HQ@K���E�<^���l��6r�8X:kCŞ����UM����Yu��d��|��ϧ��D���ۘHBA�c;��ޅHg/U_3W�����D;��:2�H�urJ j'��?4Z8}df�+��92�xM��B��D�D���~�x|���	�f����#-���X��]-�@�"W�8�3���^�%�G��w���AD��2�j�n߉����7L�e�[�PTxCM�<^dbs�
��Y��44�g�p���Fյ�B��z�	�A�9]&��^����6T�7S�j�̓��l���73`)���NYlD[C��U�"x��!�7����wB����oԉ�hD��D����� ���n����G��"�W���W�9/5<���߃P�֪EdfHi��M!�8���%$f."9?�Ll�l
,%H(ş*�'"U�n�a�y�K�g����i ��}�h�
��S�5�JGi����y��=��-=U�df�U�!9;��Kg���B!���,M:�@�U7�GѴ�M�}0����`-݃H�O#�u����D�R#�k�{Ft�5t7�L8�F"��Z�{`�nT,�m�H�N��k/`��Q$�`f3��	f����mv!�\*����/B��@��W�8��m�����5&� ��w�[���Z|'�;ú�~��!��f�ѹkᎮ��lf>����1���H�M��L�+�n��QȦ���B.�������䇄@.G>�`be�N�����R5ҰɎ�r��׌D��h��X�B6��/��/�/%ZFy�]��38��Crv�Z;�D�@S"]W�c%�!b��#���B�AmN�����Pu��"�|3�~��Ga���J�����.�v�ļ�jD;�or|,�(�x�c���[]�Y�<I/q��O�
4oﯨ5ږ���8&^?긞��6b�0u�
�t���H�-[\
e"P�!��5�h	"�_�=���s"o$
#�T�,fF.����/��gִ� -���?YQW�>��-�=�Ҿ�m�=k]׫i���]��ױ��p�kFi��]����dw&��qe׀��D��]>����r�XE�0�a��͎W0�⭨�������a�]��B�掊�4�̍�.;3�?Z����	3�Arv3�G���r.���,b��u(v�_F�	�Y�?.f�v��`�^��/d�US����R|�h٢��ȥH�O�^g#ض��h���jW�t��P��-�=��'�߸�I!9?�6�Pv}�>x|A�ω��)jx��P!�ߵ�U���v��P=޲EI�����}��h��
E{�oY� EA��
���j��X�D>�@6Y��h��.�sضe�la5R!3� ���	�F���Ѷ��ϻ^�
6���&;jqScт��3x������O'��TM��9�"��
E��@���(��n�T�2s����p'M�����uf��
Ea���m`�e�U�YjP4���+[$�@Cf��6>A��E�BQ+�]U�f��ogKؖUQՖ�n�X�b�mN� ]�ū��U�2Y%P�*�U�����~%n�D��[6ܱ\�\����zM��|k�)�
¦�7�B� l
y��)�
�%$���"�/�WG.n-�q�F�R�|.�*U�7T�9�������u�
��HUu\/�+!U�@��𔰏*���.[�V\�����J�T�@�mQW�͆����onC��0Bm[�hU��`��[��ATa�#�nC�{j�E�@h:��(����E�u��1,�ռ��hr�8�eA��񇡨Z�!��#�m�-[�O'�����������X�'
�/�X����X���g
�[/�;!��^���.o=���L0'/��6�sS8�ҡ�W��d0]�Ь���(��[�?��a��۹��n0�KDo'�խ�FCmX��`�J��`)e�}?�߳X��k+����}	�� �@i`nh2��H1�$&���iK��>y8T�V���ԌN�.!�g�hU�poT��K@F�_����M[O��;Qk��ɿ��6�����Q��7[_�,�Rř��,_ɝ8�K��I��@V)���P��E�Q�qIqo��	f6@�/Cj[�4��~g^�J��Q���y�*��ѝ��Ҭl�Ε=D��ׄ4́��q�h�QJ59�5ݩ*�׈h7�f+\�&DA"ڧ�f�M'q�XEbV+���w�<��]�r�w\YQ�c@3|PU np�2N���|�u?�$����ӷ���nw��m�����}���Y��D��w!��o(
��C�np�)a%l3�\*^L<1=�t|�\N��K0�����d=U.�vU��G�O��u�����=�?�.�b��
�	o�`3��]h�߇��bX�ܔk0����o�z1��v��ߪ��g�VU�c"r��"�۶�k�]D;�x������򑥍\*��s��9w�5�e)Ks���&m�cx�ˎ�Mv��4#����]�I��w/�n{���+����"o$(4���v��T�1Phi6�Ц>k�<��Y���;>�~"|�1���[�v�E�a�^�39^/\�q�j��y�M.��	D@XTv��:q�d<|%�N�0���e<%B��ݷ�]Uƪ�2ˡ}��t��{÷Z!H�G6�/(wC�O`Aw�u��@ρahߦ�UP����=����*�],�)Cx�`K�k��>5�_���
:w�/f7��j��7���F�r*�AC^U�Y���]+���Z��Dێ�6E\D�PK'��eӡ�$l.�x�]�>�
B�c����h��M*C��-[���9��3�\��]��j���T��6[�@D�EZ���aA�el+vi�
I{ܺUM���䲃c���}���""�KY��ɽke��|�hE)�6�"��wϐ�h$WX�܅$v� "����lR��_Q]2mL�"�T��.��� h����q�P�W?�*�`^p��-G�&�ATvk(����~Ò�iv�w)T�j�IP�n>W!�S���$��q�	UC�~��]R@o�P�';G�Ȥ
�Y������/҂Ȗn��v����M�K�?y|6=2������h!E��y6�T�ʚԓO4o��}Ah/�R�QD�ԟ�������mT*����5u}Q���yL�&kG5Ii*�	�ɺPE��uɎ���Py�*�{V�M֕�f��X06;fz��ec˯��I"�/�s���l��@���FRL�]���
�y�S��|�ġ�<œP;vނȖ�
��MJ��"f���狹sd�\L$�^�Z���w�m۫J��ض]C	0����>���Oݯ��:��]�)��G>�Dr~
-]���u�`d�l�D��E����3%_��*`.�Djn
�7�c����K'�:A�<A�8�������hk�.���*SCZҋ��O��TF0��S�6��8�P��c�K�1g�hdž�*�^����l��q��'�'�]�{��{��>�>'sK��Ǝ���Y$�+:	�03r�8&O�T<���z�3I�]8�ũ��7�*��]|0�6�!�U�y*°�<�/�������Qȭ���吶���4���`�d�p��@�zQ<�=���H
�\RH��{�c��WYJ�R1���k����X��t��3�E��E�]�=�m#�X��8�z"mV!Wf�C�6��7�����
��]�v\*�<�S/<���������b�yd�s5�,d�M�?��ޘ�̜�Y\��
+�3+���ɻ��fW3�lӧ^Fjn[v�����yOٶ˾7�m�*�����b��0s�g;3��,B��B���)�^-K�Bbn��֏��o�7�\��
Ѫ��6FDu>�D�%��9��ɕI	��Z�G>��@���f.��s�q�W� 1siM�}3BQ�{k?�Qhz���U!�
3_�6m�*9�gf�S	�ሀK`��`~M��H�)��k�z,.Ufp��/��]X���D;��P�5O}BXJd��p�K`)ዴ��{�֭�7V!�\*����!�d�7Z�#��'��
��D6����ڷJ�c ��U�>[U7l�O��������ƅW~��׏ 1;��f�cGa���kf��d9�c����p.mJ��}EH?�p�رJXJ�΃y�	��Zо��� ��܎���k>FZ�'�aq�<�m��`)���a~��5�g����]�����,~�͒^���}r+���H�`tPf{�rt�ZCD�����gw�z,}	�h���7]s!����Y�5!#�C6V��*䑎�!�qY����6���'�ժ"��m;�;4O���Do�����娲�����;�e�-e.%�Z;ѻ�D:z�2c��
�b��g�ȥ`fd�sH/κ����M8��{?�,T�[���`��mo�����q3v���B\5��R�}�=P��\�ac#h޶�Z���EZj�V������'m�Lf.���eW�3祍���lN�Ƈ��U��g&=�p'�.#�8�PK'���CK�b��҆/EdK�uvy��욒@$����
?<���5�L�������f�&:�į�з��`z�����-�K��*���,L�z�8��=h��C��-��F,��	��]T�j@�=�CPT�� �O'1?~)���K)G����d��\��0C�@�I"*yX����9�.#�X��l�z�|��ճӫ�&�K��x^hAJy$�~}��^��_t��)%��C�,��I&|ϭ0�,`b��oe�#�
����͢��e��,_dA�p���gD��G��kZ�j��B$�m���L�d̳�.�Δ��ATr
�,�0y���l��o\t����8�3gA�”��P�2,@�1�������2gg_���>utQ�� �  � �"3�.�_�~��+��ݽ;��F���TѮ�u��O�1s���BQhjC�֒���!Td�O_D�o�q	�	�*��)B<Tܵ!��6
í)�d��}=9�XX������W3��;�_�v>�[H]���qa�2PT
�֭��U(dӘ�t�hQ)��yn�V��k"2I$�'�y��h���:)(`��IU�^-��GG�������Vl���ܞ	��b���!6{v�uM�JD��
����H�v�y�/m��	db����P:�����V"���"�0S���(K�]sN�����]p��2��$pȹ8����[�)��!9��l�D>�DSgo�����"�0�b}���h�߇��A�̪Y��Ė,7UBK�}b[�~Z�7��ܗT
]p�ۍ�\Ҳn��إt����9j��mغ�6�vV}hh�!�����Ƙ�����c�,s��5��%��񴘿;�c ��ް�h�x�oG�k����>S+�Ҹ�ZI(�&cH�\*���jl��-���������'����S,-��@�~[=J�|�(P4��C(*�:�>0s��&˜/Y9K֞�g���>����96&��S*���-��2H/�����7�e^��h��U@j~�T�t���9g��R�2,�i>=w���x�i��]Wi?�.ʬB�����
sc@E��媿�`L����)�?s�x��
�C��Z�l�e����S r6�0#�ǹ#?G�aD�����<^� H(���l�g�10?'��|�4�`NI��6J8�� G�!6����7F����5G���"57��7��I�d
�$�a:luY:K2��k��e��f�O�4��]�p��2�:*��s?�2;j[�/(P&Z���j�B�)���6�l�7�-.>��ө�قm�*��YK�S?e��%Y��,?������w�8s�v]�/�cMk������A�?P�
�h�t
`�޷�rL
{C���|&�����WS��;�̳���鷅���sY:d����R���zT>I%R^�PT��ڰ���#��{��,�.�2�_�e�;�������`��$L}�B-V����l�p�G[|M��R��EKL�=���eCEA;�K�Cڅ<�yi���y̷|������w�-��>����ٶ��nh�n%*��#�8����_�n�V�m�H�Oab�0&^3�G����z�H���$���~�ov���ޫ��,c�XV۱�%"�"���A,%
�43�����h#
���֍�e"�x�'_ƥ��|�L1�*��!�xBQ���:	зm�i��9�5������E��t�w�1EQft���X�ȥ�M�#13i�Pu��7|��U�"1s	S'�bb�b����]�E��� �N��v����y�Է&��hC"~�g>���/�#����[��ѾtD�#Ժ
M���tӍ�騊⑺�����E,N�!�8�M\n?��P�{�ǾU��9И���yk�y�|/(a;˭5������ ����yĦ. ���ρ񆷣���,a[�)$f'17�:&O���S/c~�,r����$�`p\J��y��K5U�F<�<*�L�_�B���S[�	�A���/�V��`��=>��BQ�9U���d�l33�A.�D.Yܖ�K'`fӰ-��릆,%��=�����U�\��>3����L�E�<�
�NG����
����e�m%�%!�%�m�mR�L1s1�t��e8.A_MC�|��չ:�d���##
R�f�f��@���!5vO��`�������W�m����GFt#ު�?'�_�]���0`C�,?g���|�_�ϞH`�V�ZX!�F>o�|�&�!�^wO�����S��y��:+���Ql!�!�9b�-[U��F��@�B�d{�EOe|����m�霦�m"��.�ф\��gDÎ��!=�UžD��B��lb@2s��K��[@>gI��<�~���_�e6��W1�`د���E�;H�w�E@3Dk#,���8ό��-Ud��iIDAT_�M�����Č?���zpy5#
�O��z=�t�
�͚�nb��C���a�
��FRf0�a3�8 �,����rԲ�YSզ0+�l��#l$��xƱ��QIEND�B`�dist/images/free-otp-logo.png000064400000020574150755130600012153 0ustar00�PNG


IHDR\-O��zTXtRaw profile type exifxڭ�i�9����s����`�?�)UI�]Se6��L�F��oAН������Cs�X��V�W����?_��>��﫬�k���]��"O%���m|�<_�xÏk����}_��{�����+��z��<H����C����σڛ�y��{��=�
���G�w�<a��.\(�xRH��1}G�>?C��;%R�qL�}�����ez?�z���e�<r�����ŏ�{D�m-�w�x�o_������?_8�Q����%�}:ߟ{w��|f7reE�Qo�Ï�p�d��{[��)<���n~�Eɷ_~�B�K_r�a����b�9�h��qQ=ג�W�ԉ�p���vj�l��YN��X»n�[�q�84Nx��v��?�v��C�bR��)p��*��EA�֭���-����*,o�~~N1K�[��9q\�暈�{��%�k�5�j����:6
4yL9N*J��AƜR��b��6��%֨��&
QRMFmz+�~,704J*��R���J/���k��Z�
K��X5�f�FK-��j��Zo�Ǟ��k��z�cD7���\����8�̳�:m���X�g�UV]���k�ӆ&vݶ��{��Lq�)�;��3.X���[n�v��w��ڷ�����U��R:�~V�g�ُS�IQͨX́��*��j�[�9�r��(�A���Q�|B,7�����[us������rN������t�Z�S�-�[�b�.Ԛ�D���s�FaU+p��ܹ��Z]�t���+m&�u8i�?a���a/3��\�n��6�����8���X����
+["���SV~e�CG��j�y��v̛7��g���;X�u���e�q�=�'�1�:��vB;�N?ƶ3ݴ|YL�
������Z���[�]���/���>�J��5�����Vr��Pg�k�6�.}���S�<,p�����J�� �5{	g���)(?Fg�}J��>@+��������~�c�s=��R=�AՑWӕniZ��y��A���(���l��t����q�Sr7���9��{�AIff�rn�g�M�2&-v��\�B�b��uWJ�Ofl�蜰*T�Iv�}P[��eB���햁a�0Vcv[�Z��h�1�<k�b3�tF��A���SG5A�8�i���(:q�8���}ћ��|�����P�z��*e�P�)�pm+�yc�W����ӥ��}'�q�h�͸w�l�О�YV�
��p"�4(�L����!?������ˁ��H�"���SŘ6��ބ��5�z���x*�S�i�����X�Bd���=��Y\I̐�(g�0���(�b!h��v+��X��u�G3��+���'wQU|I�^H��UӃ�Ǣ3/�vO��[�V�3��-�k^�Wf���N�.��m�x����_���V�<���#��@]/n�,U��f��<.� CK�O��U�<��5H$̒'BR�{,���Y5�o�yW�[))Vp��ǀ�TWdzX�9��5�2�ͻzh�7޵�X���TPT���:�/�,��x*0a+3mtX�[�`4�eСu�G��h`�5�N�)1F�0B:��$j�;�z������w���֥��FY�TiA���H��_,�1[�S��
+�|4K?����F.����1��G�
rF�L����I�����f��ƪ��ُ�*MEɯ�
f�(�=+��A1���P!�'Ky�f�d��'�/��{�]���)�g(�e5Ъ%�E���B+�-��T�MlԞe�c3W9��.h���#?��wf�N�4��ݧ-�?�k�O���Ds�.38�a�����8����eϡ�&�A�x�4�rA6�Jt
��@%�oX��9V
9��E��&+y�g���T�
Vb��p:�Z.����D'�R��
v�UOD�ѡ6��<`�)Y��X6��Q���%T*����͐Yu�˶��y��1�~w��:��,�	�â��t�7zn=i�@�8���U���F�#��d'�`4��=�+=}�W.��%�'�"�Ím�ԲS�v�SN�-���{��c=!G�緦�SRY�8.�@�9�����(أ�9+�TC�R8�ޖ��i:�(��"~�:��h��39�.+(L�?�t�e��@�y�#���$���g��(���?�$V��.�� ���vR�X��uB��k6�h���l��"��b�^��d1 }��y_* �v��P�¢�Vd�q����nB�E�b&�m��gÙ:UFl�$1/k������k�ŀi�l&��ϊAy�)E8Fh(������p�f�/#�� H_��i�]Љ͕����f#䔞�+a
t\'�r��k*D��lk*q���ML�\�5��Yy�������z��ZbC�N�;�́��Y��ǽI4������� ?D2�� ل
p��T�	2F'��h0��b!��
8a��ua�M�� �_ք�ܝ�'uDW�"����@8��
8ޅR>:)	
��:�uY	\%>��=3+���Px�5�5�D���cx8+(�X؂�#2�׉Lz�h�$��`��mrd����&�yĴ]C�
��x�&/5��pT�K4lx	�C�d����¹7n���g��IE�P�9����k��=�z�]G'��|�0��e�I�@k�Y3��!CT�tM9C�^����SC�⒒��$Qq�B�d_�0�;�B1:";a?�(�'�^���
GC����L�$���Q���R2p�h��b�`)cu"ǡg�1�ń�
�[�
3�<�:~!�E��2��M|����Ň���3�6I�3���Lj�hdO�m(�&Uc�����E�4>6�®CZ*˖W��Ӷ2��+l�)�T	x�������p���9Ѥ��ꁜ!��*H:�D�-�P9���
S���	Kѱ�u�f�C�&�Z<?I�X苗�����Dd����@'I
k��Pd���:��IRi�����h�?�k߸����P�p�!�>XqA46�[�捩P��SI��1����x2
c3�#�0M��e�$�ҡ9,6�)k�y,�BZ(	�ghZ�v0�Kb�� 8� uB'�uhK�q̄����]\	^-oҦVBA�&�&Ɋ�Xm2�l6$B����9wMJ�p�%m��+��+��$\�Ez�\<��E�9����jQI���h�fƳ��Fy��ʸ�Yq�xP���	��PI�c���I�-�4Y{��%04�&�b��!lF~?�D@��q��Y�0�l��ū����J�r�(TX0�8�55FEL�r-`޻�&�i�b�Jyb��p�%��6���C��dƩ	���=�rX��4^��&�*.��Ild��6��X�k��S�Z���؈.��К!����$^�d1YYnYؙ.��G�#<W��0�?��E�;!!�ݸ�]}�0C2�+�d��"nr%rQ#�N�$C���4�� j����B'⾦$�y9�&L_;�t�m(���Ud��i�$���D�љ�)�$�.\"v��1?GU~;L'�e�~�$$����oD�EM�~��y	�mc�0�ܺ,&}c�����7��v�sYh(�"��� J�H��A�YU�3���y�b�*XL��6B�fN�Y3��/�eDy��r�0�;#�	.)L^a?��$���mJA�]ٮw��u�Pi;B��w]�^�����߀�b�'/��J�qQ��8�u�3�Wl�FO�s��xj�
��^�<�����J�'�™�y��K�S��1> GVv)��������
�#�@G �|�~Ged8��b�i��l��Et�����CەD��J�F㫿v��{Ԇ/5����%��0NT2��ԛG���r��,|�2I	[�KK�����+��L� �tp�pp;E�;a5��._���5��6PTL�]y���Oo��������GD�k�W����d�w�x�iUtBvS
�A�@�?2ԙ<�C���
��&
,{�t׵�0s"�m
<V���pLd-| GȄIp��LM��p�nY�o�=m�$7������n�	�xi�ͽ6�'4)�O\��䧃6�M����*ڨ*�MA�,\��xj��ߨ�yL��*��!�����ٌ;a쭂�r��(lSG��ڊ�L�H�5���Rj����^�����DB|�j�'��k���C��E�o77�.,X�� ��"�(��[D�0E]�1��J��-]Ү�o��?΂\��h/qKS�u%-c�V��;92�H"kʙg1-���e����0�C������
[��?,?�s�T}ɶ�q��L��H�p4���c�}	(�D@� ?l�z`��������A4�M���l�H��1��-^R�����a��+�s��襊+�'.S "u	E�-q�xѭMW��C�L�#2p���
=�bK8�9��J���K�Ȍnw&L�W�,kG���|H#�����?&��\�
�G���A�����;�i2�"eH,1�E��hhW��St+�j�m�r �D$�'s�
�N�y0E��\���=Y�h��w�T̐y�� 
�Vu�ti[0{�ۃa���(�m,��V��wBs�V�joo�s
����$���H����<��Lm���9��k�J����吩j�d�}�>b4��f'��Oa�?U#�q���	1JtW��kU��Z�
��٘l�7L�g�O*�
.� Ɍ���6�>m���=�R<��A$�T�+J�,��Ll�,\�v`#b"���e�b�"=�U�R/�M")�FG�T8���;��N�q��9(��ͤ� ������v������w°SF�]���u1ȯw+��vXľ���Cg���~[�ȏ@^iڴ����t[��M�A7�]Z��5%�䡠 ��y�!~̗���&&��'�rf�L�j{	�^���,�\kd.~�T�����8rQiT9^[�L�X�4T#ūi=R�Xo�������@3TDw���
���E7&Ɍ`��&�/�Et����8,��?0��b"_'��-!U{��G��}����e�����lV�>�b$�i�<���32�a�-��%-�A���BЈ�k�G��h^�!!~SR|�f��>V�>3�ćڏa����=��ȑ�Y0�D|3�{[�!D���'�=�b��خ���I����x�3�Y���j:�M��B~nK�m����b����l��GE�jH�,�:)����r��pT�D��nV�;B���1X@���B�+;{�#�F�fXJ���f��̇z���:�"u��ڊ�$"Q�=)�>�8�#��h@l*�ņ����u�k�1��6�0�����9�
�FܽrkHg񞫈ݷ>e��n�$Y C���[��5J���!_����'��ɓΩF�ݲ�;��4����B3	�^$��@����o��t���B� i6��C|E7�M�spD#�
R=޿K�vG�o��H{����Lw�O‹h�)	���_��?ڹ��5��W�&ʣa��@畛��a�I�[;`��ʾ�>*GrR�D��b}:Ǐ'Z����h�0��ч�Ob�P��>��>���A���Ec�)I5�H#��J���K�蓨�9���>��H@�'$�>��I�;D�lJ�k]�:i��ˇ�F�	7����\Y�U���iCCPICC profile(�}�=H�@�_SE)U���d�NDE�
E�j�VL.��&
I����Zp�c���⬫�� ~�89:)�H��K
-b=8�ǻ{��w�P-2�j4�6���J���`��E��,cN��h9�����]�g�>���R3|"�,3L�x�xz�68��X^V�ω�L� �#���8�\xf�L&�C�b���&fyS#�"��N�B�c��g�Xf�{�3��2�i!�E,A�eP���:)�m�t��r�ȱ�4Ȯ�~wke''��`hq���c�U��qj'�����R��$����G@�6pq�Д=�rx2dSv%?M!�����@�-X�z����HRW����Q�z�ww6���z?R�r����.PLTE!!!!��c�k��	�������"���
��s��{�!��� �)))"������������(���r�
��b�������	��))3����{�k�W�J�����'0C�l����333��}��C���;�� IJ+:0��3�|��|�Y��B��s�x�#��[�{{{�����0���{�#��=��h��������c����o�v�5��3i�aiLYQQQ;I�+��=����[�>�{�H�p��\�d����4���r���!��0��4x�!�� v�"noYYY3�� r�U�c��O��9��"��y�PYpOkAY-���;�+�����k�@��B���#��Y�HtfffJJJ00,��#��*q�_���/B;=X���D��9���*y�$��g�==="����f�7�bKGD�H	pHYs��tIME�
 �2 �tIDATXÝՇ[I��!�Bb\!lʂ��Z(	%bU8���t��GSO�.�{��#��Գ<!�';�3����d"�Es$.999y%��r�HN\E�m��J0�\��A�XrQ�ø�"q������|�|�	h�ah����R'�	�\{(Zģ�B����w��1�J��������WP��$+=�WTVN��OV����?���g�WM�O�d�S���u�|��,�*t�D�T�LV5�1�Y�:?�t�N$�k4Ya�:}�}�*="���~�d
�d�^]=�5Y]�Jz�$�zE����p�'���L�tvu6��WeN�Q$�}���L�]M.� ;;\�����*��dq��3�qM.^$Z �z�c�Y�I�z�Z=>�aw\-�A�q�~�Ӈiج�Λ�Ǚ_�|��3g��E�c*����x�F�9F���� ���pB�#	<�ݻz�ųkqw��;�1�h��ٮ��O��\u�M�a�Z�V0��g����{z�O���V��Kt�K-����u����!��~��=�רE\x��v!�S�0;�DZyBq��z������_y�R���]z�����嶔�}���;w�<�P��8�zq�+�9��{����p�Gޭ-/�X�}���ʈ��+#,{�r�Ngm��{����4�+\�	x����O���Y�����ʻ�Ɩ�e��{ �� |p�U���s����pJ-5s�բ�̉k��K��^�kĸ��f{���Q�/l�m�!���m�ǰ��Yvu�����y3��T*y9�r1V���ՠ^��rV''8�;��|�,���w�x���Н�Ap����;�}x$����)���Yõ�:kM�U'W��S␯��]C�#v����v��С��;0�ˑ�H���R��)W����Rr���[}4C�,�~�awZY�Pl�-x�[o�0�[��+*IKMK+8u�~�*��˗�#U9�U�{��u�ch��p�2�������K�c��YO�H'=���Z�3���
�rѠQ.(D���!"�#������.���>�����?�)���PC<ӴRIT�)��֦�b<5��s��9T
�W�\�k��[�����kk�kۇ��MK�+��Hj��b��G\���8̮b�m}�{�����у���у
�8�"��g�-fDR��-��֊8ދC�x�d`��[m/l>-U���\�Q{��9��»'�t&���…���!� �eaܖ�׏5`z���k�I"��d"��FC٢̀ck.]�54�

����ye�t�37��FC��n��h�8�p=#�1�'6B8��Љ����#��m�o��^4vo�u�7�a�h4�]ӱ"NQ�`��N��������fY �r�#�Hh�SZ�N<����<yr)p��l���RY���"D�~8���h^�X����ysi	:-�[���Z�N/>��h��-��������G7�s���)�`<e�8��Fn��=��Hf�Q<�������.�CHb��p��8���.�l��`��h%�p̀K���f|��l��8Mk�	���h.����/��Fp�Nb�'��n�>z$5����ˢ�?c�6����-��^ۃuIEND�B`�dist/images/wizard-logo.png000064400000033712150755130600011730 0ustar00�PNG


IHDR4Ir���zTXtRaw profile type exifxڥ��u�<���#�
ބ{�f0��s��mi���[%�X$�k����=���.ZS���l��-6����ߟ���~��z�}=nb{��9t��gͯ�ߎ��</��ҧ�zc|}������n4"�/�u���P���u��L��V��)����>����F?B��~���c!z+q0x����!�gA��	�7?}���x휔�����E���P�'}���?g��7�=[ѿN	߂��_<n\�9+7����7��x�.?#�}�?g�s��,z̄:�&�6���
n�[W�в-�O\����w��'Y[v��t�y�u\t�uwܾ��M��6��Ӈ{��⛟A���vǗ��
�,Λ���Xܽm��ܻU��z��T�m�������S��{�a(s��idĝWP�
���/�5���(�E��%FrHn�'&^�vqe�.@��ub0tFtdͅ䲳����$�3t�d�����1�Ln�$n�G�����9l8���r(䦅N�bL�O���)��Rʩ��Z�9�Sιd�b/�DSRɥ�ZZ�5�XS͵�Z[�ͷh��[i���;��\����	�?ˆ#��Gu��'�3�L3�2�l�/��?V^e����)�w�y�]w�Pj'�O:��SO;�=k�ն߿�"k�53��{�8Z��%��$)g$̛��xQ
(h����b�ʜrf��y����12��鸷��dT����fJ��7��f�(u��?�S֖hhތ=]���@�U�?s�s��ꚏ��>�W��,�	g��hr��YK��p�~Z��ޥ=Fښ\���Lm��]�㥟�R؋\�W���F���6�ͧN�����s��'��{�fu�!���:����u����3[11W�($�xw�4y%�]�(|�l7��?K��Cy3��[��D&���<��s7��5,�����.�_�0�?����/a�2{����|n���?�f*v��=<qZ*�����w*�N]�%��V6��K���ɗ��)��	d�k{��cY\���f �.��=b����b�t��&��xc����Vt:��uר��1�����h��+��r��>��O���e�wT��b!+��� �9�8�j}�������+Cw�}L�"n g�3���c�+�H�I!P6s�~,jbu2�A����疐5�
J����{�i�(�̹R���}�^#�yf�zC�'2�� ��s��Iݴ5v����Б1��msn��`ܺ��5��$��4K���'I��ҽ���>���H
����Q�yؓ�Y�Q�e��}��#�ב�?u�YF�.��(c]y&A���h��{'����$�
�W{�}h4g�S�H��A�\�9��ߎj�٦ig�s�X�ߗ[1#�~���3L!�����{�>�t|l���H�&���m�?��ܧ��<J��^�N�r,�
to�@Q��
�W��E��!&nX��u0_��b�������q�"�	�:W5�\:v��J}���Nc��tSEs�:�#3��Ҟ伯��D��w1}��ji$�҅�!E���>0WC-h�7wh�pr�0I8�;��V�s������G^�LJ��^�?�o�K�Se��R	/Iَt���ɉ̱�E�r���f3��ܒᑧr�L��}�6N���5f �w<�@�����h[��v*�2]V�r�T��7	�O��n-:��O��=@l���V*��D�b	�5eGoQ{��%oBB�s���[�h/����� #�h�y�2�Y�����r1�.��F5dc�FO�7,%���n�h#*<Rּ����H��Z�]��d"n�L�4jZS�g��5("J	i�"U�1y���Kþu�+��-��}���������
|����P��
�=���A�1�F[Ѭ�Q�<<5�@�x��1)r��/
��Gau����40)ζ��*��!�c�4$�҃��2�AFG��0����d���,T���<-�se�0h�,�<�B�3��F�(��~����U�7����@I���E11�����߈��bA�e����l���l?KO�q:P�8��tWp�벘��(Tj�G�� _ZTqmW<���ǵ3�]�5�:�c�b�F%
H�-�h��s��2v����w'�AAn�|�hS;	3�`�<a-�R�sxj��0�ʦ,���4�g'%�j�U5�	�t/�}hM�d
I��F^i�Y���4S�AK�F �4Vátp# 6z����|bVD��k�,g
��Θb˒�|�^�x*e=3���z��K��(�<�����Čda�	<��$�C�_���;w�D�J�Z�Cwc�ۦ�j�DĞʢκOԑ�\���]�9d�J�`�&�[�)x�_�����1u�w��ݛ�yr�6*�c@�}�21a�͆t/!��DC�)�c�D�6���Fe4ۀ�!��z�ǜF�Qג�	:	H\	�Xn�M�H���/"����
��Xw3��>��
7�*5��&���=|E�ԉ�/��{b�����ɀ��d
��ێ
��8=W+�%���X,Lz��HjA;	V�,�+�l$ϔ̠���ߴ#g�eԋ����ll��m�HL��^����Te�;�DžL��	GE����Mx�O����d�Ė)6up܎��!���Г�Ơʁaf�e,���A���*F
�O�&B\@�g��	֪7p�X�@�l*�_~2��"t{�X���u�RC�9ƏZ#0S2�3J����n��������
���t�1}Bt^թ�಄;�W1�E9*-Qz��8�QȔ��C`�n3eo.�K�N/��)��y��.!�fG�ϧ@�f���i2���/-Ds��u�dn�o4[j3�?�E�
s�
�+�\�`�3�g07��pΞF��ZS��x���P
��QE�䞖F
��2�;�h�P@HG���d����z@��c“�'“zȓY@3��^N�b%�	2MS��bKnB34�x}���n<J�fR%��¤�(\�+3�z�F����>@���I��I/�*]�&���E
�#�\���?��]�Ĵ�p �|L��<��X����	�Qi�fT.�>b�k^��� $�����k_d\���B��'�9(w"�R6H�Q��U�����㡛��G0������3W�i)�j;��;i0�ҧ?�ا�mIQ(�\O���~F�u
�
E�\����)� -�l�k:D͓��
<�ƦL�pYZS�V�}H�\,Qwx��&&���I;A��;3��
�3���,.�q�Q��uգ�3>�U��������B����'���/�q�/��$˄_F��C��
�����
hMC�!�����V/h?,CR�Vx�L+t�N��D��d1��(�@܆;�
��w�z��J�0��{�'U�뇆�����&^��3(<m�Q���B�&[�:t�t��F`}��f�dd�N�fi��Zz-�9�1{d�� 2�á�6qꌹɱ���o����I֦ sA��D
h���X6��نD�L�S�A�����[���ɾ�_�[ |�c����(�kVƽ�?���R��v��Ӊ
$������|��$x����Cx�HX�Xf[�C�]g��h���Q`d���P�p0k@�NEm�����R35�.m+�����ǔ���P�םhi�mKY*�Z���,�/z
�j�yj��cf�����"�:��b��b�VP��F�؜� ��63���X�3�겡I�6L��!Lv��F��5�!!�A����"9���5�A�	�
{W�i,��o��ШН���A\��U1��g3�[�/���ڐ@9Z����wA(Mm�B-��גd�eA��j
�-ZRW�
�`��v�ay��G&Q�EQ�pw
�Vn5�����E~�x�
��ð�\��P4�t&��Z<�-����J!�#�c*WIG�i�*>�2�?�Um>�5(��ZT�@�Wya�x�!L��+M��BZ�f�D	� �.oPtB��J@B+KP�{�)z-�h��f��&��9�(((�7�ћ���GΖl�q$^��3�M_�]���l�rEO���q$��[�#|��Z���w���t��݉4�f4s!f��&dEFX#��T���I%��T��9m�J��%7�:'Z��8h�&9��Z�}˕2f� N*ỽT5ޤT6<bY�#Jom�����憵@����g�����g��]K���v?#WA��JĔM�_�rk�
N�`s�Ž�=�g�a[D���ob¼�	�-�a:r��^0��,�L�״���UAX�Hq�| Îf�}ɼC���n��8=��X���������1љ
N��3`{j/_7��h%5���"�k`v�G�j�l2� ��(`��o�3P�������|d��q�=P�>@�b�b^ ��40"�2k�+
�W,������x�4}+ST�!��c������`n�v�N�6�j���a�a4o��܇9�v�
]��CEu��0T�PI��X�Bt�Q�]��~�$e�+�zl,�"�`�|���L��gk��L��/r�D��@qaK��R�]�DH��g���OoЇ�(q�/�-��_����T�q���(H�yb��t���N�~����pq�z�0I�A�c�?�[����wC�E(:pGQh^5��@�˫ӣ�6K��8H��&#Nl.�ཥ�H�ֳ�)H\����+��=n(�
3.R�ik<�5�(�'P0�ER��ʸ��(b$[�М�jޝ7� Гҭ
U;�|�P�n��w�ˆ���ԪZ2�J��5�$�+P���P���h��p�=4���4Wi`�pZ��w=�W�\�~��=�6&��Zq�Z�!8HU8(��
�.��A�v��-s!hɅ�G�b�]�[���t7h6�EJ���~���S/�*�s��Gk��HdMՒ��u�3�L9=M-��nr9���Ҫ��-g'���i�6iZ��R����0�5��Dh�ҏǩ3N�8�6ν��5�JJ��t`�e@�v�	����:�aw�c%zR� �%�f��=�Tp]�~�9X�H:���+�
-�
��=@��0Fx7��k��.oÊ0
�W��^RaJ����k��T`�c�U�`�(���nB鵡R�怬�8��sD��|�n��2����(b�iZIBcm�]n�bq`\�%�J�8wkr0��$�h=�~Z��������a!J�k�|���e��2�;k��6��rAO���W�=�m�#,V�ߵ��I"i�xV��u&�}״+L*	�h�l�B�z����`�Lwx[t�Wr~��~�i�"ҕ~D-j!�a~Р4Xʘ�����vٞañ��S%�L[=T��1 �({9��^0�q���xz�|m���
���܆�n���@$M��r�i�k].G=�
DJ_��ۥ��ӑ��s���gDҵ]|j0�z;\�+=pg�"��J�8����Ϣg�N�z�P� {Mb��!-���g����]�!]�����ѾvBïHC�vB�5pn�#g-_�$�'��z�tF�=�4�2�[J��й�I��6�E�HC���hKk��R�P�6_$��C�kg[�xR�3M=ӵ���{�����Q}ft,�x�"U�}e��t�3ȡ�<��e���|�'�滃F���k�<f=x!�[�Ƕ�ze����zx����@�^m�d����T�訇�:�NEF����|,� �\���ֳy�����A�a�	PD`��� dco�GRq-mX�ۡ,N��|L�dq��3J�۸M�:nO�k�j=j���6s�M�y�^�1NGϭH�=�Ej�䰜�A.l�
r�Au�y]Q�{�ߞa۾Hl�:�O����U�Ҕ�4�3]f�".T���,I;�e���l5��M�^���~�iCCPICC profilex�}�=H�@�_[��;�tH�:Yq�*�B�Zu0���4$).��k���Ū������ �����"%�/)������=���F��f�8�j��N&�lnU���ETb�>'�)x��{��z�Y���J�d�O �e�aoOoZ:�}�0+I
�9�A$~�����~�62�y�0�P�`��Y�P���c��Q�?��y��Z���=�Cyme��4#HbK!@F
eT`!N�F��4�'<�Î_$�L�29P�
���n��䄛J�/��1w�fݶ��m�y��+��6��O��m-vl�mM�.w��']2$G
��
��}S�z���Z�8}2�U�88F��������=����Gr��鄑�iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 4.4.0-Exiv2">
 <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
  <rdf:Description rdf:about=""
    xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/"
    xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#"
    xmlns:dc="http://purl.org/dc/elements/1.1/"
    xmlns:GIMP="http://www.gimp.org/xmp/"
    xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/"
    xmlns:tiff="http://ns.adobe.com/tiff/1.0/"
    xmlns:xmp="http://ns.adobe.com/xap/1.0/"
   xmpMM:DocumentID="adobe:docid:photoshop:3ceeab88-a437-d24f-96ce-23cca26f3140"
   xmpMM:InstanceID="xmp.iid:51f1536c-b19f-47c4-b8c8-f243a5bf97ba"
   xmpMM:OriginalDocumentID="xmp.did:4dee663e-d1da-41f6-8f13-436971561c7c"
   dc:format="image/png"
   GIMP:API="2.0"
   GIMP:Platform="Mac OS"
   GIMP:TimeStamp="1701174925467591"
   GIMP:Version="2.10.32"
   photoshop:ColorMode="3"
   photoshop:ICCProfile="sRGB IEC61966-2.1"
   tiff:Orientation="1"
   xmp:CreateDate="2021-04-25T18:14:10+03:00"
   xmp:CreatorTool="GIMP 2.10"
   xmp:MetadataDate="2023:11:28T12:35:25+00:00"
   xmp:ModifyDate="2023:11:28T12:35:25+00:00">
   <xmpMM:History>
    <rdf:Seq>
     <rdf:li
      stEvt:action="created"
      stEvt:instanceID="xmp.iid:4dee663e-d1da-41f6-8f13-436971561c7c"
      stEvt:softwareAgent="Adobe Photoshop CC (Macintosh)"
      stEvt:when="2021-04-25T18:14:10+03:00"/>
     <rdf:li
      stEvt:action="saved"
      stEvt:changed="/"
      stEvt:instanceID="xmp.iid:06deb711-136c-4688-ad23-5e49a711a782"
      stEvt:softwareAgent="Adobe Photoshop CC (Macintosh)"
      stEvt:when="2021-07-08T07:54:41+03:00"/>
     <rdf:li
      stEvt:action="saved"
      stEvt:changed="/"
      stEvt:instanceID="xmp.iid:19ea8222-82e9-4d43-9882-9bd664e12774"
      stEvt:softwareAgent="Gimp 2.10 (Mac OS)"
      stEvt:when="2023-11-28T12:35:25+00:00"/>
    </rdf:Seq>
   </xmpMM:History>
  </rdf:Description>
 </rdf:RDF>
</x:xmpmeta>
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                           
<?xpacket end="w"?>w���bKGD�������	pHYs��tIME�#yR~i
�IDATh��itU��ﭭ��Ξt 8�Ѐ�Ü7�3����1,#�$��2<g%�̀�F$�3>	"�m��@$d!$�BHzIw�ު����}Lx�@��,���>uN}����W���^��d7BZ��B�a�
��������ʝ���Q�Σk���#�|^o��\
��A�u"��c�x�+�Z?�Y]}nwN�,�>�ǃ����S�a�����EYYn1�����!̳�v�9��رc���b�A=D0��o3���N�	�N�{�0~����-���>��t�k���ދz`|>���X�-KK;�	�C��O�*����r��x�-�Z�6��/�m_����(����aA@<�~�a��{;5��H`<p2=,2z������,�]@ć�  ��s��j]{b۶R�0?�.7$��D�{�Q�4� z��p�4#x��ӎ����^��cf�LJ���
ՋB9���IuKh���wN�����Nm}�.X�a+
��1�YlB�T�"(Z��wg-@b��9�Ͷ��sߊ���Ѝc���/�U�W	��<�<��n6����DQVV�0�y+�:=3^���Vi�4�*�fjHX�GP�BȈ��v�)��ر��,�+=c.��09N������f4��s���e��ug�,b�o0B�?ϲ�L��������
��K$<33A�����^
�A	��s�[�^-̟<��={j�2IU�'	�����qf�w22�[$L�n����1�+�>T�"zь�s�J����`4V7�˫/�:�G��	�ks�qVk��iiE���I�ظF��M0�.X��B�v� x8��K�ko4V|�q#�i�Ur�v]ˆH��L�SS!�{<߷[,����Ė3���x�,֐K���#	�S��1��>�ڵ
�;���k}�z�P��UZ�\����(A��͖�xζ[�����9�E�|V�(�3,dԚl��D�R"qǿ�rN�W溺¢y��݄���w}�]\\n����ik;��;;��ۭւ����YÉ��R�,>q!��dBL�L��v����f��?�킋��z�7���6��L����
��%�$t47o�r��m�g{��@����zXF�,:n���&h&�Ы�[@G��rb�����Xs?��U������*�ݾ�b9�t� &��P9$=&,"z�ҼFJ�"BD�j��Pa!0CNk,c?=r��
s�#6�'N��u8`sM��R�IC��Q1y]�|R"Յ>��=}H��Ht���b�G)U�Go�,�^�3HF'E���It�.�~������J3Y�Z�<aD�V�R�q{����Ŀ%�Ed���~��
v�H2�Qj&�ED��f>:1�p{B��?l��'��h³�&MQ@���$Z���D�R�9�Gw�H��a�L����2j�����י$)�V(!!A���۲�S�Pd<pb�46!�Q��Z��G�0�;�J�x����	���4�O��d~c�KSd��E�J�*A3�*�˹���v͢M�v(���I�p�j�д�7&�u4�?��HL^Ĩ4/�S�ʷ�|��,�!��
����;}j"��}�����}�jQv�y*�p�0$'Jc���&�`��
^��m�m��[��v� ���-'h:�7���A�*�1,2f�"y���%�u��w����ilB���`$ba��v�v��e�����\��^��l)D>S�>���R��q����p��Y�:=� �O\�	�S�8��٬E�ݺIp����_���7�:��~��o�oF~�)d�	EJh�z�,*v�PO�iow��I3fTi#V0�l�f�˜y��������IK�o���!s�Ǹ|��6�՟��-[Q�o����}��@����'��+�IC'_�
 ��-MQ$YΨ43	�c$p���7ow7�(*�=�z�++̰X��η�v �ߊ1�
*��6$I	�TM���'l�Z��IP�
4n�x�&�n��Qw��:/�z?��5K�>G����~�v/�oo�Ef;HR�T�@(�:}�PN�mx3k��-�Z/��Q�2�tA���,��%�&��I�)}�P7(�V�~0�$E�p��j�l֏Z˿���U��"-����7��\;qW �h-u��h_�]
^��C�+ŗW����L�wyz�2&B���w��>۾
��jଦ���T\�;KL�����L��
�)Z6��ֱB0�S0��m`M����{�.W���ۢ��xZ�N\B�ґ���'�]���a��5�M779�k��6k��u+��.b���Ju�ȧ�j�[$#u/��W����nnn���]��
�8�)����d��#�[Ei*#$@��D��8���4mq���v���,q���ǞV���"iو�5����0F(�z������`�\gm�ꬫ�ui�<Q0�	k�C�~N��XM���t���r!A�k�Vۆ�9��m��p��
&^�&g1mo�pnW��9-�4�x�26�����e$-M��yFB �vVrv[��Ƶ��n�Y�O�m����`�zu��6�V�vۙ�;~X�P��Df�(cS��\����I3A����F(�w:�s͖絺T���n��������R�+� �
�nW#�l��~�b{�{o��C�-�&S'.��( ه������q�k6�s�X��Ņ�n�V��f-������]�Qw��r��v�=7�U�k�a&̽�L��$�&���U����57~s��컾�*˞��͇�V�~��"���̷4�miރ��(3����0��4�&y1IKG�s[����|����>W������udU�=�H��0B`���(g�ym���U-|%��7f)T�+��㞑i��v„\���\�X����s�
j�V��tn��}%��!�/2������gc�.T�R��ȑS嚤�(F>�����jU>�Y$�U�/y�|��Cc�~,HdQ
�>��:i9�(F�s��|�C���i�X���+��8�����$-K�f����J�J�1���+i�_E<�@)����MyA�MYB�Ҵ��*P����ѩOO��
o��4c �AҧekFe͐�R�lx_B����6n�,�&9�����&͇(�g�5�C�}�\��&IK�$̀���c��t:�.�E�&y	Iˆ�Pf@�ҟ�	Y�N�M�!S�R�"0(���L�8K�6,�$����_nӫ#3���!�(F�4�0����v�.�8W�M�!iY�`��P�c��&nl�2<5[�6䐴4q��O���H?��
�G�
S�<BRa�㖟. ��D�ض3�a��XE���	�!�п&�@Pa��0$�C����X�	$��	��1���9F�9y��ӳ����<�O{@��W����s�$��IEND�B`�dist/images/wp-2fa-white-icon.png000064400000251060150755130600012630 0ustar00�PNG


IHDR����	pHYs���iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c140 79.160451, 2017/05/06-01:08:21        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmp:CreatorTool="Adobe Photoshop CC (Macintosh)" xmp:CreateDate="2021-04-25T18:15:42+03:00" xmp:ModifyDate="2021-05-13T09:36:28+03:00" xmp:MetadataDate="2021-05-13T09:36:28+03:00" dc:format="image/png" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1" xmpMM:InstanceID="xmp.iid:9df56bce-6859-4299-a42b-c132448c375e" xmpMM:DocumentID="adobe:docid:photoshop:ea873bab-ed7f-6d4a-96dd-a6cd95919930" xmpMM:OriginalDocumentID="xmp.did:1d3f1100-b650-4960-8fae-1fe36ec05638"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:1d3f1100-b650-4960-8fae-1fe36ec05638" stEvt:when="2021-04-25T18:15:42+03:00" stEvt:softwareAgent="Adobe Photoshop CC (Macintosh)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:9df56bce-6859-4299-a42b-c132448c375e" stEvt:when="2021-05-13T09:36:28+03:00" stEvt:softwareAgent="Adobe Photoshop CC (Macintosh)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>jD}K�IDATx���Yz�8�Ѡ��W�9se�VF?��2$���lI� BՌ���k��1��C�mQt�
�lDZ���9��
�b���}�}^1�_�=H� B�"�X�{2fR�s�U=-��s��J;sE|Fۣ�D��Z䁻} �J!暬v;Z���^�����K��8����-�@�la��2�C�k��d׍���=�� v��-���e4c�gw��<�,���r�-iܚQ+��U������U�F3JZ��r3��Z^dK)�߽�|���D��up8�kw��ے���ʁk��f��^��Q��[~��M�M%�*���Rk�[�F&��F�X��\/s
ڒ�3J�p���:��Z'�2��Cɟi�Fl�#�������2p�x]�
�e�;��E�<xg���Fl�x�-�v]M�%�.�%%����k&;�T�`��r��up��A�km2����{X��ϭ������<\f��z�i|�H�34y���"��|N٠݀+LD,��?��"3x��xC`�>��.[��u����T�x�D�:<]WՆzZ���q��֙��q�Bx�@����$uu���^�}ڼ��"���K�����Kw�o�R�f�U�6��_��Ons`��tx�H\��3����0��U�,�����W�u3%�-�w�w�Fe0D���Y�b��H!n(9-Gb���l�s�'MՃ���
�q�'hDGy��"����R��~cG�2�����Xfi��m��RXB�ai�.Ym�"�B4���Pi�V�����VX�|p�ϵ��e���#p��{��+k4N;�k>Q{7Hk�l9��]�=:8k.��E�z�la�n�ڮ����^Z�vR�h6�=�[t^��W;RE�!�8�_-n�AW�gFj��n_��d��B�������=U[�~���wy�
�2�������@���w�͢��?5�`���R��;���ֈ�D`gJV��/�h��0�WJ���-��0��1�'�#��`-=������ x7HK�dș�F��5nd3�fx\h�;�L��(�3�<���9E٭����Y�"�"ԝ����)~Fp�uO�O�b�č����(�������d�x������϶��&�R<nQ���^�;�-���<������4D�ǁ�dm+�=]�
��O��Y�k��>Z܅�Xy,�7���X�]�h�Z�Zyq$U�h!���q;g��W'�ht���+B4���I�S�	��<�b�����Z�����HD6���ֿ{4p�Y��[�v�'E�<��Ѹ��t�=ܵ��E
��L
�D����m)>������'��mia��nKMwd�t�%ˀ=��
�A��t`���y�v�(�1���}0���{�fS2��8d�!kE�N�"O�|�m7�;3F����|u�����=��V�N���sjG��&�CD���JDEI�����y������1���1��ڭ\�ap���D�q'��ӵ�S���ظ3�ȁQK�����S8"0�k>�=�]�0�4h�����ٺ.���=wr��c�\˻�[��\�ܿ�lŕ�~�w2�+)��ZYⲧ%nL�^���2�=û����x��M?g(�6M�>s[�&��iQ����=��w�ku'Rn�mt/�^"����:�[�_)D�
��k����N�`J-�#?�ȁ˹���^��ȯ��A���o�����ڏ�X���V��Z��v�Jh"#�׋}W��;�kVB��-��E�?9��I#���ƒC͟�J��7qpdkr��
�Q��J�B�ͩܚH!^kY�y���:�T�6B�W�ox�����>֏©k}'�M���ލ<o�A����\������j��\<��~Y�9�b[!�#~��o�D�D�FI�˛��0���o���胷��}�p#��n�)�ո��t����}��^M��\���ȸ�����Iw�����S�i�V� b${�:%ۛ1
�Tm{�"wd�[��RUK!Db��R�[�~~��E`���Z��T���g�WQ��9�Ʌ��}�3\٣ooW�3�KN�9E�RZ��ra�W���Ҁ����;Zh�����Y��U�[��g��EɅ�1� Û�Һ����n	�E��x��V��$;�X�p����y�NϢLO�o���+n�D.D�DiD�cT�7���v������!
[�~Vڱd%��P�8Lk�X��V\��7�I#,0�����A�b
!?�h���{[^/|���&��o�n��p򟯾z������t]w�ğ�j���5��)�ӈ���켤"uv���w�K-��s��`��Z�ع(r3�����E�޺f�=�]JO�n�ܼ�1~�
�As�g��+��j�v{���F�n� R �H#��{�n�o�2��R����"�UAn���Y"0�Ѹ�)9�/��.��f�F�e�+�HJjqX.�-�H!�¶x��"�#�Q8�+X��D��S����-����J��*Q�Uq�w�V׿��i*�`0��U�-/���v�1�E��<�j�(��p�_�}E*����DSn��hY5�1���DFۼ^�9���E
��w�֚tx˺�ߗI�k[�
�X�\��;+����I
q�=Z���Z^��s�l�p�	�˃��a4���j�ɭS���3�H���C���:4a�hG���	�F_�8u>�1x[R�X�(|��z�5x[wXk�{��oU��7Cǖ�=D_���
DĚ�W�W�v��@�����^���r�	�I��Û�����h������d��S�R�h����Z�1��䲞"q��ЎK�_�/Y��3�Ѹƀ�}E
`�;ލ6h7`�� �ȵ���+b$.��`S�T|X����Ek��c%ڲ�A�]�/�[\�2Ӑ5�X��@���
N��+b;���\}ǯ
ٳH-�?���XF߸͏#�j٦�]��n��gT�W}i޳l�V+q
�F_��Z�>�5E�"N�H�A\��ޚ�W�M��P�g�Vs��2^G�d�>�K�\~Fu�g�Z��!œ��ڸh�ZƎz'�5q�е�i�뛻��j��v���e=E������&nK�d��3��^�1�v���jun����Z���ʛ���=��Չz��Ư�J�;���En����q��[�M��B�k��Í�\m;����-��>j�ܒ?s)J�]
���Z�oD6������:g�\h�W#�,��6��ֿw9xZ���V#p�<���
O)K��b�
`�؃�b)�Ə�-��Eȕ+/�~�-�o�^�:��wJS<�J]�?z��h�l����^�2�[G\������@V<E�׾�~��@��q�\�_�i
d�;c�}��࠲���N��`�<h��ɼ�^���J�q���î�mdǯ��Y�C�����R�E��1�h!�f�W��@{��xm<��cz�릉eI�ӃR2���(w-�@N�.E�kr�?p)�]�<��aKY�Zā�Zc�m��c���E�!��5�1q�'qse_�^L���)�W���3�=^�q�li�v'��ϹZ��g�����3(�n�C�&��q�\6f�/��)��+�4t�U�Z�̬��\kщ�;�k��|�jw��N[�~=L7n�fgY�w�^�
[��A�;�l��d�ֲu�]����:p�θR�����7|��|'�������Tc֢_�V"_G3
Y�|�5D���s�Bj�����U0Z4���%#��I�ހ�hآH�8l
�}a-�tmL�!�"C��)����E�Vf��� B�h�|��E`�b"by�Qx���{��k�>�=�	�m���g���5rX�?�#0p�j��6۳��/D`��v}��tm�p��C�#_[V#�f��Nu��w�7z�l^�`���@��<�}桏����l�Dm*������rܝwky	>�wY�[f����+M$��\���� ��|�8�_% ��:�EӠ�K���7����Vnz*F��`4~j7�����g�`�q�u�6��<�Y/˼�z`i����G�uCɶ��J!�hm�	����
+˹f4��gD��^[m���Xdu���[@^�:x�<�qR�G��s�,����\���ڃ���)�j�ٍVY���V�՜��Ҋ�����@���*��e��R���|�}���
)����+�U����)p厼�:�7`^�.���R��V�m6����2�X>k�����P���9�jV)"
Q�h�絎�%��;m�
QI�;���>����enygm%/��7�
h\�(���[���\E�VJE���Z�+א�@�
�F���ؗ�ٵ+!�0���I{��
b�
R����ѿ���%���2|��KO�iE�<�coewT����ո�r��<�R�w�ݚ�*��Ci�"�R���U3"�+K��Su�r�|)[�{��^�Εk�v{�K-@�ky��Q{�w�I!�)��T�Ke����qs�[���x�X��U��DJq��W�<�˽��\
�Lw�h����n���}}��)���Q�x��&#�f��8pW��q$O:+��!V��Y�ˆg�v�w<U#����;pg�����ʅ�>p�X٨t�-W~G�u]�le�΢�Y��\|[�p��Z��|k{�8��Z�|y[� ���5��wg�O���q�6 iY�yh��˻�-��>��ij����*��L�mcٿ���G�KX^����<�<�|ֽ&�Vj������<Ύ��c�T
���k�p�"8l�3�n?�"ρO-j�"Sn�P"�ck�A�n=.���=MS�+0\cõ[�|ڈ�p������n9V��4Ҏ����c����n�v�i�ĕ\��[��""�&�~��+�u�wR��X$w�)�d�&E#p���#�kzQ�Q�g�_�<��W��ο�A���u�0���l�G�x��e���������"���[�!��Rb����/���ֿ�<
7��h�Ki��K|�߷�?�F�[�����Z?���%4n�Ni��;O#j��r3����.�_�N��uɻ���.	�)r09؎�P@��O�N&�|���j�l#ԃ[��(૿C�5�L
qB��m�ֶ79���D�+[ɬ�8!����/g>y�66�������Jn(>�=F��J���g3\��^]W"�E[m�[u�����x䝮�4\vZ�ܕ����W��Q�@�/�*8zg�|�>ʄ.��P��D������"���wQ�zx���;����;4�x���͜�
�H��K�Q+2z���}<]-��//��|��k���G~���"X�8���1
[PU�"�U��6O(��*z��:���nqd���w�F�?����sx��kg^���i�#`�(\s�7��ZH�/֓�E�<T$��3r��VQ���Cg��S��E�΀��;*���(���8�iϙ�Ӄ%Kaa�І-V�R��4Ι����o���WS�+��q���i,E����_"�k�E��&��?��v�F���p�-"�ڳ��.F�)���pv�n���4�oT#��{��I
�:��]��+�r�O#�`��8X҇���a�b�=wY��K^�G�|9��F��1���N�m*��x�|7�~񙜽6/��H۶�Z&��3�t����*}�9�E���A�=�X�B�G�̬��%�g��K�U팯C��������2}q�B��Sd��	����Z��D`��ީ�8��*�����hK����ʛ#�Ƌz9�E�S����U+���|kQ:������v�]�֪J��5R��O���j�m\E�Ǐ 
��}E�����:<�W�-/���+b,�ع0Yx�v����A�k�sX���mU� b��A��H�2�ҝ�e�:�(k�z
�:
�J�5\���Jz����������Z��+��&��Eöh�W�h�R����fX�2��i\�V�3l�e�w�~��W���Z���ο|�U!��{aZ����5��A+����S�h�)g�"z9�p�_Ԍ�^��BM�New7�A|�`����Y�o���/�T;Z��%��H9q���%u��-��|Q"���դ:�-�(�b��l����:��ApE�v�C�6\a��,U<,��'����&���(�OR��p�_.9�I��WB�6ݼfC�v�a��
�_�8��B4�1��Zz�߷2�M4b�R*��1�k���R
1�����@k�V�]Q���,⣾{w	��bpX]᮲�]<&��P�|�������<��׎��j��X�"F#���B뛯na�=_�UE�"�#�H�h�j�X�,�E�2��+u�i���n����L��|�2u�Uٮ����J]��ۣ�ϲ$��q�g�7�YZ���|-��p��Y���iK�&��=M�\���3��J;SJї�y��9��3���*�p���L�`*�%��h�1x-s�Y�xf�sk<?�Y��f:���{�k��#��M�Fjn�i9�����6���W��n��6ʒ�z{�`�U�WZ��0�|9����Z�؃�J�,Ҵ��`~�������A�?n����yi���)N�UT�,b㐒jD]��.��EȵR��,��7�k�V/W�<�0�U\j��K��ǁ��`�_uJ�2��`0���R��Y��i`��`�7.�R��eؤ~a�� �.pY�2���R���M�
N�|rcv�����u�Z����8>F��9�\���X��c�]�=����e�?��a��t���$�d[#s��#O,�x}ȅk����(�6e��\Xm�!��G�ppd�񬺴�75�+i0c���c_Ȁ��k��|�~���S�p�0}���;��t�3N}}m�GM5Q3���7�,��ۣa�8�=%ha�G6�"�8�1�e��c���e!��'�?���+|���I(�_���DYeσv�J<fU��5lt�s{���d2�2��
D�L����U�y�1��c��C
����5�v�4��h��JĽv;�q���9��q�}����U���K�u�S0&�����Y
L�x<,���׸87X��<$��m\��,_{^�K_�q��7��>AG9��g�f���2xC���2��Y��	� ��A���6��GX�\\�X����8>,
X�=�`mY�_�;�
�VN���4i�c33a&�KoxQ����-M2�C���Mg_��r�M�rh��	�d,dÙ��?��<h�����:�fa��M�_htv�����0�Ԕ>�dӶ�A�
��)�=5۠=?kK�����ÿ����#K�WW�pC{{f�d%�u�r�}N��v9 b��9�"$M���h�ʘ7e����`Ű׏\㺘?�{���c�����WI,�$�q9���}�pP��a�
��x��g��L���+�g�AY̥�Re�_Jg��_}���ܠ�K#e��؇��Į���S���݀V��":Ha2���2��K�A�JmӴ0j=���:��u]��X���%p]�W�޵e;	ƨ%�Ҙ�L�o��Wџ��_פȀ�����K�{Y1J˲X�C�Y����Z��Ѻ�U��e
FdŪ��n�])�d��tH��s�,%2����o����#(A�(��|�k��l� �o����Q�;t]�a��ԋ�F�փ��:��}T���c���*/�S�9w]#�m��"�5<�Kh��C"/꣛�����;�Ɵz�.7�2`���5�Z�G|
p`��٥l�(oY��qb8��k�'i������^�����"r	�c�]��"�yָ�� �!vf��x
�O��g�uâm�v{��	P��x��q��ŀ{��Ŏ�^�0&R��1ID~���1l#+����YQ���1J!7u�F�{Ƹ��p��8�6A-m�pm-]��b�X��QX.}k�0��㏇p~��bf��b���[�i��?�嘙P5��c)Ю]�ˏVo�]Qcl��A�_��QC���U�o�������er��F���@�%C����N�G�8�΃L�r�,w�T��.�Ū�L=XD�{�HZ�ڵ�}��}���"�gT��5�<'�x�i>WG,F�W{W��ݞ��嗁6��u��c��3`O��OAv�j����vm��}�1U��LU���-V�+�{m�^69�<��{\�=g�.��@�t9p�c-�t���%�����Նh;R�]Z�]�^y�}�y�?�x����(�7����AK�vc���o�	����y����kqڍ��H��E���x9.���;Ԁ�Q߆�U��5#�^��n�ƌW-�b̿��m�|�f��8���j�o�w���V�J�A�Ɓ��/R	��������-!Q���- ��뺮W�6p["��+�uLk�[ۺ��f�X�	��ї1Y�/����H���I�Fws��C�k�d�����c�=�|ޟ[���y�s�dQZ�9H>����x����7�<��.ij5�F�%�X~�Y�o�{�"�™C��Eɀ[W�d�Ţ�%7}fm�y{���Hi�G� �SD�0�qG�Y0�@+�s�eaU��]�J�
@ӏL4Z��
�e���7� 
0Hۃ�g*���Z��y�:�{�,�<���
�~=Md'˼��l��C8#��0��R�h�#�n���d�@(U�׊y���9���6
�f�"��`2` �H�D�3�0;BM��b��jB�5��g��v�� ��z��u�ϮaL�f�Cw�����!Ȳ}i9�~8��}�D!gg!�a����+b����u���mAo��
Q
ٍ-W��J?��]��y�a��Ϳ����גQ��>.1A�?�h��@S�7q_��#%��\S���q��_�^|�?��3v�j��]G'e�r"�6�#�m÷��D�=�c��td���xK�n2a�����
�!6�{�H���my� ���1w(om ��ʅA
�>����@���<�K�v<e�K^����gG�~���+�߁z�P@l�OS�f�.�1�Է'!�����i�l�$ƹ��V��s�S�Q^k�^�MԽ�K&K���`j��4å�H<߅n��CJ���|�n�D�*�MzkoT��߇4��g�n=S��KP���2�D.� ����w���o�)>t� �ڑ��۟��/"�YgNC�o�.q1N�ɒ���{׬HN��I�u�P3#�"��S�OW��N��43�3��KD�¿/T�~{ipGu��w���̂�V�u)g�����
��jœ&�+���Ն,g:��P&̸h'��;[J��A-�
�"ߙ0�g}-2�Ȃ��[��e�~Eʽ��26u�7޳|��^2a<,��Y0�X�Kd�:�f�"e�i�3	�|EȄ�T�^&�`̼�%2�v2g�"���l|F�(�W�L���'��k�`���%2Ẳg�"d�{>ZE��+B&\S���skq�-y^I��|�Ȅ��<gK���'��dQ���"_2��*_;�j~n����:�#�|g�bj����?[D�q�|�īRj�~�T���A����5~n�1V|�>X���h/m�l����Y�+��g����,=A���W�p&�!
�pT+=x
�"
2`���뺡�&�|�Ȅ�#���*n������^�;p� ���ʄS#��k�k��)��nGK<�{����-MF�[���'��$\�}\��g�"�µ��eb�3�W�;�����/X��H���p���n�������Zg��;j����9X���͈��w�l�:�U<F	�"
�H�,�,�,J0�d݂`�U�ʺ�6�(E�7��j_�1A����j����c #��qPO��+��*r�zp� 4����ȯ���[I��+"�O�_܇$�����2j|5�&Y[�RD�/ȩE�5�1rZ�R��E��9��	�"Ⴐ�;��g�Z��U���
�CvF�U;ge֪���,�ɀEt.F��v��N7�k��S�["=�!FM^��^�]2��"ՃE�t6p�3J��24N�"659�����iȖh�D��U��|E�`�0���[�����j�H�a�}��=V�,:5pE���K���Z
�"N�H� �!�����o����ٕaOĚ�ua���v�R��n����A()ھ������M	b�����A_].�t�4���
����&�0�R�86;��K�qZ��iݲG��\��^���)��8�"a�����1ނ�H�,?��qO�m�K��H�,B�|�r�n�V'(4��n��|�s�-2a�a��(!q��%K!�'͠D�=/L	bI렛��!�C��	{��
"�;�ׄ�C�LG�U?mi�0i�=Ѿ��Z�?fO����8	��f�3����X$_!G��*|�Q5˯)MQ
H��7�8�+Bɡ����`�=��.���&���ܒ9���0s���mR��[�����}
�q�D�+�>�#4�M��~�k��{��=�Z��|X�-��c�(�g��[\�XX��l�z�I�Go�<���V�,�<T[˸US[�Ě�+��k��߿�uݰ�-���]������E�(���lx�����r?��zu�o�S6l2�-29�6���Y�c��i��B��Y��4X�.5y��ھ���V�<Z,o]��u��Ԯ�3���d%�,�e�=݊v{.z����v��R;���…9�J�2��\�Y��*�m#]�ZȀO�2�4��Q�:Kny}5wM��z�"ݠ�a,�y�q�g	�c�k]
�&�~!M �_� ��J7�u��R��k�8�J
"B�W��	�����bl�����$�1Y|�4�K�P���XYBDL<�I󙼆���
�q104�~`2�dq�0V��6dqb�0�,�26��fQ�hh��΃�ے����h�릃���!���q|,�o�ݞ5o[\he��<�Iy��>.տ���0��P>����{^� J���?�&�A^&�ן'����"���@6�ӄ�eʎ�O�/2��$��L�N�?"d��в�^"5�p`�dz�n�Y�v{���vv4x@v���jԬ��0K��Ʉ��xm+(�������g������v[p�d ���3N=H�|M,f��/w�:eD<�U��p��pc�,f�[j��T�C0��K�]bx��C�dk�M��':"Bf�%Z�e���Q��hAw�as%lZ�
?���Y����`�i�K�_\�)s�%>'�@AY�K��Z�ap	%�q�m�e��J
(�����e��s�u�:2`4��
e�O��B``��"0�ށ�h��{�=�[��p��n���cUP�8�wVg���'�&˅5`�0�c/"_����="j�hj�k���WD��BGկt '2`Tc(������Ŭ�[	|E6�g�k�	~�z�o/V�F��W8��{
�kB�-�`0^ �A0΋�p�a� M�-Y�//V^�ٮ5d�9���xm#�F	"�q{�>�O���&��ʹ�Ȉc�3 ��C ��N����ľ�yAxs!�D�C�͍@�� ��_��nt�B�/��x��l�>2`��#�W����X!�"���a�Ȁ�!��d�6�*:B�E	d�v�;0�����S�VB����lX�0�/Z �a�|\�v;�cM+�1L(����nD&`C����D;� � ��zv���S������+�xyv]��n�}B&�XYƉ�qbg���Ǿ���d;>V���t]7d,_$�*\N�w�{�v��5�^=Pe��VȀ�|�g�wD��y}�pCA��G�sE�2�A�,&M#т/��`%�����FD@�@��;��^캑	B�l��r3�zN��ToZ�A�-7�ےWC�٤{Gͽ ����@Y�
.^E��~�Ҙ�B��W��	�d�ŸɅ�V��I���1��
s8q>�8�x|P�p�c��mx}p6�x~pp!��l/�zq�
��>�IX��,����,2��L��Iៗ>t2'Թ�L�<4/��yyɇ���
_h���n�;�8>��`����z�0Aq��/2�z�ʰ����*����0�U��QC>�����A��1-�,����u���Jn�6m1N}�b����'�W�(Ad��`�Ɔ}t�A�	&�`��E|�Y+����\�Xd-[�P��ql�Zbj���������o�&|��������Vh�4�"����wud�;�=$ �����:b��,�	��Ȁ7X��[ց��,�-��[� o�0����L�$=-�W�e��v��g�`3� VX��Y�=D|�{d�V��l�&�Ʉ3a�/���F���xb��D�L��
�_��&PO�d�-0�':���X�0�<��l�S��1�vZH�-��u�z�%Yp���|*�~����A�[(
֖~���d2
X��+u���R}>a�DXS�@�c�ԓ:�yP!v�T�6k��ڻt"�!�`ed�q�;��I{��S~�G̙�Rf��b�?�rZai)���+�����p}�c7��`�5eE����J��|+�k�e�ʊ��
�
�$��0bx׏\�z�P��u��`���!׻�P9�JZ�
-g&?�j��^H���5)	��]�;�}ỵ2Re�dD~��;�=�h�%�
���e��D%���P�;�6�S���r�O��ì�L��	�Q�%��f�$Ê4X��%�q��q�� \Ġ�K#=�SKy�z�}�:}��I��ü�'S�4���{Ʉ��uS�5?���U�2Я�la�pEQ�T�
��&���Y��i���Y�x��R`	���w��6f%���a�8�_����§5J�*�s�>���a��:��]΃���6��u� PU��+�o�
�zR���L��"�M��>�?Bc�D�=�*I��.<�z�<�#FQI����sY��w��n�W�W�#Ȁ���2��s�2`��moi惫ܯ G�:ד9�n���ḵkȀq��7�	� �閛@�c�<�v�>�)���C�='�8Xc�]C�C��XQ������G�g�~26�E����p-�F�M��ŜYzh3�0�Ȁ�@6[2OA��5F=`��m{�V���=]o�G����ȆW��Sqw�Q5��N�C�@v]�y�ۨ
�D���}�ݷ��?/"�>��OpO�Ֆ�t~�8��^όsḴkȀ�zld���q�N�辴ہz�IL���n�Iϒ��cw�"�C��N�Z�rz8׋��̒�t1�5����_nc���D,.�Y�x
�"�ת�ls�2�@��Y��1���9���%8�op5-���Ih%��:^}��B��:���$�Ԗ+��Cvh�ۧv[nP)9�3��ݎz��8s��H�G�3^?���k�q}#ͱ�Ȁ����/Om�O��a\��+"~����m�2`��d�"�7�.E���\���lL��+F�]��!,�>�׋����"+�x��	fVy~�v�NI��+"��]�}���K�A��E�	�"��C6�^�u�6��b�ho�^�[���́w���5ËQ�pE�l��nKfv8Xz3���6K4+�W�g�Zj��޸#���M[�����_�Zk���q��@2~׭��Ma�a��IU��I酵�+�'��<��b�EڌEJ~�c���lKG���ϙ\t)K`��_��F|4�.�y�AX����X�-ODK,gfg���K������"�`��"�8��忑3�����/T�z��Cp�%�|LO�R<L�֬^�:�������P��8/O�kM������.��:��PPV�<��#��c�x	�"M����[tVy�7��c	��y���ʾv�P���u�<_���[X�>�6nȈcq�W��mdcO�/R��E���U!Ga~��ɡ��9�J�����Wx{�}3��+��q`�x�
9�Z�NC��]�O�x�S��{��+b/�.��Aؗfx'�~����x���ޣ<���c�Xi�x�L����s���܌�i�>�ہ�v��k#(o�s��
�[���$���k�n��3�O�mG]�[��L`>�	��Ey����N����?&�x��U�!�΂���:N,[i��5�I����������%�
�"�wI��/bD)�β���t����LK"^�<�w-j�A	⼮�:so���0[��R:����W1�.1~~���{Ȁ�#��#j�;˞��.�qpA���e32����q�{��i��Jׄ/1,Qn���t�C>��.��25N�|�nG�\K�Q�x-�F{�E�%�D�-m�!�B ��݀R(A|[�ڍ�@�W��r��ZM��~zh�G�8�_^u=�rJ�/#�v[�����ۂ?>�@�k7mD�O��O���a�v}�s�DB=x����-Aק�[[Ɛ��T�7��`2�צ<�v;P��:��<`2܃�)�37��g�x8
�G
�ڠ���d)��s5}�_f�E�	�1s���2�n�V[����|1���n�kZ&ȖA��-�[�pӿ�z
�]��Ȋy��(�a�t$o��~�{��@D�# L=��7�\T�5/J���AЅ��s��9��д�ڟ��_�b,��в��@�U�O�v{"k�*r�2���%�h� ��4�q:�,���� ����j+����,>I4�ʳ��+b	���g��J#��2�"��mW�7�F>�����`��]��Q��CƆ�OT{��AF��~�s��(�(� Dڕ!(A�T,A4/�.3���?�d��F@F?R�h;!(A�D-A"�0(Q	����P���2`^�`�VRH	�%�?2��6�G���؊�64 �H[�DVX�!5��X�`J��J��D-�@v�p���6m�Em�;!��aY#�4�C���Մ��,�34�� @	��`��8!��)��H�:05` �h; D(A���.�k6p�u�����6��%��X��AB��V���ݢvÇW��7�|+��q�[��0Dld�|�[��L�2cXc!f+����>Z����@�}�2�NS�mS��$�N����<�e7��H����x�m�n@
�h7�Y�@��D��\e��?^�f��q&DRV���6��q����.���-ʃ8��Spk�c�������4�	���H�,„��j_O�������+c�pe����8�_"�+�nƍQ��0�0hDZ�&I_���~C��
la�9��W�N��q<ܖ��>ȂՅ�,bd�;!�3|/��u�>+�����ہ�L��h߶�'���_�꿽��{ڻ��ɀ{c�� w�w��S=�q$�\�oc›v:h�n�[�0���-��vfu����r��,���
���°o�n@2�K�o�n�q�Kk}�͑Պ-��Hۭe��y^M`��ijs��"q�M�=��`�Ê�N�`liuB]F�����\�>JY(�bF��Ԡ݀$<��C�$'9�>���^=:F��(_������y� .kϘN=al���Ys}���>��Nwo�}����`V���">��E^D{����eN�%^I�]Z���$�+ȳSp�C����x����L7Y�ofylsD��P5`Ts$�T�8��rV^��W0/2 �^��YZ�����3����N��Za!1�„���si�� g�����f����!
���m;�Y���e�e�����Q ����e�K��D�vz�j9�����O�<�׬�� ��3d�Ix�w����άm���)@�x-�y2�>*�
Y��˙�p�پ,�?��}����2=���7�}��8�L�
o��g�a�os�K���~k���k�?�Hy�,���^�#(�{w~i�,�dkM����ٔ1�~��NԾV8P'�}}7��?Z�.�j���G�!�{R*�ܾ��-��P��V�
k����`��}�� �L�k�����h�b ��Q���%0u`a5 Z\<p��p"j�"ԁ����`]����Α�&Y���":��n۩�*��Vh)�[ց�,�x�S��CC��eL	���u�|�9���	���錹/�M�X�^�]��^������}�y�~gV�V2���0���y
x����<�/Pt E(CL�5����J'^y.?���3�&�=������T�]րE�VE�=gl���8���
�,J�4ޛ���>j��Z�~�T�2�5�nݽ�R,�\~�B�_�%��,��|J�xV#�|�����Q����8�x�
�VD�]�VV��r��zRV����T�DRQ-�뿥��,��"z?E,���*8�`�|RS��zW�ţ����pM]�
�m0n8�e	`=^�X���纒W�k�kԂ��<Ok��f�=��Q�p���Ȃ�qm~�zA�
W��wv0���~k|�~�����hݸ��?��O�fǣ��Vyy�v���K����#&�WKN,*C�vx�P뻗�/
e�pD�ze-���}n�j���������=d�vL�v�B�ʨ�/Ÿ�GYd��\~�+�l��&u�1��K�,¾ࣼ�ɚ��3�ե�"��`��,�P�
�kJ>�ޖ��f����q����ϯ�×(C���6��k��b�2�<��c�A�օ�5%/C�����㛵A�`B�/n����6\��"�Dg��;h����{}�q]�kʸ�����r3���m5�K��߆��m	B�2�g�!~�8h�]�?�Z=�	�W�tY��R|w$���5e��<��Q7��-��������w�i����Zf�8>j��З�([‡$|3P��֭���o:nU&H�v����2>��"�ب�"�E(A�^�d��E+?�(�ַ�<�+���Z�@qD�y�W���c�Z��2��k�[2���y�%�A�w���Ϟ�z���PR��[��/�T3��82gÙ��������<A2�������"������z�U\�|"d�����-��
e��X�΍}�e�����L%��p�=������ b�!B�E��� b �P�nɀ22fd&X��:���ٯ��0"��P��D�B8���\�2?�d�"d��d��Xʀ��zl p�P���U�+�Y0`W��W���h�{^"����|�*K�A�f	"�øt_�@LQ��%&V�5�`��&�왲_�psd��k��o��a��li�*��C@e�ʂ-�����:����l
�j�e+?�̀'����k�p�`8դ|��9���`M�SB0�B�[��-i+vD�"_��r	���+��8`�sa�Y0�^��K*?����l�Z�3D°��C�KZ�Ȭa�}��=V����<h7�4˃��|k�ŋ���-Y��9l`K�������n�Rćq"�;`G�`���;s���\Ȭ�Á ��a���d�@d�:�d�"d�@
�?=�pp�o����~r��%�c)E �ց����8�^
�h�����<i~�Nv�h"'3^�Y)֢�!أ>J6x̀E^Of!��۷�}����d��yd�vx΀U0��B�}��}޸�䑳`2a��E&�	������w!���/��v�\�kd��k��_i�5�Z�7��A��� ������4?��yN��a:�,���uݣ�/%�=.D,��7{��}��-���7�u��_�@�H�,X�L�)&$d�'����Ȏ��G�DB�v`O���E��a���}�ũ�	��k��ɠ�K[��i �A�eN;�.��R��Ř�����G.��
{�߈BO�LY��!#�ŗ�����C	�@ȃ�D��}���	IQO�����l�W��%��F)�XF�ǘʙ-�wd�� ��_s-�K3�Ľ��<h��Ч߲B�qK�`2�(�"pY���5�0<����(A,����-�@��J߱��a�s[ʚ�0���/��9F�/[��k˖�.L���c,���|���`m�'GM�X��Y
�Jg���3�Z�R�L;#�/��s����-����J�X큜���m��a)�N�ƿoh��ԥ��ʁ�L�ceA���Bȱ�m����(E|#�4f�v2� �Y
z�h�bб4&3H��'���u݇��H��u�ܒ���kʁ����KC$��
����g�F���%��,\f��Ahu�j_��,�,!�Iu[��@�D�����L
�����[\�^�Cvf�O\�
�A���xE��7���A33�y���5�lF��[�<�n�����������A���@�2��q|�ȧv;Z��?3^����A��,�aO}2#��C	)���uf�e�D"��p7�4XȂ����GDɈ=ݙ���|�j���=�E|d�5b��\D�z�~�qq��5M$ƀ�
G�Ϊw�amԀ�r��!x�c���4ܶS���p��!�ZkV`� ���r�p״2�,+A)�̳�9a��%��/\��ԃE���ͳ�G��g
��2��ԃE�e�k݂\�+>?�Q�uvX�����Z�`)�e�<^݅$�&�a��^�&+�_�	�����ػ��V��I �D��ʸc�]G
��A�3+�uY�g��=\�B�L�#.+c�1vp!���I����݀L�M��0�K{�EW��fm`��z�v�i�/�7 .d%�2a1��{l�6��e��c-��&3^��_����L�+���ڤ�6k�D𭃋Z���4cB�d�L�+��Wfu�N���_�b�k�c����ۈ�LX�If���qe\�3L#n�r��8����D,^k�C"7d=�_�-��WD��ҙב���E��pPcLJ��L�m�r0����[����:��<{Ȁ�Xp�k�.�zE�3_꾍���"b���A+;�Sm!V�!��
_3����:5_E�'&�	{���,ޗ>�l;��8��"�#s��k���?�&�1^&�@��S���3G��j��xhSy��a�9�����a��I����݈�}"�����C6��ф�~P�qql��:�4���y̴Bb��W�m��%RC
ظ���u���;��y�@�8�4��% L�Ƨv;nP��0D�r1�W� ,b7;����ym��f(A83M�A�w�=��"���k�����YN98��u�Pf��B�%>G��pO���Z巵�]33�M�]ں�(?��j�[,d�K��bqԀ�&a�ô��S�{~M8Bmw�����A8�/|��c�Z�0�ݕ�}��P�Df�/C�2��v;PT�;�l���Ȇ��xn��X�C ��S�!��sqA�o�w�;�e'��m^���,�%�<�hxx�8����KjǓ�ȉN��P��-�2��!��P�h�rDȀ��l�*3'���9����U�.���$�[d�dZ�*���@|N��;c���n��>�kqNځ���?e�3��9<��e��uO�(xh��4(*QV����k�t`T5���۫a0�����{L��n�0��Z+��ۂ�XP1���T��u݇v#�">�u�1Yn�s(A@�|6q�2%hc����u��C�VY��I���pk';�!�"2�s�p�8���?~j���AD���N��HiJb�'������7$�� �5m�eF�p��6�A�7���	0�q��;��u[�

�"H�4e��Q�Dr�0`UD�
 B 1��e�ǸA�]��sk|.���`���"8��@$�6��"BR��2��[�M[[(ڍp` !\�����F8�n:_�K�@&]ױ��1A�`Hx{H�[���s$��?$Ā.^�Z��F�8�Ku�.�@��S��:��$���9�@$������a�&����U$�u�46����ŗ��{�%6�P�c�����6�a`(�Ǒ,"�b0P{�X�S<�Bz$��E�8~��p�_$ývc
<N"�[$�	`�����*�L��^ ��UaD�V8�<����G�>U�������#40�ڋO�����t( ~{���4�#=ߔ�^��v�����
�3/R�+m�Q�2b�k�–��H�������B���Q�^���D�pR��1�_��ݶ�v�p_�u[�+�aǨ����%W���>��x�����a	0B"�5��(�Y�#�9$����&��%*�f���#���^�-��gW�Aȥ�ac��|Q��,Lp�J�	lg�;<-���141���5$���X�040���os�΀t�3�g�{���|��v;�X��ʢ�t�?�\?���vC�`�ǣ�6x	��{m�P�Q�E��U1
��&�ݡ�a�h�
̧�����;�c��/����
|y�1�DfL�o!T�=���K��P
FI$�P���� ��X~�N8M����%�HB�����!����>��T}��^�/�H��:���sBs�[��Ao!6�*�hfG�n]�A����·���*�h��t
�ۀ�8�g�u�F�6WTG�{�/�%��y�Q��TÖ��8��b�y�?x�U�Q�S7G+Ι>��k��0�Pw݇��2�[���o�	0�"C@I��c����E��_���r�f$��m�/a��K,NZ��r�/"l��M�M�F�@�T���a�'���H~_�&�_"V'E�K�����������{�qAt�|X6ŧ�v;,b]ˇ��$6�
�+��m1�q�7�%���8�g�����CH~�� �o$���x���@4T�#��F��%�ߟ�"#�D̏�S ��@���@tS���a�`\,��Ĥ���@6l���w>ba�/$���Ȍ��>�[ ���'�/�숃|NUq�v`�8��6X@�������q��^�-ʾ�'�aБ�{�H~`��7։�D�[Djp��E��Lfp�Rd��H��#�%��Y�M�y)�7��x� W?��Y���Z��/��c-a-�
pR,��=��4�'�O��JKW?���2ش2W�i���^PN���%s��t��D2ْ9(@cO�(a+�#$�d����º�����PkP��^�Xs|��D�G3Y+p`ǯ��~��k�	(��8X�]JT;���!�]�
�Z������>�"���	�^eL�����A	|}��!c�P��E�n
<��
f�~�b7��P��aw'�e\(�����^��*��e<��`m�+�T��Ƞ�`F�X�E�Z��7�,�2OZbN�D8(��@�*����p6;L a*��׳Д�9ƍ!��2w
zv]��n~"0�-��ȣ�s�j����	�5��@�p���P�8�}��_|bp#�#	�"}w֨%x��mCP3j>�������H��{�}��9
�U���`�m ����6�^�--���ؾ5$�d[�H�QB�ys/̡�ds�� �Ϩ� /��2�0u�3�!Ӟg��i1���+���-������<����@��tsX�#PT��ɺe[ bI��
�p��`{pQ��-�N$�L�<w�x%�\����;xz	
tB �}�d�Fq�?.�4wY�l`��q�b��l9��?�
@����*F-���0s�p�j�KT���>� 2-.��'�N��0~H�"_�3�
0����~ѽ�vc�C���M�m-*�h�G�AdZ<�O��C�"�P�8�:��
0�І�"�*�@�ȔDp��ה��	]\�܏�u-q0��2-l�g9�H �Up�jZ/�(8�@1�녂�~�vc����M����nR���98�v@�q$�Up�W9_$���8d��f�di��a�ڍ
�d��`״��d��wI�O/y�-ڔ��b��G�u�b[(��*�u���v]�1��l�(��0��ق���=��ˣ�[ݳ�G��i�+)�_8&˸f
��N"K�!x�£��j�U�����:��
dY�X�l���8Dw�05��0e��H�+�2�Y�l���8Dge�L�P�MN;Ģ���*ƍ
�3����X)E�-�����}i�E���^�=�#X���ݞ�$����E"<h�%����x�
A�*}T��e�s#B�dܴG�:&��d<�@p��
:[�'�"?��O�d�V	��B�Ȕe�{�&5!��Ge�Ƥg�d���0�� ���c���WI�u�O���D�dYDz�+� ��x��*���I�ƥ�h��,�X�~��N"K��<������2���Ǽ����,�i�?�
2�حc�fy��"���q�g��0(Z��@�[��ZRA�	0P��CS��k�)9��VY�u�����3H��6�O�6A��>K����˃v{�8��L�6%ɵ��ٌY��XEl ᵍ�
>���H�!���7�a��#%����7&�a�H����$����BD���]��vHz��)���e~d��,}�`��!hF�B�W���s��θ�����	$�?1�_[��A�=�#P�2��ޕ�����8���>��`�=Iz_[$������W�Lj�d���0C�cS(�����M����.��H11˖�H}�'K_z@G�!p��q7�5ᓼ���e�eXDz�l�p/��4�c?-�%��~�5��V��v{�c"Ý3TPm��p����3�
����+K_z@�ҏ*/��9�8>*T|ߙ+œq���a 	�D��p�j� �dl���eX��Y��V�wMY��:"��Ő b}ޱؖ����,�h}>���/=�#��8pUނ��7��ZV���yuE���=��-�2���[�<�<η�W�{�D�G8�H���_�Y��/��U�˂\��m�/�U�u��@^���o��3�Ķ��<Vz��l}w�Kd�s��u�C��F���J�4�I~��G�Ӡݠh<os����-�KL���& ;��^%�$�8�I��F�mW}-�*"!��D����	�C�A�-�m��K�
�EB���v,���+,�h��`
���L�k.gهj/�������.���rS^%�Ͻ��v{<Z�ʇv[��ܱJ�������	��S����.��()�|�>_.�� "�Y,����Zx�=�EA����]$�v5��O�	s�=��E@��l��xZ����mpէ��8�G��|����_t�nd���*�`;�A$��1opX�����?7��"��TwM�S�
`���M�u�?��RYG���r���!�1h�d}_X3jyt�����:~���@�[��TnT�s���x���뺎/�!
�]�Hz1c��VH��ɮs���;$�jb0��ڳK���8�_����O�����{�a	�ClAeČ@���O2��M7wF�?�y�7U羴ہ��+�E�E��ψ�it]ױ�mmap�m����j�*�(^�C)<�L$z�!|-�)/���
0�ڬ�ƶ�(�/�C�S�7�� S_P�*�����-��n�f�n�{�3�=Tt!���$�HcU~h�@}l��ao�B�uݠ�8�X��ƶ�Ob >�8^%���A��>�^�M�넸�n���hN�����\���F{���EB���?�yj��p�8�_BU���E�C<�h�_� n��"Hx8����y	�s_pZYO�v�vCǒn���s2�\"��Kp�
�d؟}�$�(a��_ ��v��lH���z�l��\T�~Y��t�'M�rPY�-����~[X�V}ml�����n@-�_�c�4��h LR1�y�G"����-|�hg�/�������'�e0p��)��#���S�
9��$���l��($�"3s6}�d�k>�O��@��Ht��q�d�k>�O��z�����u���ik��F�XHl��zm�����c��`��g��Ir��$�pc��!���n2�8/=%p���LE�P^��6���R���"�}��`Ï~��)�놀	?�*��
{m�a����w�U�Wn.��c��'����%SB�u$�eK����[T�IT�-��o%���Č`�^D��;�Y��4~�<�#���y'Z��$b���{�S�����Qt)*�">�^��GD�1���cl�A�
00�K��j	B�L�#���>`�ײ�K��R���gO� o�G��b��c�?�Q���?�J�������J��*-Q4}A-
nP�m�n��&�]�

��
�x�l>~�*X��l{�{��k7��a�Ȗ��:�*ړ��
�ˣ9���]��(�;U@�����1�.���Q�� `]$�+�䞈}@��hyū��`��~��q�G�p���d�;B��C��h%q�%�}I�<��r�wמ-�{���
�;��]�+�����[�-�(R�rl���B�Do���N�����;��_��S�-�8r���T�$��6s)�������'���Ss*�b�v��i7� ��C��S�S���GgO�~�+O�E���?�x8���S	p���B�$5���������l��Ucn��
8*�_��{�EbVL=����_��/
O�2�O%\-w8��]I�/�޷wDĀ.�F�O��y*�E��\����rWsR���=���'�"��n,-� �S���]J�;_wl[�V�U����9��K܈�7���W?���0=&�l�n��Z�����8�)?
��^q9�G�x_�"�	Ђ��_�8�S,�6�[U���L�޳�
p�t�N��H�m��` �=n~�NH��0���{倆[@�򹈯�["�	P��.��� �'R~�i���_%Wb;	�
	0O�y�� 6=��X��)6/�x���{�Ad���(��5.�^u����k��.�B�ժ��!b���.n-ڹ~�U5��m��G�u8�K��1~�r��s
�ϛý�?j�������^����6/������
(��N���8z���B5�	eJ��������
x�S�)G�s�oW��8�4h7PP4��+�7��KW���)<U€T<?�.�-;<��o�F�N~#'�k_%+B$�H�Ӂ���w��"%����<=�X��x���s���Q��k�r�H���u/R�:�J%��8>"-}�:>��q�-��l�Pr�P�g��|1��H��T�eJ��V~�K�>O�1�"Okm�7�E���q���?��"�~yT���֒�[�_9B����u'~�ͷ�ׂ� "E��̓}�n2���K�F��u/}
��GGy�E�@���&1{�T��TD4h7�0ɯ��5��nA�r��%b%�$[+��X�P<���_��N|�����?�[���q���S^i�����?��i@$��GD����7D2h7�Hɯ�#|	� ���qF3������21��/��(Gj%����+@"���84���
U`([ؖ@1��|�N(z��qB�}4�Kl_P�i���ȧ��u��Su4�����6�ܸm1�^�����9SC�@B�������a�͹��/������k7@�S
�v+�$���8Ѫju�8~����8���B_��4�A��-Ur��t�����9j �Rs�9�R�ǽ\cD�iݏ4�j]��@x0g8����Pp�ddUrQ5IN#݉,yM��)X����@�����o��Y���*�%�a�"BX@\����zi��yݛ|���{8d.�q���
1��}n<&�!��
���P�7����a���{�+\r��[���q~��e�!b��G�w��{�O!{�;s��Ƌ?��d�c
�Y��Ū�3�q@��	I�QoH�Z�$x�á��4f�=J��q{�_�&oq|:Ֆ���e�����#�YԾ@zU>W�e�*oUG���<������ڿvu]�E�0���q,@�=Ɲ�υ����z�
H�I�]��&h��Ru�-h_"Ҧ�`ye���;���hu�I��4Y���0�57�����u���H��uo����TgL��0f�Vt~zXpj�q�/;p��<%��o-��ZG
��&�,��=���s�[���WxZ�#�ɖ�]�,�kp��
�K}�,{X��Qc70�4�#U���몝�q!9���q��ʞ�.q-��<���a��V�5���8 �0""�J0�Ӝ�h�����
��@�$��iK`���Q�SO��P��*0�(z&�ǹ�I0��"��뮞�<#�=�B<��y�OE �/��Ѻ�&���������T8	��`���k7��A��H�a��@K~�q�3$�3�`�W�'��g%����/�u��X���Yj��Q��/
~I�
�+CZ��>k�GT�pz��Q0�䷐�s�}��E%�8K~�m(E�8h�2��fI������䷎�RGAQ	���{��e�
p#�ڴ'��o=_�+��S��%��vJ��_�7`K�;�%~VԾ�k%�߇�	�=%+��:�okr���µ�ܐ�s�,R`�䷙���P	 v�
�	���SA�݀��BER�W��[$���[���k��x2PzĚ���_=�$�J�t�a6�t,�YN7��hAɯ��I07�Pc)	;�c���	�$j��q/�C�
H��׎�I� $��5�0�ڍ���l	|����3���{8��&���[֒�w"��֮���l� �
�#"MF��gy�J�9��W����Z�w�5hK����x'r@ɯ$�p��zi��x����^��"�u�/u�9{�Q[�<�:���d��D^̬�W"ݙ��_��U�E�-���m!�<0{�]%^�Ě��J�@c��_�e{$�0�[�)Җ ���������{�\4h7����Pl;���E�+.I��W��l�L��cЏ����7��x[W#�Wn��{����w�:d�1�w�0�D)��}9ޣ>��'D�+ƫ�"+�"�;�6r����c(s��)��m�o����Y��(��(�ZXG�x���@,�/�_�����(�C@�DZR�%R�n�,E^�N���H~��ѐ��"qƵ���s�Յ>��d����(7*�h��W��'���ɠ݀Z�n��T�@��`�Ǹ�����}��j'���uݠ��A�%�`T�8��]�$�g�x�&5."���Js�N>�c�Ը��į:���x
������|MH~�H�E�v�A���Mɠ�()uSJ�W�����Ź�
t�٠݀�B%��'l���Sj�2vr(1^��~��.�ĉo^��H�
�H�J��]�<9�'��Jĵ�ۼ�/��뭎.�;��/'<ՙ\
U���a�Ƙ�Y�B�X��>���/��t�� OuP��5��׈�	p􄋭�����X;<߀�:)	4�~�PF�|�%8�Qh�C/O�y^�>�81�s,��/��K��x"��|+x�U� �}���7�1���>X�{}Lr��s��	ʚ���7������ρyn�'���vJ
�ɿ��kLJ�/��H�J��7���\y�2F=���P̞Hǎl�:(���:6�1$�q$�uYl{��:+� ��1@��J�&����s���>���(�s?�	�x)b�-y�c4�M��@e$*(iǞ1������vJ��?�I�'�'�+�ǗA����'���v��i����%OϏ�Ż�ktȬ��H�$�x�S,���Έ��׵L$�x����^��ly���y�h������e�z�w�m�����q?�`2�_��q��2����9ʸ��G�L�'n�>��~�Ab��}�?��
B�Km����ntxO��$����t׃�@�u��`�_p˓�	��<v��u��E����+ѓ-�9z��p���(k�ל��[ f���z���&!$�3���T[�'�>��#�`m���K�xH0&���6yN&��
d�y]�~�S�|��{_\��<�����T � kn��W���$�?�^�`�G�8"$�^��
��=�W�/$��r�N{�H��� �o�ʯd�,=	�J������A���$�	��qG�?����ܞ�8�}���%�
���J	1 k	4�7yH����,Zw�O��a�k���z��Q*D%V{^/�H~���5E�{<��y^[�"m���'%P~#�`�Df��&Jl��қ?�,��L�W��!����`1�S��<���i�V铇/�~+�s%"���FZS"�K)T�J�52�	>�D��$��qQN�4)�x�,�,�n,EZC"�KiT�O�0��O�}VD���>s,��	��&��`�;�bm�x��0W��$��-C��P_�������*�����{�'�S���<#%���dH�o�/�ͨ���)PG?�Z���݈��z�H~��Bi��q״�+ʡ��T��*��j�\@�A�{�Z���T�p������\��WX�N7�5�L{�%�+���׼�rk���`�j��O&ϒ�\F�k���Z�
l�M�=$�W��*Z̏x��
{��46|M�&�F��_>H~�c����WI��L�騷^��)JL_#����,IU���K@pF�X�F�-\�ʢM�=�&d�>�I��"��y��V{�+�2X#�,}t��H�ui����7�%��l�>���t��v�i=�<����FH����"�q�f��&EZ�&|袂����sS������=cu���B%)^��{B�D� �h����1��"�@(��B|,c6���7��<��L~�䷮�º�vG�J�H���X��W$f<��oڨ+J4���/ㅑ��8�!N���*`e��g�$�GT��Ȱ����.��>�rS�@��,	TĘ��ﬠl��?6\R�������V��o�5
q�
IV	;�#gvE��k�6�O�v������lH�Jp�D��(h$E�WDd�/!�EA\x���5��+r�\�G3��ET�
��`%u�v�u}�ԇV��E��;�}0c)Y_(+�	:�D��$�6P6.�D	���%�
p�T�M;��� �nM7��p"j0��ґ�?\�1Y�z҃$X׼I7�<˖4e���!)����0eZ�G2$�K���|@�R�����?=H�PE8Plʒ�g�We�s[Ǿg�u�F``����[�ݎڲo@D}�bm�G���׶��.���cO�}T�>�$���w"�_Yoh�����D�d
.��?{�v(&͍����V���;�@8�q�E�k]�͏G��e���,�"9�c�� �V
�=B��xQ��_��c�@ �J�lj$€}$Eߢ�+��':-��/Oe
@�����h-�z���4���Q�E'c�d�Ak	bg�:�d
,Aڒ��<i��Rǜ-��7:1Љ�ED{����4D��E7֖H8-��GheO_����8�-S���Q�o,$�	L���ݎ֦O(G~�-�����bI�}g$��С�$x#w��[�1\A�ؗ!�^v���P���%݇3^I<�w��B�in~b�s�ʜ��~���
p��,kc!>�'�y_�7��n��}�T��Ѱxi;��!�͂NN.ˣ�=�mY:�Ɩ�����&��I�~!	ޗ�	᰷��q"�ݎX���G�$XX�b�0�"�9/�z��ȉ=��#�`I���y�0��`�^0��W�X80>�"�S4m%tzA.K�l�QX���,^r����O�rc��.A^�9���P�����zn�!B/$�!"��Q�m]�-��"!AMâ��X�(�v�<`���������?(�Jo!�$�/�8,Y�`A�>�N�\��2�?�6����%��Kr�*'��^���[����"��O�_�a`��t�"
�X"�ֆ����M�w ��$�����w�<V����*�[2}*�Ẹ�r���
e�+�`��`AY���0�Lc(�<'H~q/��^h����5���e)Ɵ��;H~J�����W0hP\�@��YO�=�-����챖��;��8�q%�̨Z~�nQ%~j�ˡ_ב�WW�/��Q��m T�<H��/� ^���d��(����H�!v C�A2�C��x7f(�-�n����؟jX�u���n�Y��Ɯt`�ա�n��i���@,$�h�q�P'F8�"1���x3�eϮ/$�߸QCM,�`[�&�Ua�1c% ��lCu$�Pþ�]$7��$�O��y���E��0Р�࿋D���M��;~w����r@,�8�{�I~'��
T�a
�"^b_���7�;@�&Qy���X���P����x�
���4��V�ì���~m���n��8��mS�KQf�n���>a�1b�>�k�**�p�m��9��9��A��H���b[���Κ���E�������;�k-t
�#���H�7���+B�1�9,T�{ĕc�'�-az�F>h�=���0���b�^ ��3.Bb���[Nk+㨉a��]GU�.iz�L��X��=$È��C��<��P{�_������n1�ɨ��H3DE�4��� ��_��������Y��G�H�3��yv]��n@�{��a�N�D��H���ch���ŘJ|g$���-Ho�5����6	7����_�EX��T�I�*�Ҕ�e.RFMT���ũT�zh�v��|�0
�o|�
�����5u�(�*o=^�a5Q�rR��|��V7��}L��UM�|C��$��0�ႚH��xaNE�>:�v"Vz>�)܄��ث�CF0ڋ���N��=�@!�S��CLE���^�ߦ���&P	�#La��M�ˉ����,[��=jJ5�-T��ɖL��͛)i?/N�DMl�X<�|j��֏�'�v�4����@�+e��FPBU؍P/�q��ii��[���P��f�L'ƌ+?؎��5�P�u��M�v{p��	�/٭�0����Nr�0���"ҭ�1'1������0�G�i|t]7���P彆51)G�>���G�����S����W
"FM$�@$À��JH�Q�@t]��I@3�Ň*��8�
0�a�*��QFM$�@�.�Fҫ�5�Iq�0��7�A�BM��Z�ad�02Vs@$��/��*D�|���$��.��x�P��-��
0���ée������if2��!*���@1$�h��nl$�����b�D›	0j"�h��q���H�Q��*N����K$���S �Z�21%@O�6���4��h���<����uݠ��A5Q`^�u��j!�b�~UwI~XB@T�U<��{h7�PFM$���E�bHv�	0j"���K�م*`��`i�O�H��xk�.{v�FNp\5f�\�����	/��"2(7o��[�m3�@e�?=�w5w(�����&`P�st/�!=`�D�MIgo���v�G�'�Y�ܘ��j3�,H~`a��zOlKy�PM�/�tHp�y
Ucΐ�k:m����8c�Y$�B �5���	$�\�9&��h���$�)�����I$��a��0�d�;��O�rDZ��k��/�3^^|5^������d��-�r;8@��*^(`{�]$��#�!$�~ �P	/ @���j_��N����n����!�O=�FyQ�p���"�H~�4Ux�L�ƶ���X||�f�-��n�����c�/����������qbp
["�H~��x�
���:�P�!����y����}��OܵI����}��$�@p$�@s$��a$�@P$��:�`� �_ �^��`��_ �^�<�`��_�9�^������FYq����Ȳ^�-N��4@�_�>N���2���p�|���$h��80mo��u݇v#����6���/�J�~`�.*��Alq �V�&`�^+l�
b�`[�`+P�_@[��V�*�����K�఩
��n�w��>T����yL�1��(���42���@=�8��/��F�o��q�
�������h�D`w�@aT{XB���/P�8�_B������ P/�0���!�nc�O�����6ް
�q�\�6α
)��'��@$l�@6l{N`��h��l��b�����H~�H|d�6DǶ�q{_���/�����b0"�������ȳ뺇v#��H~�_T�\�����8��Y��#=^�C($���/ND�?���lm�d�$��"�` ‡+�k���%�wLs�cl Fb�G�	{_ )ƌ�1���t�C��4G�[	qj]�
ڍ� �EZ$�i�l낦i?1�p$�p��)�b[xTv� ��wH~��oH,�N���Qhp�������#W�H�C!�$�H���=�8u%�(\ �E
$�n��&D"�G��:(�#��3�F���e��nP��+�D�!����b\<�ۃc����Ea|]�\����YXD�!�0�7Wy�
�OT�} �"����,^^CU�}���*���Ϥ�T�#�EU�j���Dl�%܉!��9|��8)�*���A�H|�`q�5�l
۟��E
��@��H�� �*+�F⫏�ސ�@5,Zp��WI/�#	VG,^p��WI/�!	Vŋ�h�����8�,R:>H|Q�u�P�5�|Ͱ���)��ۑ��B�i�k��`�<�l��燑۫�"F+4�@����Ip;����_xA����&�n�
�@���M<Iz�}]�=�92h�%:^jFm,v0�ķ>�^��a5�����/���:�..X�6h�%��#�PL⨡����n�Q�_�3���͈��(�*p=�X��`��
��@į:��$*�0���
��@#T����ĝ� �-�j	��!��S�(��/L��݆`8�P�8�p3�"��PG��,\�b\Y�8���:�;�â�E�+�X�;�U,Ű�0����a��`��>dQI/�˔�}j��;b�b�@{�� �~���>b �`�����Û�s����$�h�`u��`�}ӓD�0�_4�K
�q�;�t3;h��1�$���)������w!�!F�(
�!񽎠�A��l`K�`����5�y��$Ü���n|`��	*�P�#n^��ޡ���<I|��H��I#�!�EU�K>��{h7�>�B���3��]%���{G�����G�{�����H|l�,��x�=��
��P�p��b+�0(PU�����c�c�ƶG�{A�Y�(N�9��X#�EQ�8>����/��H�O�|QU�cH|���㈻�Q�E1$�����)�q�/f$�(��r�)E|{!"$�(�������^~��/
�%�CH|TE|H���c�!nc��[ô(@u��x�"7*��� ��/��H���=�ܘ ���5!Z�ϯ������Xh"��*y����Ƣ��]��nt���*���A��
>��G��D��xT��S�xPn�U�v�*��^�]���,b�>���B���h�/�H�oL�B�`�
/��ۈ�yP��aT}�08�S�
�b��6�Z��cz�ܘ��v;�����_B�w�@��#�Q�F�7�ppU�ߨ���=>*�x���oGAP^��8ޢ2��|�����(p�F�/Q���}�Ba��oTc��/Q���j����?���]T}"n�n��9vQ������E����b�8��6XB� �=d@�=��
���@2O�XA%<&�_�B��A��R�u�65Q��/��E�@V���G0T~�g�E� �A�F��
@Y$�X�x�o�v@�ο��-��c�oT}���@�"º	�_��G-���"2}�}PnPw1����7���'ևo�1P�����iF`�M���0X�!"��O8�v�N��ȓs��#����D���+������?�=�ݾ�H^�^~K�����T}�;y8�5�5�;*��񢛈p��1h7��;�七��b���7�X���H|��]�^��X?�b�Cbly\�����7�O�h�nI}J'%���[H~������&�����Ed����m�y$Iq�N�%����xC�������_�C�P�-�r��w����%��U_(/����-��Y	��Jcm�l{H&��e�z�ha�/ܩ$Ý9��x@�Y�62 ��$��o#@#l/�y$����
P2h7xgLJv�����۠%s���_��u��6�,��K3"�6�^�8���ILU�O�vh�%X6%�*F�WD���7�T~�H��
��`ߏG�Y� �/��JYT��a٫y�؅w�;���/"��y�dM�6�����ʋn��hbK精��H~s���ل6��+��z�:�~�#�ETT`��D�'\���$��e=74k�vM	l�_'�W"`o$ws�u��maI�y8���$ݻ���kw�Q����n�*
0��
(`��>Fu,��w�T}a��m=��/*��X��c#�z�?��
1��m�e|�–�kt���V���7�^���(�?q!��
��k%��x����Ո-0�5	VP�E$�*�Cy�=`8��7���[��-F^.��4G�&P���K@F�%>���MKh�_D1h7�L|E�`ɿ�
�oTʂJxw�?h��éÜ�kO%
��H|ђ��W�
0l��H~��Њ��wFS(T��ߘ�}ٍ7�ф��w�g|�XAA��_�Ǜ�h�i�	0���؄P�E��	P[�9�|�6���Tż���(�B}A5E�O�h�8�^a��¶�`�-����.�Q�.8������� ��z�v@k$��:�csPC��w�JR.>�5��&�8f<싅�qJ4�;�l7/���o���9d4�z-T~�Y��f���WD��$H~�٠��7%|$}�2n���o �����նZH��O�@JS��k��*`��Ko�B�K�c΀�H��,`��lOg-#��W�v���i� �_x��v�K�3|K��$�6����s�dZ�9/gL�K��{()�K����mZ\I|�t��&x���	$Ӣ��3��4'|p� J�4OY�l��/�?eZP��"�H~�‹mM�"wH~�Ѡ��4U"I���f�+$�qdz��$�/��"��16��p�O�့z����=x,Qq4�}�8���B��%^l3��������ph�,R]4�
�
h��n��E�	�-�X�b�L�5L�_ndZ �a03H~����C&��0����pm�;	"SE��Ryd�	
]�}h76d��a���›A���m)��T���>m�r!��o�8>����g�s��?�� �_����dh���*�9`�@m$�԰�[��&�_ͱ��
@$�p�뺇vp�p�P�/�f�怋���&���@uls@�!E����m(�m�"�P
�P	� \F���q<�Fe=U`W������q���>��*���6(b�8���m���2�#H~��Km0�/�x���%�8~�d�*^�����iT{���A��/��8¬����u]'"�v{��
`���B�[ǔ�.����a�Ӣ���q�u��C��KT{��KĦ3k�QU`T~�"���G��D����
$F��/T{�:�52�z�9Q��IoUSE�����CH��/��[�|�Ý_8	���@$��j�v;�*UU��K<K�,l�\` �_ 1��V_���]�=�� �����v���H���U7��G�� ������H�jogNt�����
����I���S���mW~��"\m=/�q��	LI/իʴ*�|������q|Y3�{p��/����Aq|Y3��{�"n���H~�`x������1S��/����A�B[[�_l;�}�M�B���_ ^hk���Z�7�S��v@�����L��=��-�B����š�i��C�W���y+�C�!~"��a�C{Q*��V���1$��lqPn�,��uLU`�/`�/`��C�v[���(�A�`+���0j���U4f��R��擣�=$��A��U�nK� T��	P@�¾^U��9��6=��"�`_��,��a�.�� ���W��W�����|Vx$$�����K����A��|`���dÂ����1]�}L'p� *�@#��f��Ob�hH~��x���9�6Q���dza���rY� D@�Fr`�@����H��-<�b�P���2_��P�TMj�΢�@��9�1�Y�`���/p�%5�I��/���/p	�=$��x�T~����9�Fp&0H~�Hz��6���~`��Hx��3�8
�UT~�
TzM���G����X 鵭�6h��t]�`/0+H~!�u���?�D`{~��2�@�˼e��MZ��">C���*0-T~��8���n�#�́*0
T~�8��iq%�joBT��D�a��%"�v;pIonT��B��,Nn�ۂC����P�/B�_b�j��tn�C���s��D���y9����]�^��U`5��.���/�����<���~��<x��*�p����Q��0}l������߱2���v��@mô�w�n�5;_dώ���Ep�TC����DwǑk��c��A�@
_�ƻč��=^�p�/��8�콣�-	�{��,�_��B�gZ�cx!���_w�B�AWY�cx!����2*���M`y	�8^��
�_W�B�	��0��s�
`�/�3^h;�t�J|[!����!Tzϫ����<�B�Q��[.��d��]�V$�����V�)	�5��=$D��)�:vA넔���2�h�����A+%���:�=��/>Tq�v���#`?0��/�[xZi�g�F-�?a�/�Ԕ��&k	��n�w��"��ac�8��߅/�2��C���d_oSb��ݎ7��q|h7"
��q���q^oASBi=�}��襭.�R��//��5%����8�'.����x���T���2ϖ7%��v;�`\�Qsld�3���җ�EX�Q�{zˋ�0��S#6g�HslO���p���5�dOoe�J��,}q��e�K��k�8����QH|�o>��ݐ��.Ȝ\'C�19�����^d�'�|d5p�&*K�Fs)K_z@�W�]\0[F�Rɴo3�|�$�8
�����8�wP5�/�ˤ��6�>U��/T~�Q��>%�b�2�mdX|�a��s4	��'�_���m��؏����+7N��L;�#x��Ň2�4F�{�.j �mg��$�?}���`�=��ƀ�Hx�ʼ��(�d{��Y� �Mh���x�����m)�Os��g��Y��:b�d-udXd+�٨*ü$�ۑ�#�D�_�'#�H��Z� �!�EK):bZ�z��E>=��-�q�H~m[��S�o�����#�����/����A�����Zp����G����#DHta����͡:�8�!���F��������	��Ў�v���V���Ubs1%�"Ts��D�_Q���[2�[��C���j��A�k�������ڦ�?��O\�#Hr�.C��`��zH~m3�?�\L��v�@t��WTF�rv������y�_��\$��\���`���m9K�.�<���SD���ˎ�$�~^���,��s��׎N�$X!�5�j�[<=�Kђ�-$�v�����2�6�qG�{j7�O�����h7����^��g���n��H~��s�yyk�8��N��I���v����A��Կ�������/2`k�#T{S�
�9*���#��!ʉ*0����"�?U^_?��B�h�I�(x�ͩ)�ዑN�P�/<[Vyڍ�9T{�U`հ��7�91���
�d��/����8�_$�8���B��
n����'|r�}��׿q{�҆���O\���-T~a�sQ���{����%Pp�/�`[C0�І��>����m|�" ��q���
lk�ڠ��N!�E+O�5ĵ���k�iq���=�&G&@�K�
�Q�CO�[`ؼ������|��7�8��O�X�Dxl��8�!�)��^��u]��l���x{��뺎=�8��7�h�9���p[����"�ɑ�b	ob�̆�$E�����$�I�2�"	ra�onO���/���~1	(-� ��|Hx�I/�Ӽ݇$���7Nh�$��k$�@\���]�u_�i?0���7�e��n���2ಞ$���ׯuu��� �D�C��}a�.c^��B��+�{�#�K�u�_�ػ�Ө��P	��kǐ�Nol�Z��=�:."�K�uG��"��-$��-��Ƕ��xI
E,>CL�4��k��O���HvQ�"��n�C��$����H~�"�E�8~���}�6���S�>
�}��.�Y���k�@s̖%@	����U]��E�8���@L�9����H~�=��.�%6 -�
q�����Hn�ez��k����q?�/��"��ߧ��.�q>/��Ŷ'���{UF�s��!��>��y�n0����E
��,��h�2|&�߮�$��`�[��.ͧD�pS��ж8���
J�9��_�������\6�%�%��@�\
�I�F��W���2��Z~<���y�DN~��V	/U^�}����o�Nk �����I��$��^������H-�G.�f1��5y�~R9b2�D������"��E9b!B[&�|{)���K�D�=���/RX��@��}-�vc����?��",�%��8>
^�7��e�;�oP�r;	/T�L�y�7�$�]�
��.a;���'��n��mpe��R��~T�yi���r��ߴ��d�ꅗ�l��!t�6��/����Ua�a4��v���(�SHp +�衩�ܛ*m�x��`T�>����<$ZN��*��f��B��ѷ���������o�F#���1Nb�+��	/aa��+���W�5��"�c�[r�Pӯ7#��^B�%�yH�\P��R �����[��:Dv_>�_���:�q@����o�J�> "�9H�<c��=3�_�A��4������G���A�ge8"��������~%�]�=�Q��HؚpA��o���6K��|~=-ϒ�����{Y�_��1m��m���8��[�X�u��G��_���<��r��>ث�r��a]�
�m�mHc3�
����v���/�
3%��j7�2%��v�̰�?fJ~�/�q��I~������8~I�����j��wD�-������f��*ն��Hj7����	��礪�
g���r#v���"�6���7D�-爖[\y�
	d����/�y�$�H�^Ի�w7k�:�����k7��t��L{Z�>��Ց�/�ߠg� �t�_��nt�=.ڙo"�����o��A�GE�)^�z$��E�"�kD�)^]�����/@bO�zy���d‘�w�݈���\�$���yh3v�M�"2t]��݈��!xz�-Z>���gM~]
�=��<���wמ��ɯo��p�/@Y�;��҉p/m�7�qg]�=�������-�J~�&Y�j7�4o
��1ю��uɞ���g��q�D��nG)G�=ɯs��<���#�>�_�C��4�$��;DZ�n��^���v'�A���n@�*�ʉ�W~JrS�
���CH~�Z�8�hNt��hg���:�oO�~�#OyE���赧��4�9��?%�Kb��`������W8��:��,����jy����^R!1�N�K��?D���-�p�뺡�/�>����w��)��?.4���V��)�ӆq���0lⅷܜ)��&�
Y��cjׇv[���3�˨��	��=ˊp�Ǜ�U��R�J7B���N��Z�=�{���î$����l��4F�{K(F�p:�
ڱ�v��$�'@V����I�Ҽ'��!87���9���Ȓ�.u]�!T��ujK� �a�^�>Z�J!��y�#����N�w��	x
���i��m��v;� ��j��;�'>D<���9=��� �K���ՎH�����8���|i�p�cz�;�+�)O/'bE����o$���_��}TB�{�8�M\�6���ou �7����-�@i^߀����/�4,%�Ӗ�~�?͟]�S1XYPI�u��'�=��f�Ζ�}���Hrz����?w�j�"��,�\�i_��w�:�x�C��	�-��q�D�Ɩ��G��{�@�e?�L�F��BX��f[�q�+%�{��p���I���G.����9��c��ZU��x��{G<��?徵?%Ҟ���=;["�	pU��lx�U��a�oZs��P�y�z�oH���v�;���E�z���R�$�]������k7�mC������%5��_����{G<W3�W��&j��q��Iһa�������<�
�����~��"]ę�A��?�3H|_�x}�=H�K��hy����!Oo��
5~��m�TLR�=\ �F��+�5�&^�Ab�n��T}� ����',@H��1�U��x�
@+���Ğ߇���gK��Sz�f�G\7�O�B��y�le*�"(Z��^nK���J�@uw�t�[	v'!"����"��J�:���&���:1�x�"��;ם�/�J�͖���ߺF�0��L����p>H<�y
�BŲ��{�uC�����`�oL�vo*؅��	G��y�݀��;�r���S���.Ȣ�^����R?�cT�ߝ��K"�z�ľ�?��Rs4ʧ�K)x]�C�S�i>޽�l{��P�g����T�s$�/���ԧ����ZE�
[�g~"�}��n�
T\�c�����z�XR2���l�(�Q
�W�"qmD|��)�����P�g�&7"�R	'<�+�X�حp���^�-�Ho�<�ɹ�?N#h����D��%�;{~��(/���5��{�vTi\-\#�9�x��N~��?���MѠ��F,���
��t���o�g��74h7��E��"7�E�_�-�"*1�yx�~�M܄;V�M�Ho.|t]7h7⊠���J��;%t��!U���"��I:J]w^x;��n��H| )*�y��[��ȋ*d{\s�'O9A��g���[���$Ğ�mœ_^�2g�n��
8!��b�<������z�I
JW��Y���da��C�W�$����_��Q���i�/E��D"����S.iޕ�=�-�v@;|�g_�䗗��a���
(h(��x���/�=��@��lG:�h�ھ�H�L�<�Y��ȇ���q́?>���iΕνjV~��?��T�`�����L~�g
�q� �j�/�L�aC%���N����(��o'yP,��(��k�V���v
*^����V���K�����v��O/��Hs�F�U��ˣvV���X�%��{�+���|����J�A��k\g�/Ok�9���H�רt!�s�������5.���#RjrC�o(�C�QA�vN�PP�]��������R�@><�������n
j�Z-*�TVX6h7�m^_S=��:`�S�*�8�뺏?'��U�G��%��^�Q�G%���r�~��H~�sԣV�;����}��A�G{�R�X�*�����X�q����a�vNpY��R�X��
�a:d�B�MP��<�>7���o��H��uoR���������<��UPl�/��Y��t7��q��Z�G^�+��7?������{��i��4��W~aK�J<�*Yi���P��y���A�G{]����߇��x�#\�tw����Y��
x��A�GL/���(�vn�4q�|�p)h_ ��T�yRc��~͐��W��G�뀧�1�Z�Gn$s�q���<���_��Ξ߼������i�;�R�ۗ��PC��wh�����&�*O3"%����,p��^v;�u��f �l���z�7%�C���J�ķ�����vJiqݛW3"�IY�X���T|C{z��-٫�b�����i]������$��x�$3^zCd�礗X�u��G��-�;/�%��pJ���6�'�/g��D#�����n���E�+���j����s�������%*%��Ղ��'B���Te����)U�I��Etly��꺓��i�����~С���v�O�y���꺳�79��"�ҋ����j�3�B '����^�3���k7࠾�#�E!s�]w��~� ʾ_������]+����ٖ��ٺ���i��.�)��_w�+����i�4[^w��B�G<�B�ם�����7d���Z���pQ����A��O-�
|�@��7@<��/p���\���^w��}1��qa��ȍ�'����ݮc�/fn�v�#H�~���"�ՠ���L���}h�(h�n�Q�v@���{��ʚǪHԾ@>T}_����
A��^�뮞��H~5|��
�_�7O�6�}o��ֿp-j��4�f�&�"�;����iݎ4�4����_�����q�c�n�6w9��`�<�E΢�r��y�����:�nS;�B�J��ܠ��*O�o0*O�I~�r
��%��J��� #��*�t]7h�^+�oȄ����N
�
���@��~��=�Q+-��c��=\?$�~_%Z9���/l��0��A�8��~Vb�v�3<%���=�7S��t'�򡵧�)��npU�2������H��k^wK�ߐ�~�� ���#�bq��oQ�v������ۉZ-�tW9����jeY\O$���l�yE�
�
ޠ�[X��0�Y@��`T����HF��Yq�&�,p ��up]���'����Ѿ��*��pU�J
_��11�
8�\�sw<Q+�w9WD��Gu�.�/�`����Z��P��ET}+���<%����s��Hw6+C�uڍ8+p�)��mp��v��(���u�X�U�#���n@ɪ��ϊ��/�s5&�We�g�[��ݬY��9+j_�'��mq����8��p�-V~�rz������6�t.6G�@a&�z��e�/	�M���Y��8�UH\wD�m-�2w�\w��_w̠ꫤ�"��W㐘U��|K���5+w=gL�õ���.�?"�G�3V����oXӖW<~��P�UV���;�@f*�&2�-���V�|Έt�	_�:�@? wg�G�/�r��_�����T����݆��d�[���lxzK�#��mـoTm�?����扥ko�����|
JV}�q+��/�����_�}��D�Kw@GE��}Tm�_�ǧ�!戵��t��� =��Z1u熸
'XT}*|=���o9�}�1��o�r׳���"���P]����G֪��D�֮����$j���n`U_���¡A�����Le�{"�,Y�:bZ�z�v .��>�O���zb^X��*�ay���q������_����'��-Q�~�X�#z'r@�D_�/x�m�
4L����kn������S�:{KN��+����`�.h�㹃��7F�'�
�y�����Z��&�%rP�:8^��h�Bշ/���V�ǚ���m��ì^{/�D�����k7 �^��s�^�����&3�=�+]�
ڍ8#����C����V_�2�-_{�
�e@l�<H�D��C�������m�Ӷ��0�Q�[^4����/�H|��=�d����c`��v���$�`L���WwL�N�@eT}c�_a��'QƼ�k��Y����A�B�p�4n�c�k������8 .����OR%�	J�v�qX/vl}@�[���S��n��Vw�C$���	��H�r�����T��k�qۃ���z2�v�Ơ��y|���nC&��=Q�6<=��?P�\�oh�Py\4�]�0��,�@����������
'�_©88�5T��k�u����
��I��@��=ʆ
w���G�\��Gy��„7�R?��"�A���);��o�EyzO��[�<:X��������h��S��#�f�+��g�nLJ� �����R���W����bl}@�{т��k�q���u���8��|���A�ꋅA�@!Q�zW�Hw{c����ޥ�ę̸���Xb<�"7�M���co�����������������p?�s����m��U��'ʝ�owS"���Q���j`�TU�F�D��ž��x6��ATGb�?Q���7o��H���C��3u��@w����^a|�$��o�����zLO���oQ%��~EA�v��NO$!�_��և�J�=��T�_��?�x|�>�R�r��GJ~����1x5/��{������\|�����i�e���{j"�������+���;1	R�����Ī��*2���o���I��W��.3�=�8~�H�ݎJܝ�'BE&���M�\��e�z��"�*���z�����A�h�e��i�
��_����8������n�n���A��L�:\�u�2�}��'�ߍdz���I{���{���k7�A�h��
D^��@����!4�&��vDM~���-Q�Dp��v�ʠ݀��u_�
��Ri4�囹h��,��k:X�E��!���<�
OB�ƿ�
@JQ*j��b�DN~�T������9C���x"�3<��(7h����_�8"�X�_V�*�$����/�1Vp��uoe�zȕ��<>B��OM
�
@U!*H�;���'��v*�e�n�*�y�%�R��X��9x]���ܰ�(_��Q���"�	�����x8��z'g�z��Шx �H�'.�p�M��A�y}���	���S��'D>J'��8�mη@y]��B��)�<r�ⵊ�O�����w>j��J��:'g�z�-�h7����A����Hz��*�<'�����+�2=��Q��;�(w���ul�>�G�cH�1�����{~��z��玑��^�4��ܱ�����:+��&�w�Sb�e��86a[�x�o�cG���s?�IQ����7UĻI�F���Op"D�+As�4�o�D��˰����J��^D͝•�_�^1�h"z��jG^!>�����z&–�T~'!��$.���Ǡ݀�B$�8g
�ѿ����8�Hw�x/j%���X�#��W��J�?lO�A���`��/���8E{Ć|<LJH���N�m"��"���o�v;`�8�=�oJ�_t��J�\)lV�J��n-z�`��#48/�W9���5���y�w��n��q���EIp��j#G:��qۃ���*/9������A�6b�8~�������<8���=�z��E��V�D� �ye��?����+����@]?��k7��;XH|!"߇vp\��(wi;��E�\���q=h7�L71��y,�¼W�[.�p��vW����k0��/���<ʺ�5o8+���?2vY�� ��e]gRd��D�[��..z�D$�c�@�
�Q�+��+tU�x3�OXɒ�����O��>��o
�
(���a�n@K.����&�[8���6-E�"��T~�X��Qm��8�ׯT���)9���b�׻�(w�����IK��'�����+%� ��9�D����4��6E苳��נ݀�x<� B��er|s?K�r��k�ɰ-��-�ܟ�|��D��6h�P����^��-_�ߵ�g����i���3<���=��Q�&Ϗ���
�{��(��ר�&�|�����5A߇vJɚ����nq��^�We;���u�S���
��xM�i�q���O�?���-E����UT~�E�C��v��_p'Ě%���]��绾�}�x^o��=!^<���oR�_|�H �A�%{���m$;2|U�s����,EZs���T~wx}!��w�L^0o�n~���R��<'���� ���R��&J��E�'8�Fy�����A�Vp�F�;�=��3�x�y]Y�����E�>)���{!�'��/�2h7���/~"�}#Ñ Ώ=���+ʖA���(�-P?��l�p,81J~�l����@�(��Ǟ
�m�X/�`
�/f�5p@W�G랷�qC�e�"��‡�*j�GT�M�G�֓H�R
�߃2��&���rw:"U}�m((L����	��f_��u��ڱi��/%Q�=!þ����C܅+���p��;�}Vx_3�"����4*�煿��߉	̈́_I����xO 3�h�V,{���E�K��^3h7�6���W��^�����F�{A��$��������ʠ��ݵ5�䱺�^���'h*Z�7�Z�oj��{]��c�D�����4�c�����
���D����O�J�ua)��mT~�	�� Aa�n~����Pwo�$�/E����OP[��`)��0$y!�6*��
�
�-�'�E�V�]$V����NK�;�M�3��aX
�Q�p�S�_�D���U�݀R��O5$�edH����A��)��"���$U�w�pNн��:��r�o9_�`x/������u�C�
�P+(�U�-I_�-����ov�߲�4��;h7,����m(���p[[�;�=!>������hq?�
JT~qE��^��8�N����m(��:�����-�H��5T�%�I��
n⮡�h�U�D����_pD�ؾ,և�~���oQ����@�{�o�;"J0���
.��C(��D�[���@jQQ
5�,��B>ް"ڄ��1Y���
a����!��k-D�b�8�_"�k���(ɯH� 	�D��K�bz�~j�me�;\�@K$TȆ�X�r���1/Xa����H~+�2X����,E���B���� rq&XAF��LQ$�m����M�E��]�(S�oZ�X����H��	kQaT���!"���h!R�4`�\��%Z܎�_��6�.;2��`BW�%������7D�ק,+_�ؼ%Z����pQ�61�D��Y�
@\���Z�8ͧ�+a�C{�vZ���/���|�^�
����Â� Zb��G��.����7��U����VeJ��܀@p����T���nGI�L�d����(�����M��������Ai�n0D�z�e:E)�,�q�%z�j�H���T�$*b,��oڨ���p�_��2�(�����WQ�A1HI���%6��h�K�|Y�͞��~���C�X��Ԥ�f�_e���B���K�z��/�_��k7��G	�/E,
��-E�YA�kC�A��;h�@z�5���k+�����n��X.S�%j\]g�j�_;2U#�4�[�Ğh_����G�k�?�A�RIS8��g�K��3��ז4� p�7MP�f�o�ED(|iI�ē�IᎰ����#Y�;����a�@�ړ�r�N~��4F�����_c�M���=��qPn��2%��Q Q�2hz�5rU�ȁ<j���T�ʣ����T~
��D�� B�PT��DBmƺ��0��o,Q�ډ'ע��l�h�_�2�݋�
rO��+[���`�~��N0.AR�z/��{�v�%c�u��ؗ�	�E
{��l�	��E�-Q�d���=�m�D=�l��?ܒ�7��/��b�� f{��5�cᖨ�]ƾ��ʯ�&M��7�*9�`W��/�%7�G椛\^e{Y*C��֧����D-|d�O��G��=F�>�Z���%p,���S�ư�A�-E�M$�B`S�$)r�'񵉅י�wǛ�$���/�4��Ϯ�ڍ�oT~���R� ֨y�;�l8�_�H~�I���n@�—�o$��Lߔ}�E�G��L��>���R���$�%�m	^�MۯP�u(㣔�/D,M�A��˜ ��F�׸�ϻ�_	˴PD���϶D�q��:ȱ�cO�����2ű-�c[����m��{����@<��u���vpL�IA�;�=���}D�-vm���_?��:�q�������h�DZ_yj7Ǒ��0h7��^���p��ۢ��Ğ��0yƂD�;�M�Œ'|��l�R�5�>v��o��D�$��8��"	��
}�O��Yt�̖l�_*��$����נ��ƒ�*�4�$a_�����k7X#�
$k��t��0��w�8A����[z�8�.P��=��Gfidf���At�,�֍$"��^���.�N�~y��6�Ӈ��`��w�AK���ٴ�ljR�Z���I�5�����/�I~4C�i`���H�!�3�O|��mz&���qK�����N��㈢�r�n�״Dl߰V�W��������?��� �m[�I`���H�a/&AW5=�-�L��:<�������j�rM���6�ׇ���o$���^ߩ���y�ӆx�;�k��v)�B�b�{]��m�ى^N�xa���+�zm!yK�ߑ�({�G�/�^��򆧙�����f�!�&� v���q�C�AD��o��t�����jgz��x���q
8<���|K�eBЙ�'>&���;�[�+~�����aj~;�s�Z'�iwY~��'&<���=�w�6��>�:�{����0�<��y�ay�����c��-z�zٶ��P��K/�	߈���X//�k�ܾ�@�j�C/�߆>({�[�[;�\���?��e8�j�#�^�|��_7�p:��j�+��z��I�\\q����	���v�燽�o��qG߇t1�-����|�����X�=����o�/�_��[�?���A�6�v��<�ė�����	p/�zO����@bG���tt�����}��B௞V��@��o�\<ӏ��[)~#����W�/��Ҏ�(� ���g/��=�7�/�_.�<����M���}>�������^��pޯ�N!��}^o�}���I~����Bo�W,�B$(Ԧ7�z+��[�䗟�\�`��bP�rE�ؓ�nc�a��o'>X������8Uy>_���j�N)&��`\ކa�j"��d��ޞS�>){�޷��JEֲ|d�r�M��Jz���#�x�pS�/�o|p��#k����w���$������b-:B�"����������/���ˏ�Z�:Bt�;�~K'�C�q���]o���n��R�p��[����z��Pt�&�<Ĥ��Z:��;������'�xT��?"[P
�9�Ul��w��?�*�a��H��*���zD�8l���~���S:}�^�݀�V7��m��gI0�-�x�I_)��sAF�&]�g�?3��)�6�Ty��-VtVl�u���xQ��]��xI����7�3DnҸ��\;�wƽ�Px�	���y>�R�k���x&�e��Q������"`{�0�GK��dd_���Xy�(w�!^N������(�G�O�?���0iVc��	�:��W�^O�}�=���5Y}/:>m]�gw�}�߼._�=�c��j�?�B�J�&dS�4�B#���1��f�˪�^}�pE�0|�r8nG}z���lR[�+���D�/�K�w����خ������?�ζ�7�j��L|M|y���%}�K;��/�B��w�]4��c̦���2����&�y����z�=�)���|��h��CY�q�g�=~��05�21as�'jWY�1���^{+��%c��}���؅�����I<V֐����$��b7^��y��`GÄ71��R��lć�]y�g܆ ��	o�'K)Jtؐo����]�a8T807�ӄ����<z����%��S�{��MKy�{Y�7o+�A	�hv�]����B�q��~��(���i�;m1�3�B��S�@&y�Y������&
3��"�]��P���uо �$�p����R���g&��.g��cO��j��*#$�8���+_��G&z?�a�vx�}�F
�_B����%�����_�0�%�hBn��o&����h$?��<����@M&�����擱�8��\W|�]>j�$$��\|�i�x��/H~	�򦏥A>�������:Q�!��&�"A �Y?�[E�_B��4.)P��&�DbbAxj��c��������H~	oI�����/�OG�ߙ����4���8���>��~�]DT~��"a���%�!�ߙ��'���M�l�;�w&�D��t�X��H}�m��d`�KJ^�w����.v�H���L��g��񞹋s�!�%;IÝ擱�8 ew3�%�_R[^�&���P���|(��1����mP�����A�u�޻�(CJ�_��������D|g�{7_Ғ��	�S|��w�c��9�a+&�4Gr�e��;�1{�+���e4�$�9� �n��~�&�4��i�A��V<�{�V�!�4���|�h����j��1��/M3�{�2Z3����Gi�4]�{�n���_dlE�K$/q(���y>��>�{�V�a���I�I��_|lE�KW"�Г����5ޓ���.�0��8á� �g�5�&��(�J�9��Gr�>��k��C)�82��,{E��P?t؛	�j��T�9~]��o)&�l'܏��ù��Pڵ��6�GvQ�[�f��݋���h�Õ�lj��σ�c�d�G���a!^��0S�A���z�O|�Q��/,�r�,�Ѿl]�w���� �o)�8�9��8�䰎�{��y�
	�f�Bp7���R��菟����|x�#y�7�8l"�ķ�_���~�LЦ��h��\sV�;�Kc�M|aK��A
�9dtn�籔b1����F�_�b�w0�Eڏ4�S^���ϔ�[1��;I������j�92�e+�ؓ	�~Z�x�I�ZyvL~�J��V�~Z���n�W��Zz^�fيn�>0����Yߍy/�}L~�I>4�M�s1�ݕ[��x��*�-
Jy��}�Zي�/��!�z$^1��V����-&�l�GV�D]&�u�������䗭4��L�Ch:
��nG==L|K1�e;]<@�7u�!�6y&�u�2�-���t���L��h�@О�����w�'�V�:��,	�T{�m�6/e)�i����K��z��–L~aC��G+��e.w�=���z�_�l8�j�Z��v� \\=�P�"፥�ߦ�#[����apSPg%��c��3�5�
�6�ႝ9-�B����j��w/�1�b�(�ȣ�΅���[[��/[�AE��SI�j��,>��lي�_��/��i�S{��i���Be��r"|1�U����P��&���X��I�{�M�"�� �Ck��Y*|��pq�o.�s��iCդ��Ix����$�l��sX�i�a��
Z��C�8�?����l����֚s�C,HH��7�c��/[�`B"�  ���D�u�V�=@"zCxz�Bp�_HJ
�(sX�W[��BRR`�����B��P�I�v��؊� ��iaIyp�1R`ؖI�>��؊�#�ͨ�x��aR`X�I��؊��|�����I{�1h�▶ʼ�؊�:��W>�p�[ڠa&�Бa&��G��d�=`;��cJ!�D]o<^OlE�S
�Ao<�@)��y�?J)c�q�NhN��VL~�/�B�:Io^ElE���r���)qL~����]�������y>�R�k���71el����Cqd��!?�_���<�$��Lz�a��V�$���A ojz���VL~���S�Io��V؊n�˴G����X�����0�ڃ`{^#l��،I0+2���[Q�l�b�=�,o8���/�-�x���s�_�"�v3�ےk���$���$�@5�M挤�/$�l���n�籔�Q{T�O/W����_ u�}��[L~ي�_ u�M�\�����$�	�yy�䗭��i8 ��z^�f��VL~�t���)k`
&�lE�/��0絣j�c8��2���S�Lvي䗭H~�f�u�x�=��Iy������9$��i�
v#�e+�_�i�0���3�5�� ��$����T'�e+&�@�L�K)��䗭���鬇�	/a���5�g�{7Z'|���/��/���GHxIG��VL~��R
��H�䗭({x����#�g%
�ڃ�H����ͽ
�0��ς�H~ي�`e�;Ow_��H~v�ġ9��tI��VL~*�rh�d��/�1������_����u��8��	x�r�ᜐ�-���0 �/@�.*n���e*�����t�!�h�E5�#
ơ����U�@��Eu�@΂bU�?�TtV�+ԅ�M��$�$�؈`R���N@4G��$�.ta*������?8�+�~r,E�a &�/�5ջ���R��0P��h�
^ �����	�f,U���S��ձ�1�@��rV�+�z#"��R�p�0p���N�������7�/��T7�S(<U��/��%�}/�y��J)T	@{�����M�6���tM ��	z�a*ZF@X�_�y��el�f*.�����)U��H��@��P�@ SQ��@�T���T���:�/4@�@����	 !a/���������
�_H���/ ���	���*����r
�����]	`'�<�)�Ꚋ0�`#�{ 
-"h��V��қ��`!������
 -�/<Hu/tk*��HD�7��~�& 2�/\������=�`����i��ڃ�/]��h�lg*�P����|���	`7��=��<��<��~�z��I�;`3*hҲ�z/�^ ��N�@3�@#��B�@j_�q�`�&� %������K���}΅>\�=T��<χrj��w�0o�@L�_�Y*Y>j� ��0�ڃ �/!��
`U� ��.�lj*����8�@���u�B`������<��r3�\�5��?c��=��lj��C9�� �
��:mRrI@c�}`5�:��������
*x�*_���HL�/O����y��k�ǩ��!���=�Q
������v(�����
1�_�0o��τ��H�/P�TJ�s��2�j٘����}GP�R��{ �/_,a�{��;���w%DV��BK�@���R�v~4�U��\���p�/U���@��9��[�չ]6w�*���*�tJ�M;�R�0��〻	��ZB�L���|�	S9�aP�KW�z�!?!0�N�����Q�CJZBlL��0�/�&��!<!0�F��
Z�&�y�J)��Y�a�M�O�2�/@C\��LE�M9�
���+�4@������&�ݹ��_�Ą���c9UM�ķ���S�#x�� !�/�j*�y�
,U���7�$x�� �/�l*�^�"�0�Fp�/@B_x�Q�D'���~!�L�ѣh���0!0��_����p�TNU�S�q�b�˩B���� �/�HU/���:X�M�0��@�_����^��;���T�,�>j���L	��n	*���`o�`���H�KDŽ����K�nv��/��$c��	����Z7���<�s�Ҷ��P������a����C)�X{L����d�=��62��A�K�g�����p8���i�Z��h��+sd��M��N��@e�<ʩ_0�ƥp@S��+Z*}�%z��+��(���&V�27c���̋h�y����/��y�חL��emS�����ޥq�B?` 5��O�ח�g��̟h�*` ��Zv�-\��xV�+�`W�0Lg��Cd�1���p/��wR�BR.l 4s,�r�
HA�p���/d!� %A0	i�&���۪ID�	M���� =���y\�}��c�_Lz��*=��d�l�G�p��n7���B�/ݸ����?]D�X���B��O.h�E`Zr!��(�}	�x��a�=�f����S��9U�@*����% U"��;2����rL�`�u�������;�/�� �
v`�G �����@WT����|� ��]�.�� U��!����S�Ae�: 8�E*3_6����<��<�s1�g���T{0��ax�a(��¤��y���I�/�$�T�j���x/��lلX��
М���$��F)*�sK`5�>�8k�{��:�%|,�/S�4AE;r+3tli	!�c/�������s�;y[�|�бa��J��ph������Ru�NT[�r
�8}<E��d�����C�Qف9*��/��K�ؐ�
�eB`6v��P{@�_ 
mؐ�X��
��w�)�8��f`s���r� ���W{�~�4�~��
�0-!����O�����a��<.�}��c�B_�!0���P{@L�!9�ʴw�q�����p�]���o�0L��!0k�8��b�ˊ��@*�¬�\(��@����*] 5!0+1/\��wv���m��m�=�W,}[�n�U.�T�u�؍��h��2+0_�����Yv�MdyִT�����aZ.�2��Y��J��_�
}�x��e@�̣yŲ�t�C�΄�}�˒y�95tD�`W�_�4��
�� x���K��_`7�x��i�S`��TCT����7�/�m�0|��c!���n�a�_`S�<��_����ax+ZA�w0�ME�����G�q���g+�
��a8��>�/�	�/2�X�99���q�!�_`u&�<B__�m��`h������<�(���مpp˸l����r����Г%>��	��!v��U~��޾��<��i
�/�rG
b��PJy�=BCr�>/�r�}ZNd	������8�4�/wx�a�=~�|c�q�
`HJ�/��/7|^�6��-��p���K� �����ܠ� 1U���ΐ��x�����޾mp�C"�>����4�ep��]��B���h��0m ��u$����2�/\�о�2�c�q��r��_�&;�\1-�:��g��
b��r�W8��1��$���}~$��
�/@疠o�=�!q	���#]�_>M���I`.�'bR�|����*k.�a8����ƑΘ�p�5g�! m�/��̛I���3�KE8���k��m�<bp�YZ�M��A�� �/PJ�����b7��"8>9U
1��K�L�q=x��:L̸Y�.g��S�V{�c)2Q���"�/tn9�3�U	~؄�R�^T��t�o��.���M,wI�ov�iS���t�<��+v$�T ��N�xu����Y�t�:v&�����=.��M�_ؗ���}~�'��*�_ߖb$`'^����]���I׬M`'*�#vX�fr@(*����\BlL����k�_BwMq�@��x�=���[�P�R�lH���[�_��a*�^i�s��줪���4�	4i,!�•i��Q����G�@:K�8؟��;+а���X{�ˮ9�9��'�؆�_h�2a+���0��ro�Ty��]5�
�/��Ny�z�	K���8�ը��O�
���q����y��O��c�oL_1�����<��:.G�x�/�˿�X���;�7�"�/4�%oݙTD<��,@e�l��(w��z�}����?	��|�x(�����
B�P�r
�X{��w�c'�8T_�<n�(}���V�8���\X��_h�K޺����<��&�ɸ��޺��V ��v��.@��Y��É�*�Q��sd��V���>;v)����b���AT���ඊ=!��;��+N>��}����>�����Xi��X�B���l�*ox����!��d����%!0�>GtB�<��aHL��~h��]��vI�-�E�D�$��o7�83��XJ�XU�6�T{��.�u<A�I���j1�E���`E
}�A�/<�CI����
@Ӌz!0�J�>�c�q�9տ� �AB��w�@��y�����RJ�X.�k ;����+<HE$���~�^5\�{�J`�h��
�Kx��_HFuP7T.�����J�^��x�v�������]��v���}���OSDѾ�� G�!-ڧ�Ћ��;<j���~�?t��op'$b���yBߧ	��h�}�f�>H��}&0@˖6c�q4@p�™.(��;�I,��v�=�-��l`����=�n
v�������[�LP��}7��K+
�R\Ѷ�� �/�1��21�!��C��9!�	!�������<
}C|�Ȣm�:�
.�\��<i-!�޳q��T�\�
�S�P���h���7:����m����;�/�C�<Q �y�Bߔ��@�[4O����x���9�d���c�q���a�ڃ��RT#$l�u�L�/@=�~��\��4��=�j��81?�B`>`͛j��~�B�n|h	�n9�0U�Q�
?�Bl������q�[��Z����&�����`3&�@g��~��/�h������,\'���,��f�	D��/w�h�{7��!���،	'P�~�<I_`�S��}ኡ���E9mz����R�)�c-S)��{dd��4k-���`g&#����e#c���I�v���! ��f�	lni�/{�H�@O������[NU�c��П���UP���pA�1�W{lC���HK :'�.!���؄	&���*_�NK ���؆6��� �y��|IHK �0�j�j٥ԧ�A�0x�O[*'���M�0���/��vY{�?*��ځƩj�S{[�B<.{k�~���\�F�>{j�˘�!���؟*_(���yA�j`�e3���@U�U�p�XT��j`K�_D�@�*�U�p�����&��X��`S�1��|8��+��j`��^{��`{�	�cU�"���*�gZ'�؀*_��X�U�
pa�=�%$�=o�0L�lo�@@A}���ݬ��4����_��Y|B���:~!��#������/��^������\��9�����|!-�@w���r����r^
|�=�-	!GbY_U�жwm!�a������=௱�8�u(��У��^O��ax�=�5�����pF[�6��D ����pm!�Դ}����$m!�tT�lk�=@[`U�m!�B�lh����W�:;8��ڃ����m�J>�B�Rʛ
` ��@z�<�u���@�/��r�Z�D6�ʁ]�N�/��>�@b���A��w�����w�{�!}�����[�a��
��ߋ��/;�D ��Z��~����X��
����._��l,gAp���]|^��=*�!�6��_��3�������M�_`UW߱��7��w��,��
�&��/���۵��a�T�� �K�<E�KR���������C��w���y?���2-!HJ�����ݭ��%I	���_��/�M��Ϻh	��?V Z5�D �J)_�l��g�{i	�?�����$�Qs>��c_�yk�gi	Ar_��y���'���,��,�%�U/pۚ�h�XJ�@>�_�<χ��2:F��j`0�A�,�؄�Ш�����x������ѐ� �P{0�W�_h�g�^�/
H���Y�k�^�.�X�����mK�;��(�n[��%
9��	�J����y�[\�F����h�X\U �%��4��*�{� ��E�`؍������x`%�|������@W��������]�E5�Ty8�&}����D�_�H;��wCK5�gl�Kk��=�Xy<���v��P廳a��i�>���/lL;:��7��j`�;���$��
h�@GT��8:1U��#�/�䢺W;ZvT囋j`:�*��IW�9���:o�p�=���ΌEU0����\�6VlM[�F]TO��{P@w������)��u�%qtj,����.��SSQ��=m!蘪`�$��{Ku�|���'_U������EU0��~�G�@w��r��=&ؙ��M[P@^�_���h�봅�U�����$��_�:�	m!������N&ս�e�T�J9m��t�P�@Z���ޱ�����>��_��hq�=��	HC+�J_B��?0@]�_º�j�'ogU�S�-��!� a/�j�����<`C�_�:��[��pI_�u���`�_vu�6}{᫩��Kg\�
�/������:�3��"��pF��������/>���#��%�^x�Q����(n�<���<D�O9|��-���w��+a/<m�¶����^r�����Jн��=��y,��E������O{(����xjQ�Бy��y�?Ϊz?���1�R�T�B|KE�"���a?��c�aAVS9|��B A0��/@C�����k�"�&	������u9�f*_� ��0j�"�$��lj*_�\
���Cx�� (-`sS��X��7A0��� -`7+���#�@F�_�
T�®���C\g�p��Bl�A�O��p��`c�z�
�/w[��s��	7.�����#��lHU/��(��g��O���_��]�����&�2;|�CKk���;h���a8��6���	�l��<�
Oy�D�(�K��� x�<$��_ ��|
|���!�B�K�!��Qѫ�� ؐ���˅m_�u�2�����5���k�Q�����q��Z>����O� �1ޛpA�Ԥ/��1�$fuW.��*	HF��i*�������%!0���b�=& >�/�5�{�L����M��C�lA�^68�$fs�C?�k����� Q�{I�.���P���΁�$}/	��Օ�`�#�_��սB6�P�{I���,�j�	ؖ���T��S��Љ�C�K�!p�w���>�/p��em_�4��G'��QLM.��6	�������B߱�X*S�K〬�c�;�/�Iu/!}$&��`�� �/�a*�{	b��+��g�r8¸�
v�'&��v��%��K������CL4K���`-" �/�c*�{	�"��uZB��� �/�v���%���B�m�Lh.�������y+�A�KD���N_`R�rq�066��fY������#���)�*�~�|�T���$�,��h�h	�iIJiIr�K��C8x�+�&�@B���7
��V{�a���-��6x.�;�`��C��;���l�>�c�q�K��̻C��2�E���%|础��T⑒*�f�I/B,dj��
�K�΅oPN����i.���̀�r
[>	~Ie�N�E�ۊ�9�?O�:�<���VN�Z�o.��+���`z�;��K�h����*�T���۝>�u�~���/�X�~���
}{i���Xr`miHO�IC�.������e������0��@p�^��ʗ>����h�0�����w�d�4K�/O��~�t��0x�l�F��z /�w�B�/�9�+�22Y�i*�X�XNm!J�Τa*�h��J�jh��.��ezӕ�0��mQ���{�B�/�n)�m���
�|��go�IF.+� 2�o�T���U�T6�UΩ�N,���/'s�X�}HJ�.�7�^��ʗ�T)h���K�y�J)J)�m�`��w����*_�I�%��O�[φ��g5�������|8��B]��LE���	�Z�-��7?�o�N�	��x�@`3ǢW�RBM�a�B!��֑�8���؃qF��j��:�!���o~��:�����ҧ�$,Rj���s�+PLE����3�C[`W����u��^5�@aq��Ղ0 ��{�G�:�����@������*��aX\���7�9���
K�^ld�#�6#��O�[�𷊇���`�Е��Ȃ�������J�����NxǛ�����؆������L�����Nl�0�gA�������/���l�)��T{�j�0��W��y��yQ�P���~,�0�����$��ӱ����j
��BXc:0�+A��^x��~|!���`u��9�2�`uW߱��Ekk���/@jׂ�C�AA�м�������y\������Э���U�8�"��ʅl��/pa,ZC�����K)�����%�y/�^�wc9U��Tq<��K��|���_\���Tm4P�{�p��`;�^�l	|]�l�CD��/�:�^�f!����ykS����<��|�=�޸~���� ����Vt�"�u���,�T���Ϧ�bk	U�����8��
1�
`g�>�˩_�9�� �y��y�?�*|��cx�X���+�h��	e��@o�R�m �
;�0�hn@&z���h�Oռ��~��6�C_��
֢�h�TT�BZK��^�q(��.Ϫ�����Y�;����r:yI5/$s��w�<$���j����b'H�hȕ���k�	 !�!�������\������&�g�P��åq��Hn�0��.�䙉��p���k���0�/�Au/@U���^M�?�?.|֦�:�� ��|�4n���p6��� 2�/�,!/th�${/.h�d�8��EtB��7SѮ�7��ᬕ�G�d�Et��Xۄ^M��?*@�R�r��X���fi�}Ѫ�F+�h���b���s,�I�5Z5e��q9櫕�����-� ����T�i0ϳ��5����h*�����p�+�����J)�6
���`c��\��P��X�uME��@�@c@(�_��T�hV&� ���Z�_�p��_\��
��XJ)^`+�^4a0A�C��a��w�&�K|�υ��3��	{��QA5�C��������S�T1��XNA�Xw$�0�]�C�������^	w����cq�
�C�������V	w���p��LE�`V �K��?�Kx_N�_�#���.��R�;a/�a,��9�VY
[H�2���#�ԡjHi���W|? ��y��7a��:�N]����+�;.���] 5U���XT�)mx��]�Y�z�Y����LE�]�#KU�{�@��r�:�X���?S9���u�ni��὜*��=�" ����r����˿xq�<����Ҿ��
xյv�D5\��Ŷ&�����
�A/P�T���B�R��3�J|���j
^5��a�P�@]c9U��X6�`W�_R���E @���i�A��y�GA/Ё�|�g�0/�v	����&@+����"�(�^�_W�y����&��I�v�C5�G9U��R���TJ�n���%�\�h������o��0�
��1����{oޱ��zu�JX(Щ�ڿ�,�	l��vk[	cQ�К���G�$����~�yֶ/�ky��7)�/�6!/��t
�Ar����s��-o�0L��#��IS)�O)ejm=*�I��/�I|����"����Wg�?{���cI
�r���sߵg�j�u�U�W�^���R��lh*�����02��S���R�+� ���2��|�4ڱ�RÐ�X{<���x���9�mѦ�ڵ��$'��~��M�j�X��;R�w���D)*���7�����Uz�@USy�ǰ$'�����X����[�{(ʹ��Z�
lG��]���$�ݏg$���m�w�i凜��t���_�@6g��	w�:�2��~�a8�	o�=�D�.�묈�����T�)��x��7�y�.}#3�.�6n��Dz4C��Y��R�C6�[a��_�.=~��@b���K)�ڃ ���\-�k�;�a�g�>$ ���񷿨�.}k�V@-K����NE!�B�$2���'��S�A�K��W������v�*�-��:N�a�~�����<�263�]���9�u�A�!ˈ�ֳ���d��M���/@>n�
����$�yT��åo@)��t�o�+�u�"�b�=�0�t��Q�����������	���2����	U-Ƨ�/�ﮬV��y���c`w��.}��	�����w�f�*�3������,
���:�z^7�T{���X�����
�W�����.�}4�������WY}�<j��|�yk����{�Fm�Oc��G
t�	]�����9������~��0��
s�a8<��a������ 1'��*r����
�ħD��9I�#������IO�����`���p7��@)ڈ6��W��<�����Iӣ������j;�<�����쭔�_[��p���X��_�؞j��j���o|���3��R�T����$��n=ۂ���W����'.{k��_��X{�jz�?�F���>>=[Q1��n!�����SmJ�`��5�K�y���aSG/�8<3�aVɱ�NN�+�}��������i����X{|���YGog�=~��o_�oG�y>���U�_�d %���j�Z�l��s�=���m��� Z$����,oͶz��:��6�
��U���x���������_��jy�m�=��ɉ[#o]��wZ�������?�а�����ᯇ1� �UN���j����m���P����������"�u�U�<���@)��ԡ8�*�5��
����j����_}XҰ��!�~���*�Y��5��_%�	��"S4�'�;�I��L����R�T{��jQ��}k�RJ)jr�|�<���~�=�	h�Е�����T�Beڤ;��#Ö���<�[����a6�p�g��WlC�1[���?+��%mR�Mɀ�����)��d0A=І��]��g{�e15��[�{���A���1�?�b�y��R���kY,~�6�S�;
�0���΂�E\���wJ���{��E>'���[��<+�V���y�E0@��mC��&[��-E�~c���]9����a������-�{|Q̭�'�ۧ]vQ�*�`W����Go@Zt,�y7c@��a�j�5��m���Q�Kv���/
VU�nh��Wu0-�s�o�	��?���/�+�5�a�=��id@b�����0��X{�h�
���>����=�Y�F뇺�H ���ߩ�8x�2//&�X�l.�-�T�Z�Cu*s�j���m�= 7�t~�\1��^��@{��5��a�= �/�o����|�|����o\���]��%�>V�G"|;uO���j�=�ص��~�i8�Y���J|��F�`�����y>�~am��߻�W�~c�t�s�^Ϋ|�ڃ!����`m��m��n����/m��g[�R��
[�Ѹ
9O\���^J�H{�~V�9`#��j�����9�g}2���hsR���.Nc���ځ͜���j�����[���1m��#u�$�U���E�]�����rn��<ς��yU@Jݷw��r�c�9��g[�UK��
�A�EPې��5��ٽ�o)'�~|��S��<<���w�籜��Ls�c9UdO��� �؀oq\{�-E�~�i1բ��H��rO�y��R�G�7Wy/�|\�߲��7���E��#�$�]�~���V�^%��@��EQ՟� �Ͼ���Y�<��<�_Ғ/�}�e(���l�I?`��������z��/ZZ����.s�{˩�w�<���R�E<V���.��K2�m���u{f>�>pK�6�͔���y�"��X�V*��%Kl�����Z�}�иi�X��WUc^UYp.m��E�ks�5_��k��0T�)�6<a�6��+[9�u���]Z:|�5���ڃy�^���V̽�ڻ �v�;U��s���z�B�r
~SU�~��-����"8U����7�E��7[�J�2V�@Ҵy���k��sc��#x�<��,��u֥�i}2��v�t�]�[��,��3Ч�埡oQ��g��g�I`X���A����N��}�T���.(@2���3&
���b�m��@�����kZ��Q�ޘ����d�=67�@k�|��fG��wa�'���t A�Q����X�>�tf��
�:֞�$�F��w�烋ܚ����i����p/�Z�!���T{�E��
l��-x�;��օn!��D��SJ���)��S{�e�=���hP���/�[,h�"�M�����Z��0ᯇ>�`5o�p�޾]�/0m�j�QNkU��u��w��1/�
l�4���})��;ztT����Iռ3Z�KA=�j�UL7��V|R�G�9߲a�` �����m���z�Sk�SvG�����#�brY3L���8�T��~��߅���T*�AЄPs���S_~�0���CBͅ`c���ߩ��O�E$�݃�{�GkQ,�x�T{��τ6��P{�8v��0!C-[^�*�rz�}��9O��%r����Q��)��=b�o)v��XҼ�c��,4S��3��n�r����X{\�2�n�2�di~��"+��"mDz���T{���!���J	�.���.��b���M�p���W����EID:Ր�j���#��j����wA�@:a�5�۬$��y@6�1�W����
M�R	3�����k�^��۬"�FB�?�j�5���|5�������$.	�U��!�z�=�b#��E9{��{�F���.c������u�*Jőy@x�6�[bnS��{��w1�w���+��RQB:�钙����T{�1��+��S��7�)�΋��P�I�)��c���n*�x�M��7ZZίT����^�M���h�n�4j�&�p�����"�p\��*�� 6�gz`S ���Zc�5���,��T{�m�=�9��-c�}��a�y�4��5)�_/�T;�Y�]%�+���I�m���U!��R��/�$?����
:�g�B듹����&���? W��X��1�-�K�.��Wv��es &���lc�=��j�'��_W*����
�X{�s���O�]\��+K�.���>�����
�/���2�@��|�)�Fx��7��y\5��֢
 ��6�����x����������!�4��K�
���T{���$�����X{g̟��T{gl��`α
v1��rj�Y�<ϵ�}�aH�;�h	�}�x��v���
�l>�}�f�ӊ�N������W���RbU�տtn�=�3S�Ж@��X{�~71�W��r�����R�*�]EY��W��k��{k�RJ�oCK"�'�g�=�{�/M���]�8��#��	����XA�c��1D�6�Bˇ����3W��bG)A�T��3���a�j��&���k1�T{�k���̅nI�P��.YvDZ�� �H�A'Dx�i�z �0����w.�Hs�[�W���(i���=���\fS�a�T��P{�~��;�T{�H�R��.H�vFZ���%�w�w�'��_]��0@ �~7�DoX�~�-T���,q�]�ݩ�$�w0L')����c�$S��X{|���J��x{��(�	��ˍ�!,A��{D�p1��a�=���+]�]�o��(����T{|�Q{���!\�� y۰�P�9�=���y��ˇ �h�`~1�=�`ˆ��hA��b|1��3�	�lr���m�g �P�J9��.����h��QH�B�m���U)�뚚�/���`���l��[�fi�)��j��<�s�1�OԹLv�w1e��7S�[�
�l�U<�̳Sԉ�0oE%O���d�=�K���3�@���+�ܸ��w���C*Q���(j��l�ܗ���� ��G���������w�=�6����T{\5�?�O��¦>�}�r��b����LF�T{�h.�]��M�����h�ijZ����m BV�򲷨վ��� 0'K�1�W��rO٨���\�6��ȳ�9�+�oEJؠ��e���y@H��ۈzD�ޛ��-�уl43ߏg ����hр�уߏ"��J��d#a���7�.��t��~_�
������ZA�SI���r7���h���w-ѥ.[�E�|*��,<9d:b�DX��;�3G/�w%���ȪZ��-E�c&����H`>k��g� ,cx�R�[�i� ��_E��CXS��!M5ɳL"SibG%�@*)��.��]�j,��<2�J��<�V~�W����1��}M����3��4�a�>�x;SIV�{��n��W6��7�f~�M$ط�AI%eeSFz3��6�T{�p��˦r�3M���4��|Z���FfSK��*Ki(��ʏ�,Ʃ�0x�G�*��f�[�<��(�_�l|�7��n�5�{�"�UI��#�Ҭ �1�ep�T|��#���K���c�1r�͌��w��ޛ)a����\Z*������|7��IW�k��Z�E3�}��v�]T�.�*�n���]y6�[��C����U������ =k�
�=šj`m)�D֢�7�,UL-�(hF�J��f�V/Zw9��w#��!5�{���U��ەg ?�o�T��{b6�V����*+U��w?vR��-d��_���ɷ!�����{o���4��J��T��$�%d��4���;|;c��M���&�[T8����KD�
�t��S�j�ŷ�S˿�+Ki8�o���]y6�������{{;��x���7�jߢ�1��w`��l�9����O0/�߆oaL���{��-��T�5�w���M��-*�x�w�v̕�i���t�}�
�\Z߉�ij��G���0k�m�!��'���s�o)��-Q��+�@<o�<�q�� 4���/�3����y�o)*����@(!+�i��.��۱�)�im�W��bW+�y��؏g �/�R�=��7�;�߹H����/	�\�ȳP]�o��7���-~O1��ݎ�uH!�E[P�{bw+���x6������o���9k��D���O�o)*���N�q�<U���	~����-����ݎ5uH!�B[Q���]�\,<���ؗ�E���
`���6�{K_U������nL:]�����؍�=��K�ؗ���XK�r��%��_����"t?�
��E
~?�on�T`���e3�`z~KQ���]�t�۱�ų����X{l.�|J0��T�n�<*��s�����ήW.*����؆��T��o#��Xy\�1�-E��M�?��".VZ��X�p2��%�o�������X{|��f��+\j���*��l�'�K�۽i�pa����^�M�'��{*�X^�S�a�*-29XG�ɧ�Rʸ�BQ�.k���'B�j�&��Ϻ-OJ5�~</����m�tےO��o>��#
��tBSl�#,���Lع�����GZ�UbHa�3{Q����w�~plϳ�-�/�Y\J��	 k�m	~��7o�t�Q>��l�/�ɻ�;���Q�s���
)�fO*o�#�Gs��t�{p���,�/P�k�m	~��/*�b�&�}~�{�Z�'�����-꼤r����[���	e:�a����uQX�ۼ(�B�o�Wo�0L��*ߠ����>�O�x.v����j��0- �`��z2$��3&H�`JG��N<���Q�V�u�oT@n�w'$��3*��1��y��ڃ�g@�Kg��������`�Q����&F�:��Ga��l����~fkQ�V�}����V�ք����ǩp�e����+�/]��;xX�mI����+L��u���L���\3������:�2?z���
s��T�^��9!y�̎�n�,=�™����9�j�`g�f�1���v���� ���X>����1�/� ���I�wq+̷BR����ϳ���2�gc�0j�`c!��y�G"��;Tt$�<�%�~CR����8���dz�*l�[J��I`�j�`�s�5�/xQԉ-?s�e�
�A�_xP�y�@���w[��aHG'���uv�Q���p	���_x�|2��%�@����#��͎�
T8�c7tvE�F��&��L�KXT-��ݖ�mH!���x1�� ��Gw[dsH.�R�KR`�턜����6��a�v�<��8zdr%�%�- 61E���D~���N�X��d�hvH������
�^m�5m<��]��됌ݻ�h�d"��m�X�Q�-�AH�H�Wf7(�E��HB��	Y�f�db=�=9O<~��Q��>��,G��X�0�����P����6��7$��I�7`W(���֨���ޤ*�7-�jؐ|'U��S��
�����`	V����$���T<�ic�~C���|#nN)��EvO�@B���tJ0�}B�_Zc�����ݎ݈|�#V�4��Dr�$h�c��T�D���&���ɓ$��x��N�>���0��l��,��5֫��q����8i�6;|���j	�h,�_��c*����s��D��N���L���2i�|{�B`���-� ��b��}���;p�[J���q`G�_�G�������o�њ�u*�a�"�1b�I������
`�Sqҡ� Z�T�����W��+�����.';L��|�B~!+�<��K��Tj<~��P�����Un[�|�1��!�/�A0�+�D��෿��p��a?�`m�_h��B�ݘ�Ţ�Ɋ�O�L��5؇�X�����XJqjXS�Lk��B��_�@kg&�i�\d�H�%`!'�6�`U!�f��B��Zd
�4���W���ΖŨca��/�y��^!��>Dm1�R,X��	~��[�;��T�Vbg)'��!$�$������S�(䜦E��pB~׳S�[��Ƅ�
��N�$�/lN0Кc�9M��y�~����"´B��t�W�S�󰫐�B*��GE�״�9����x�T�AN>��D}~����F��F;�ń㒷
i�P��	E<^آ�8 �/p�=�^- �;i���dc
�h������@�p���E��!s�7Q�6-�����NKT��nr�]L�w��v���.��ƨHƻa'�~��nO��p+/�}������@�=�˝2���vr�+G��a�J%�V��Emq( �;�U�l�D-�h���X��sH�@�<t/dU�E���PJ�*�L𻟥�h�<��߉�7�V^�?��B	�����҈[�B߬���;й���R^��i�<��"�t'��{R
w�Z��V��Q˼k���a_*c�����xF���XI�
���	~w'���z~g�߀m�K��}xF�+j����`��wG�ݳ�?eցY`��xr�<#оHG���:rmN�8
���\�&��E^R����T��㲟0�6`a���Ɏ��0���Nl{����<��U"�
���-����M�+��6���E:��|���G�\��h<D5�Nl�[�>�v-G��f�g�6��T%��C����Nl�@�T;kh�dN�/'q��T&�M@ec^>:��I��Pe_�i��3P��7	KZ�?�d�$CC�?����n,��s�����>%�����|:.T����� �MD��Դ؉[t��L��.Ra�J5h��;[
��KcQ���$�y���c�)G�},;�>�ЀH:����`+��/��
G���ߜ"U)p?�v��/�#�{S��#���24'��ޣ�~c���<��
�)�ޏ�_hB���%���r{�ԇ�|�j����p<���Kk��n�N<'�_�M3�H)�L�*hJ�wLƅ�9Hۇ�k�it,eW�Hn�ԇ��&���[�Ũ��Ӵe]=Vg<1	[�����9���D�_hB�M�a&!0��6�L�r)�{
xN�SJ����uwP^P
p�4/���8�exgz�@Ꮬ�M	�G��\���L�o�$��c?>D�_���a>���{���r�O���_h�Q�?��I���V�
�c�+57a�D�
4!}U���?G�w_6R�ϛ2ldwI?'��{4�Fp�߆x��(s+ܪ
M��R2_�vX�ա�-�B|�>���Ҥ"�d�*%BF�M �/4�&t�=��YH@:��`�9*�#0h�J :m��9�m�XÅ�YH‚�A^���)vG�h�w'�vS�aW%
��Q����mRv����}yV�
�@�,�%�/�c�֡�CH�҉�T?��c?�h�8����m��q9]4����Ve#-��Sw�(�n�#�9N�#G��9��7�}�]���c���!��m����]��~�DМ�*`!���}�m�V�D���<	I�璉���K�<4K%0�	i�<'2+�>��`�&��	,v�&4�;x��tC�U�#�iyi����ƥz�������p���y>,Uh�_h�$��G��x�����
���ܦ���Е�����94�I�U��nH���B������Kݲ��3g��Ywե�o8ڦ5@ۇ�ة�ͭ���n}������7��<��:~�_�iy�uq�������</з�|�����w�Y��P�6PU-�6�bqN#g萣l�鷳#=��;L��?���2�v�۬�*��!�"��~���C�/&�
�Rʟk�b~�KŽ`X���2�D<2����옝�ܼ��%��~+��
�ȍ��o>r��n_Ke�Ty�J�u�����
R9�9�l��M�3������Y[��h���ΩfL�}�-e'>���9}}��s�����K~�?��39��W�rH�=4�n����v���ٽޗg�q�U�=����Ʃ���R��<��slf_�HEec��x������{�kyf�����^
I���/_h���v&������j�N���K9?!���y�(�������i�=�#7�?h���E�?0�!�
B����/�>p�c1�-A$;2���A���������+;t��oU���Q���!�:�ȯ���T�������?�o���<}��_-��#������H��uh<֨��r�r$`�<^�
u�Ɖ�+�@���gB::�//G�&@Lϥ8���ơ�oH��Ω��^��nZݟ�)��oK��7�/�_�&�j»xK���H����~������Ȳ#0�J��6�!��K)E�_��Ş�]�:����	��X.xk��/�K���ivY��ס^"�
d)n+��_�	y���mXvhٙ�"�
�o19��%�/O`5at\�g�C��(&��)m���V��u��V<?p��7mCr�W)g^�M0���	�#�`�_B���"�F�/k���c����T�U��`\����Y��gS%`�g8�j�����o�~���ո�
Zx�#�S�[{|���ƣ�/7y��:���_PE�!z%��I�PH����K�M�04���"0���d}��{i��V����KO'*����bZ�R���#��l�p�x�#�� +&��Rl�C��l�p�x_��S���i5�R4V߹��]csv���#S����~�Z������O��
���8V]&a4B��r�S��x<3<M��n�m�% ���"����c��z��'�T
p�k]zi���70�oX�^"�e7K�X�U~��.0	�	~����7^�l��9��=�*ӏ�$\�˥�R)�*��T������'�
��<,�V�F�K5��4C\���\R�S�qyvX�U	��!�L@$«��qyvX�ߨ�1�6�/�#��0�$�yip��M�����	�j�t�=V�!��D���)
Ή�Ў�c��/�-��m��S�qЕ��76�ohZ)�b���v�>��3�N�T,��Ν�T̳)
���!��3���	~���9��(�C��|؂�~��qy�؃����������Qv�X�dݖ�5Yh�h�B�KT^��X�P�r�s��SO�Z,4�R؍𗐖��8X�h��0*4x�Q𛃻?B;
~ٓ���*m�
�@��A*���v\��n���'n�8�B���X�YS�����M���:��d�xh��O�
Kd)�+���T�%Nv0��K �<���<���qb66�(���G*��6�d20g�MD����IG�p0�/��	�r��g#���%#}r��LVb��O�����MF��������<���"8��]F���7<�/a��#-��h�>�]R%c-��-�I�G�9>���ڥ�C>��Y��=�5��2 �4e��!k��<W���/�-A�Ty�G�\��}��'�%,��4c��R�X{�Fp@&���9xx�_B��ps����p*)�o|zg�(�ћ�9�l��`>���7>�/���$psLX����CR���d�J����
@8�<$&��O�K&~�4M�p`�7�̙s�.��������n��N@��T{{��%�MA�K:*i�#�Mr�-0���̏��� �%%�/]F5�78m v!�JN�猴�}��KZPؖq�$�6�N����y����32�ҍ�e}�=V%n�ζ�����Nb�I���8��I�ǂ�,�����"m V�y̍S���/]�I�C|��<M�C#�)~i��tiy�k������xʛ�7�y�Gk��4E�]S�&�9��I�o#�yK)�+�O�Ks$tO�&p�?�����o6[h�p��a&�I8���!֚ix�h���P���*��},���TjO��P�Q��o�_�����A�,�I8�J۲�X[�!��y*���ҟj���},��7ä
��Q���o�_��W�X7�ͭ��'xj��,���
�/�@�,�d<�@��%k��7
kB����@�f�K�H˳hr�`N��a=�<���4�tG�/ܠ�Y>�	y��T�6H��T���� pj��5�1��85j�T���8���n	=�N঩�H�3	$`��(s�T�tM�_���M�X�IID/` 0�}&�M�(��w*�A>�MS�����B[����-��0�ڃ��T�ƒT7�c��E2�������y��"���<Is���	��*``g掍s�p:�_8����|L���&�%@$!U�����m[�~�
������"�mst/7�'�S�(K�s	W5`��.9w`%Z<tB�t����>�
��h���狤��V��C\얒�~!�������Tx��0���c�= ���d�=��b���p���2��9���
�}�&,%�/�A�7�B��U����E����*�Ϣ�&��B��������Y&��C6�����ax��v�׷/.v�I����L*�w\.�����+�yuȩ���������/H0ô<���c63-�����~S����yؑ
�������o���JKu>�H�/�HO��ܞ�~�@no���i��C�ft��넿����5����g	�M> ��ul9�^{<̽*��LP���]pD�Q�
@
�b��v/-�/�H�/T�LD](ն�Epm:�n�=���un��Q�֛�֥�*[�O9��>�4L%?����ܬ�`�_��n��4�b��RJ��!3,�v�}��@Є�}�@�mYxz�`?o�_�y�M���B�n�ܸ�~��g�����w�=�Z��_�4	~a{2Ȯu7��耶.�*�'��nJM�؉�_h��j���},�
4L%0�B�/i��Q��Q���<�v�;��җ/����L�΄��R�^{lO�����W"�Q훞�*����PJ9�ۛO���`�A�U�;�6�y��Uf��*��h�!�8�S�U�<�gm�	!�P_���������9Q<������1q:�
D�����rܷ���{O��=��	�r��+v�;������p�޾M���Bbྨ���Ą��d���/��$�L����>���v$��X�Z��-�T{�W�Ƞvɻ�z�R���R��]�c�a}����,�k�~���)�T��TO��@����w�<P���Ap*�1.H��q�C�A�J`*P��CT�6E�?$ ��	��c��U�4�Ni�˨!+4j��C)�8ؕ�w���
��b3�@2z�B��J�|,�PJ9�_z/��+�~O�<j����_HH�/4N�_���'N��x�з)��!)��и�ʿ��+�ɡ�8�g���NP��Oޖ*�C큐�j��~!1�a�,t��,n�n��3��oS��*�#ˇ�X{�j\����!�a�Tt���7����m����:�p�Lปj`����k�&i�
�B���w�d��Y�4��U�,n��4F�蘋��L���K#�/��谖y�ǥ�d�=V3yO@����9}���0� �|��RD���-G�
h��@)���������8á� h��B��y��	�/9]3��i����es�mZNt���bY<L��Az��Z�lO[67��Ao�&M�_臇�J_�T�
�B��ց]X4�<:#�~����'�HYͲ�|/�`�G��+��R�}���2A�j��*�d�=�[�B�-�!�廉�;��y��Y�<@DŽ��MˢC�~�̪����ױl�����t��}���M�/�alh��C9���o+�h��<�@�<�$�{z�9��q�R��<�u�/m��iѽ�^����]�����&��&-��&�Ω���b�R�̾Tɨ�%����ר���yiSQ�׵�
�&���v�H�/���<��<�o�&�/�/`.jb�‰*����|�i	}i���+�/�m X�`�����L�������x�j_��>�������]���ފw����T{@pI��nh����؄#؜�@&�P����0jnq�+��C���fLB9��xGWLE;����
�i�)�_`s&��Q�@HK�^���GR2��94�4�/�v\0�%4�,h��i�F�Eo_�U.|vqv�R��R�!�_���������3-�%����̡�p�~� �v%D��8�jn��p��S_P ����{?i�@ZB�.��lX�]$�
�ո�\R�/�8
Kh����уX���ʄ�&���T���t@�K�̑��؄��N0W�i�0V��@�]�n6��/P��epS��g?�@�U����LE�^:q��w�=v���lM���L|\ǹ!0�Y6�ބ���T.�^G�i��ܺ5-﹩�@��i���L��#qtC�:�g/���k6��]	���<�B`������Dr��=�oߖ?��k	��|\��l�D7�����ᇢM�M�k�A�K��K���_�/ U����9(��ދw%u����M�Ԧ�HA0�p)��ɹL�
MEU/�4���r��X{,T����HG�7��_��AS)�p�g�����	H�e��R8x��N�)�_S��ӄ�E@@�_ 5�ln��y>�R�+޳-���Va>J1����&�"�p:S9���*��"�e��M�4��;�aCB���ya[�,�+���@STs'�#��&��<L���r�\HC�4��;��CEg�	�&���+9��HG�4K0C0g��{ݑ�F��}�`��$���7�%<�5&��@�`x*��ѓ�R<�z��	�n��<�i�!�eï�z��TJ�ST�B*B_���h��芉=CCV�x�XJ)*� ?sC~`4C�tI7d
�r�p'���}���n���!0$��_��M�_��2�4�PN����X{ ���y�X�k��p���T{ [P��p!Op4S��/��.��X,U�&�<b\*��� �y��>���'o�_��_�3ZA�$!0T��w:�3-��h�����`'Z;ps3�[*~pV|�=�{���p�
��q'-�������^��!���,p��P{�	`��
��� ��<@��3�><@+V��r8���<H��*^�
��N�3NZ�'�~ �x�
+�p�[6�y�Mt���+�"!0]0�IB_�;����~�S����4m�����׷�G�����qt��� ������s!�'6�(#�� �y����3�xx��`B`6`!@X�|Y���
��;�
��� U��a�G���
`G�0���
� �j�y�y�X.p��7�/���T*�
���`SN4�"������d�i�l�ˌ-�`uK��T����Cz��d�=�V��B%0;PU�ST��2���D�/@g��S��,��m��*_V6-���_����J�
;Q
�_N"�����������M���Ζ����o��f6�OE��P��Ύ�0j�m�`fc̈́����@F�_�D��T���
�g�<ʩ�����A�d$�HHLS� %�v�l�I�d$�H�b�J\��9;j>�$�2�_��a�a�R� �=��?�ڃ�d��q���P	~���p2�?S���PY7�?Q,�����t��W�d$�h���]lM�K%�ۅ�@F�>4h����T{,t�ci���JK����Ǥ�@^*:�� �R����>*|���J�K*�����T��|	@��/����CB`��� ���~���� �2�tLLP�@���{'�/���:�b8���,nvYВy��/�"���7��O�/�&��0�ڃx�o+��}��_ #�/�8�J�0o�pɅm&�}���H�����$1��^��w%�_ #�/7YԒ�E.�9��	�
��\��M�0L��pdp~aܡ�`�6��<��qva�X{LpŴ\�f�@)E��.B��p��gA��R�p�y�ga�J_"�p���ıW;�p�= ����VG;��H��*��$7��A�|�HJ�[���H�V1����X{,�|��E4꼕�޽$3�S�;~���_6�g��+�"��ʁLz�Ơ��H�����4JA	{i��7�/������H���_0T5��_h�~�A	���`7�Ө����ҷW�KvS���
���-!��TT�KT��0�Q�d$� -!茞��a/��!!�/����p�b�=���ա���&m��T�v`g*IK:w,���S�ږ��{qڃ~h����@F�_���TJ��U	{����	HCH�Bu0!yW�9U�
�	HGK����ؕ�^����_ #�/�i	���v�HU/|1�S�;U;�	h�j`��T��AU/�H�o���@F�_��6�][�ޱ��߸��s�_ #�/�R
/��@�I�^x�*_��	�j`Xͱ�n��j����h���_ #�/]Q
���*����2U��J�d$��[n���ME(���_q�^5
��V{� �2��=7���"���*؄��x���H�g������q(��6�ʗ��������A(�\4�7/�j*.oc%�_ #�/ܠ-�2�BU�:�:�/����-��y0��k(E��i���@F�_x�K� ����<��!	|ٍ��H�+��2�3D6}|�@�d$�����Ǘ���@F��=h�0�0o�0���r�N�s��j�Q�;pQ�M_BR�d$���	���/�	����P� ��	|IE�d$�� �t@�KZ�_ #�@�eq��eqЂ���|�|�`G* 8�$4�R��0L���Q�d$��D���M�i��\i
1U}��]�@<�_Hj	�����(���}���QJk��f�a8�Ԥ����7���^{����6�G�d$����0�_L�T�;U�$�2�@Ǵ��ޤo/�G�d��7���q˥q���`.l�N���Y�C�U�-��v�2��@F�_�&���s��P{��/���x��`�����6%�2�/QP��^ؑ��H��J�����j��@F�_`3ZD�Lu/!�2���4ս���H�T�E����	�0��@'�r����@F�_ ,a0�a/4@�d$��I{�A�_ #�/��0B��	�f��<�S<�	�8a/tH�d$��%V"���@J�_�+ZEw������@�TC��r
{�����	.���ME�<A�d$��Au0����
�/����	ai*�z������+Y�E�W°U��n��@F�_�
	�a5�Rʤ��E�d$�ؙ@n���
@0�_ #�/@Kᱜ�CO�"��	[��´b*�^ )�/��� U�$1A/��/����z	S��؀�	�����
�����g�C�q�N�d$��P�T���	�BO��LE5/�M�_ #�/wS-��T��O�	x�Y��`���yV'�2��)m$63!/�n��@F�_����k�������.��`�XJ)�x��	H'��sSѦ -�/�����T��Rʟ�E@���@F�_�p�_��`x*�]�n	����t�,.���w�_ ��1g6���IEND�B`�dist/images/wp-2fa-color_opt.png000064400000135325150755130600012567 0ustar00�PNG


IHDR���H��PLTEJ��J��J��]�`�]�#��#��&���n�|� ��j�"����u�%����J��\�a�d�y��#��s�v�p�%����m�h��� ��"��|�&��k���.����������N��K�������P�������T��8��X��A��������qtRNS�@��@�@��@���������g�
y��IDATx���Qn�0F�<t�P�< �f�����opl�`
IIL�{>�+8�uK�VE_�������`���i���u�{;����)z>�}Y���V����g��N�g�qtv2�2q:^J��g�@���6�2c�d[�3� �$�i�koe1�2��`�����zcܠ����f�1�t(2�uQݻ�&���U��*�A�cܳ�3�,��E��#c6��[A�D6�N=��Z��,,�
q g7ʲd!X����m��[�,�*C��26\�%A��=c7�����`V�61��Ŝ)J2�����e|�1@j�Wbn����1�(�qb�256"b�@"z_ΔeDZ�3E�;���*���8<r�1=����ϗqa�G��n���kJ<!�=��FZ��xE��.㺳��al�-�<���.�k�qL��elz`���'ߦ0�cd.��o;�P�˸�P��x�q_9�^Ʋoƾ���e�+�S��	���ao��u�<�q�e̹���D�eY1���~������X�ס�˘y`p_.��x��e���c}�xq�.c�`��7An~e�v;闱��^�1��c_5�x�e,�_h��qx��%ǀT*��y�����5�c�*
�u�)��f�2��Z�s��{�~�8.c�����6a�U��W��ws!B��h�"h�����|�}���f�@�?����U�ON+�������m�m�ύ#s�/Oq�2�k����s�WmK�Nǘ�W-c��1Ю$b4��o��W����v���c�eIT�2�g�gc��J�
����2θZ4����Cs����@;�.���̘{�Ƙ9�O1e���c�y���S��c��٧�2_��1�>�^��� �t��c�%IV���1�kg�;�٦�[O�3���3 �$R������WƘ9ڐD<�1e��2f���D|���󦏿�q����[/^���y�ǹ1�b�N��1>8��M�Ƙ9��E���ۡy���e�=7 �$�j���;�^*c��"�D��1o��+�o��:=��}��qO�3�6�mS컌u�)�+�+�pt��d5G(c��o���1��~Bᾌ93�(c�?��S�����e�8:*K��!�Xg�{�e��1g@U�<�������Y�1g@]��Ů����<�ͨ�G�*�jҼ�9R�3c�2����,�^�(c�2�c���'���1��-�X��c��n�b�e<�s�ش��"(#g�P�aƸh�@)�>��s/(�Re�y����ej�˖1��JH�G,��(c�2&�k9�)�hc\���c�R���e<q�ظ��c�T���e�L<�º��c�L�b�8�,�8�f�rۆ�(��H�m�S@���Ē5`�������.#��q��f�w5cr�jq3��G�f���Z��g/}t7crt��Dq�f\7�K;7c�U��8�fo��n�n�jq3�:�^͘�_�b���qW3&�@ꁶ(f\G��ӌ}��'�Xf��>f���]3֌wj�R�^�c��|p�b3�h��.��v�x�f,U�E@3�nm�l�R�:QD0�7�C���3�*���"�ןf�ٌ�
�Y�b�0�!��Tܔ(⚱s��o�R��DŌ�f�q�f,U�3�6�㙱Tl�EȌ0ず��*��\�0�pc<��G�b�kƧ�����X�6����q�1Ԍ�1���q�c����8�X�ַx�'Ʊ��av7�Pfl��+~/K�afs��m�R�v��f�ь�Xc��<�-�b�^�Ό�
��$f<{�c@3��i��G7�^�Ҍ]Usq3>��ӌ�c�nq3n�:��|͠c<�[cd�~��f�w��yz�<�G���{0c!55�1�:·���Ryh��s���������	n0�3����˒Ɍ���TGx7~�Qf�U��]23>g�kz��&�b�>�!1e�s��ş�ǘ��If��)[�Ќ/+�T�F�E	��ء
$�~��`�k�C�1t�]I�l�>�!u�S�qa<^�T��fl�����I�^G��ENi��i([�ό_�B��9�[��Kn�0���hc���8M;Z$p�������cۂ$<�аw��x��de�Q� �(2����WJ�T��.NS�LD�Rj�2���N�8I����\j�2���M�8E�%��@NQ���	���g�
�e����%\�^�L��ؾ�9qS�y�w[��(ջ��1X�]�\��L��ع��1.6.�&^}�[���ۖ�)����2��`F�ش���}�\L���b�2�į|lY���;�q�6��16��~e�<�ů��1�.�+cL�su+cl���ʘ�O)fe����p�W�_�c�2�� O�ة���oP���8���SƜO|�R}��4�b�2f���ZaS����]lR���X+��
��,�.ne��E1)cl��.�(c��
�
�2�� ɵ��7cT���
�� K�oƨxC�169��..�ތ����)����v-c�;c^�'t�^���t�����߉I����?(�],���;2��16��{�^S��;3i�16���e��w`�.cl�t��f��wbR.cl
<����s.cl�\E�YƨxW�[Ɯ��,-݌Q���U���1����P��5��`V-clC.-c���$Z��F���1*~(�f��h�c��]S0?��J�1'0*�&Yƨx�'�2��0&��z�1� L�e��aD��Q��@���$X�����s���PE��@&_�"�'5 �_e�^CcW�=�x�*��L��ʘ��J�ye̓
�i��m�,v��2�Ì��zM�)vT�ʘk/��ʘܙ*5����'�Gm�,Ǝ�2��ǩ!�5�U�\c�hRT����2f6���K�f�)NP�ʘkMC�k��D(Ie�5��SIeL�Ae�l��[���QJPs�񴗆��dq��S�K�դ�2&���2f6Ɠ�.!�1Y��Ɣ1�:u�xM��-Nu��1�1�s*����]�RCʘk���$El�dq�#�����g4
�)�r���1�1!e�1Y���Q�\c<��3Q,�D�1�1�wj�M���e��P�\cܭI	eL��&�1C��$�o�|�[LI(c�1nu����b9���1C�t*���(VT�˘k��4I�1�(U�˘�wi�_S0/���1�79e_�d�ʪ{3T���o����e�5����L�+�e�P��Z�ys�7P�˘k���]�ތ�(�P�˘���z��_Sp�wQ�˘�6�\�w�2f��H�.c�
L�{wތ��[)�e�9~�Y�b���?��M��e�l�yZw.�o��n�s3T`�q�-7��[���Ǹ��Ƙ��/��)�\�-��a\���Ս˘Ow�*�2�}f#��f�-�[���1C&x���k
n���Ƿ�*�߽�o3�~\˘��/{w��F���`�@i�&��b,A�Ļ+���{������"u3��|�qh36Tp7{�ȼ���<�Wf3��xL)R��,��#����=RdnƲ���[h36T0{�H��pF�_�T�
f�zh3���=v�0T0o�܌?e1�tcCG��k
Y�*il�`��g6cY�:i�`�H���bJcC�F��k
���7<Fm=���ݱd7)�6cY�ril�`�HwM!�Y/�
���f,�Y0�UcF�ރ6cY��i�ؘ��"�B�h*x}�lƲ�U��P�@1�یe1릱j��q�5�,f�4n�ޯmƿ�b������������ŔMcC�w\S�i�~�ƼR�Ӛ�,f�4vl���e���w�y��-N�s����Ϗ,f��[CgG��f,������8�z���b���j��buM!�)�ƪ1�m=��lP&��x��ڌ�Q��yg�q�5�,�V78�G�jƲ�bi�
�ïwQ�q�bi�
���8��!��Tc��f,�)�ƪ1��q�f,�)��
��/9��9;oc�֓��,�j7�-�9��,�l����k
�1ug���I�XS8����s֖��v Z�4v����Im#��0Z3E��@cM�w�B��Z�ׯ�	d	���g��gm1��1��fcӘ{�8g{���56��3�s��`�Q�?Qjso�\Sx�9�4����9���;^e��4��0NiƂ1�fcӘø�)��|�56��>�c��`�wM��i̍a\��[̅���k�8�B��R�P�q}'\Sx���5p1�C��H�UMC�i̹M>��~M�-溶��S�
Y�"�4
�1�øpM�6ຶ���a\��k
��;z�
�Ɯ�e�WR�@�_R���8+��X0恖��4�t'\S�<��w��|�U�2��@�lls��X�`����4��vMq��<�2T����X�`����4�4���h*Lc�UU�5ŀEZ�
?��JX�"�u���H�rC�i̶�ֿ�)xF�P1�]E,c�������U�X��I
C�`n���k��i*\��mS	�X��i�B���ۮ>^ڌE
�e�
�xjU��5ŀ�5��̶��
�?���`^U�7c����uۼ���e����w�����,c�����6U�7���7�]���")B��I����;b$|��)洭Z���H�ƃ��k�)|��د�4����4Q��6d�ʅ�y�7*�)�"��t�r���V�-��p�nx
�Lߌ	c�倡�^=�ʘ�R9���z���1�;��4n���˘0�qHcNx�/c��9��P�/c�w8�
�	�/�(>�9��2̩�	c$��5Tb�e�����8�U�ͧ���!���m��L�2&��J|s�+��ʘ0F:�i�NQ�0�2&��K|7T1�ʘ0F>�i�NQE7�2&��Lxs«b�_�H(<�j0�2&����qƒ��c���T��
�2�Ch�T�Йg�������Pus.c�	-i�t-c�9]����Иw�-;D�\˘�HJNcv
h�{�Hi��) 1�2&���mi�) �^Ƅ1R�.;ӹ�	c�%�1;�_Ƅ1rRӘ��\)��YƄ1�RӘ��f@�HJNcv
l3�2�Kh$&�1;�
�,�G�%4��,
;vM�2�Ic��-;6�k�Ә0Fbb�S`S7�2~�Ƅ1[v
��e�Hc޵!-)��c�]�YƏ<�]�.	;�t�.�O�yݖ��%ņRƄ12Ә�;,��9�=�[�^��N�,��y�,��
�z�;�7{g��8Q
�
�k�3�7\�]�cb'鴔�=��Q��&�*�C�]�c�t���"[E2f|%��o�Q���$^��߽��������"W�Hƌ��]��O��z��d�.@���2#<=�$3�{_�❣1£��˒1���6~�L4f�M�Q���EȻ?Cf�g�ƶ<�|�����sTz
����d�.@���8�HOAi���W'c^���V*��)��Eɘ�]����q�Uc-��ɘ�w1�.��1�)�%�8�|��]������X��ɘ�w�.��Qd���Ë:c��'��QY56P��1����wl�����Uɘ�]���16N!�S c%FE2��]��O���~��ɘ]�yc�4=��^��Y2��bl�����k�1�H���)���"soi����塧�f/٦����bl\N��@�p��$cv)&��CO��5ɘ�b���qnQ�1�0*�1-�Dc��S����ɘ]��.��=�����*�1-�4c�.�2p���ɘ�w]��ې�)(�ါ�$c^Ϝ�bl܇TO�!<�c�OƼKQ�bl����@�Q���D:��ظ���<�qcc3SK�Eɘ��ec�n܂ c8`T%cZ�rc���E���;FU2���w166�����٪�1�m
\���4���C�J�T�\̕R�4�&��d}�͠����FO��?�Q���qcc	S>�K:cZ�>.��=����MO�Tƿ��bl�����/{g��8D��_�;4��ܘ��b")jk�]u���^��'�H�V�c�X�),���1��ö�\����mʘN�?[��bmli,ӏJƾ����iF���X� ɸ�X.�1�4v���'c+�P.�Ɩ�r �2��m�bm�.��1�sB2�2��s�6&���MOHƾ�~�bmL.����	���?Hkcpi܄�%![�!�{�v�Fi�9�X����2�.�����M$$c+���.����X�9�N��x	lkc��=� �3��-���Z7��W'ce�lN�"<����T��%#���
�I�6�/x�S���dle\���X����X�@kc޳e&%[�p�3�4V�\��d��~F���2v���S��-EkcZi܄ʆd��S���X�s����e;���S��dle̟�1�4V�X��uU2V�(�bm��%����=����j����OS��[��?��X��I2}Cg�����t����<�B2��w8����Q7a�#�	�������d��2���ӝ6���Jܹ��K�x�NMF/��1�Y�+��2����OK1c2�a�bm��%�sRg�1EU[�_7!�#�������2���d��?�:u�3�2�f'|��/STv����_�hB�']S����6�.x��Il��P��`]lq���H��xp.���Xv%��)���q��8�
��)���Qn�0Q��RNb�?`K�����^��0ZM�KHꙶe2&㕜��c2FV
��w���/�x,���x�s�p<���Ed�Ռ}���b6�^A<�z�==��\,U�1^ʲ:�d�.fc2Ɖ�qA�Ow���Qz4������X8������&c�����#��-��j�d,�d��̑��c2�k��ik&�G���1������3�b�x�u
2H�fl�-O.��Q'㒜�r�T1��]
��r���d������ث�x�d�L�� Kd�~���f����w�;�K�u��f[�\,U��V��ֶ�1��v���[�0�E��*�w	ߌ��8���
2F�m��2N�(ؘ���x�1��F�TS���j�"Q��S��C��v�M�NP��6�b�Wd�~��7�s�TA��.��1KR�d���B� cߌK*�&
����u2n&�oHR�`�"n&�3\��TA�x�f��B� cl���-ݭHd��n�f2.�R�8�	�W$�[�
2�Q��f|�X����qu�٦8������G��Mq�Dq�"��62G�x�/h�
�d����2�(�Y)#�ն�e,QHd��c,�*���;��$��(jH��$&��a���f�nV���R�]�$�/��ca���Ō��+��
�쌙���w��1�Cg|WT�
a�7;c����9�0�?��=�\L��1t��crg�'�������	c|�3~1c���X�f|;h�9�0��rh�9�0Ƨ�ŌM�Z�ca�o�Mq,��ca���bs<a��vƶ)L�.'vɭc0c��ې*��@CgL�oD�Waf��œ���r\
�1����k֜1P���03�⳦��yr\
�1t��m{�|ĝ����>�G�|�	���?_��)����5w��|a:�\->{��1K���0�7�b����+ Qr\
�1�q��5{$H���0��8T�Ϛ���#��@�gj�ؐ��9������qv0�@^5�`ƁZ|v�I�9��:�<->+��̗�j ��
�8->+�j�9���H�⽲.:
��j ��3˭w�|9�����6�H5�`�IZ|Vl�2�H5��'��Ҍ��j ���s�q�p�*��@�x!�l�����A^5�`�!Z|�#���[n�@Cg���g=�3P���0�7�2|q=fad� �c��"!��z��yu�;�ۆ�*�
�[�+@���K�ܙ|�92t�ܠ�ϸ_��*j	n�O�;������z�W=���Y�=ˌ�p\=�h��c���F/�������Tn�*�w�Y��~q��*���s����p\=�h��c�8�4:�d܁g��s&��z�1�m
�B��bl��*��شP���I⭢z�1�߁g��_���$�*�C�dlZ(^�'�VQ=������jQʭ�z�1$OƮ*�[9�c�VQ=�r�����l��d���m�x�������Ch3�UͿ�!Uh���A�y�m�hs1˃�5�C�)�G����A�UT2��f̼��BH�ͭn��7��s�ˢ]�"��q-�M��DM��*^W�B-W2��f�\(���L,W2����ZŻ��T�pr��oڀ��(H*�����x���O���w��U2��f�](��y��?Bd�p^��%��{���12�e�6E�En_�����t��a	�q�BŔT��cd�p^�?4Ő%cg�@ư�qF,�IS���M�4�+%�1���"cX��8b��JS��,3��ߧ�Ő�E� �����zo��
d��k��X5QLH�d�v��&��12Yf}�b�����9#c�eT3�U�O�p?jd��o�{;�?�J)2~`ǘ�x����p�"�/�� '� ����\a`7'���L��h0a��^W�e�io��dfܡb��.����KE'Z!<�S�x�X�܁����]��B��2.Qqڈ"u���K�̸E�y#��8VƂ%���fq��^��oSԨ8���PƂ%vf�d���]j+c�z^��ӳ8l���Kd�8xs��SƂ%pfܤ���]b+c�W�M*���8VƂ%��*eq�O��2�Rq��.퀔�`	���=Ж��X��܁ץ��,�8'e,XB�M��O����)c�13nSq��.�!7e,X�kSqu��X|ש�<��q<��_�>3�S�Y���Yd�H�2�S�YL�ce,X�w�=թx�,�Ʊ2,�2��=`GƱ2,Йq��'�bh+c����Q��e13���`�}��QŽ�?�ű2,��q��_
�ce,XXw�u�x�,�ű2,�2.|�m�i13���`�̌+Gœ>D��ce,X(eܪb����X� �+�ż���X�ʸW�f1�`>3F��f�eq��˘;�@��_�����X���6���,ޭ��Ý�~��2��13�z�^�X�B�s�8��M����ox ]���~����e���aH�U��ş.��x{w���&�X�̌O�fPY|q����3*e������2>w�����n���۾��8��-��<����
�y����6�����;�m�(l�{���d��sM������s^�ʧј�����~���l�b�?u��`��3}(����Zl[�E�g[�1��
<_�	kw7��k��U�1��wS8���8��ӧ,>��E��[�1��3v��0wq�v�IB㟇c0c7��
</�v/�n礡�y`�n:o2�2[�P\-�IE�ǻ�1�*�c:ug�d(�wqW-�IGㇻ
0c7�6��m����$���x�㊭��N����������R���������93��P�-.%1���U�1�i�Θ����Z\Jjowc�Yl��
<�C�ٱ�X\Kzov`�n�n
�b��W�kIQ�/�
0c7�3v�W���ŵ$��?��1.�c:�x�>aCq���D5���c0vS�d�x(����ג���
0c7��{������jqN��U�qz�S�d�u=azC��8'e��c0���n�
<��	�o,�I[�����M]�����
���R�8̃al�Գ3v��0����$�q���ݴ�x��!��X-ޖ�8,`�N�n�5�b��jq���5��s����V���Sb���1~��f�f�������|��qQ,����a\2�q���ǯB�IJW��0��8,`ƣ����啝Ɔ�Z܋����{��Sk2v}e'�8��K��a,�q������-kn(��i&0�+�2�R��x?�R���xؐ���v�Ňal���k�"x��S��Cb?�~��1VӸp,w�������6��0��8L`�#�L�Jq��#0V�8L`���Ŀ�{�h��][�7�c*1[��vY|$�bU��E�xc+0���ɣa�n�e5.�1���H��,�咏�XX�0�1��7�R^��N���'�4�1��	�k2��X󡊵	��X�yz%�{���Gb,�qX̟slƾC�_��vvy;�4��'���;�m�(l`fE� ���٠���A����F��_��W���^ܤ��W5w�B1[|"�s�Ru�`�W�Ī��f��8��`�R1NhS|i��碱���e�H,N���|��9�*0c�8�y2�X|�9i�*�y	�ˉ#���rGW:�K�զ
�*�z	�ˈ#���rO�?
�|��Pe�c�"�qq$6�L�����5�>�.g^ő������S�\���H`�q�%��
���L��z�7r��87���'c��k��c0���M��Z�������R�HP<[�Y�E5�1��H	�����?$��c0��℠x��Ʋ����⤠x��;#�N�`Lˀ81(�j��u5�1=�����0֝*�����۸5���Ŷ0V�8p���◹���S�1�!�@\Ů�1��8pƴ����0��\lc�"p\����X&�x���b,�qZ`L��x��
c,>U$��Bš�����Ǜc�@���{�3U���p+��~�px-(^Z,�1SE���9���Pj{
���g��~��ŋ.m��1S?kr��K�o�0Q�`��9���x��E�Å����D!�1S����[��n��L`�LnI�8��Bc��1����$W�ϩ��yo��Kb�TƱKS2�ķ�@�:Q�b�T�$�n��(t1f�c�$��JP�n��Ƙ�����o��Z�[��1SG�߁�a�r�x�(�1�pl
�?n�E�_.\pUB�<.P��<0��k5�x�0�X.S�b��������JeD��.��q����aX�|���D��O�!��g�V��(��EȢ?��C�aǶ�Ez#iT#�wN�fY(�p�3� ����7�����'wY�G�1qB��q��^Q�'�.��p���7ㄆ����?~}wq���2�o�	
��}�+���:>~3U ��}x���qr7�b�2�#cQ����N���cd�	o�xW��%ԭ�xD�����cd�����'�,�(c���8F��X
�}�?��P�\rC��X�}�v�����%��]�䆌����D���2n�Ș8F���H��,�L���LA��xR��X
�(~dq���V_��8F�k�8̀W��fq�ߌ���q�����<����m�VLƉ8F��`�Fq�ť2�)R"�_.��72�Y�q�%X�ı���á��>э��,.o�jW��2&��12>�Q�g�2ne;��#�]�Ń,����e;���� �+�x�m
Y��)7�Aƣ���Q\����X�j�A��|�12^Q\���2֞)DZ�L��	��1Q���]�T�]���C��3E�86d���C/e�PD�ލ�y惌��lx}�6�˛q�2�x��|���3�>Qqp������2�惌'd��O��wK����x1��#�"�٭�b�x(��y惌���D���2V��Gح�|�12v`����n��q�8�A�� cd<�ĖoS�~�0d���a�X��sX�6��(2�U�2F�#0O���r�t5e�*?.|�0�c�xR��x��2q^(.��2�8�f&���򎌙'�,w��͸�X�)5����1�򅢧���m�7���*����8(��~��݌}���Va%�I����7Z(����Õ�]��ӱ� �Y2~?7���‘�C<.?QƩ��*�Oɘ74�-�q���ul.�c��b@�'�V���Va.�c�ze��e|��y��{���&Da�#Gi����ȳ@�(����B�C�E7��8n�,>��`ĘS��0�|k��e�r�cg.#�Ę'Q<�1�g���m>�`�a<�1��ؖ�P�9�������11���:<!̟�˷�Mr�`ĘS�|
��"�2�v:F0bL�y'�I1�ږg����11���B�}����O���'ƔXr,�x�mߌ�읎�{C�)���݌}�ucܥ� �F��0�f����9�p��y���1�����!1�����C�۝���I�%�3np�ьq���'y��cJMq�b~��S����0ǘ��Po��A�c�����Y����W�qKps�� Lj��GI$���x��u��1�=��b���<�{���!2bL�y���x��q�e�ѭQ�q[�A��n��'b\j��G��8�s���N���(��o�'�<N(^hn��3vx�y�w��A�!�ocN�g�^�5��
1~,c�C1�.�|r��ǹm��q�e\c,���26�1fD����lj��ي^��9u�.�6�J��"�Ø�8@�:˸�����!�W��p;Wǘ1�
Ɯģ��kL���X��K�	m��v0�$������o�n0�S��x��1�"Ɩߡ�'�(��Z�~G���E?�c�2.�c���QU�1o9)�T�2�"�z0/��8Fz��ƼMLP�a�܌k���`,^ƥq�,c3�@��
�q�7}T����˸4��)bl �&"(V���i��QA�ׁ�x��1�E�K��P��e��x�2�!ϩ�X��K�Y#�EF��(ֱ�/�q�99�c�2.�cd���đ�XƉ�]af��1N^�mj�
�#��D�#��Q�m_�1�1?�r��˸4��H�X}{B,x�M�2�"<w7H�Y�j9�Rc��F,�X�2���q��+�Ʊ��1��k��w�P�z�3݌k���,c���a�v�J|5E��b�v0���2>!K��2V�1V�}שP���_����b%>k,θB���e��x���7�+ǘ���9��x�b�[�j�˝�,cec���(^&^��Ŋ0��;��Вe|B�;���X)b����	U�])bo�u��9S�X�X-b��XE��a܁Y��k,���2���kV���g�Tt�O�Q�q�w�UX$gi�˟<Ɗ��x�P��N��e|�G>g\WX(w2����w�m�P�k���tQ$&�$�2x�����<9ت�@�A�{ 8��N�\ ���v�ZC�xt���he�qg�H#��=C��gf��T|uf!�1O*��>'�����x���1d������+�����餕1��)�8Go���ó��ݞ�2�s3���fNZS�5d�^ƸO7�M;2�?�!���k���2>_��̃�S쪅ج)c��Q��l��:N���p�ʘl<BW�q�As��a��?��b�ճ��u"���eCƐ1x��(�'��+ۅi���?wd�Ǘbr1�2NnƵ]�	-�tCƐ1x|)�ʘ�fL�x���H*���IqAƐ1x4�y����#�,&\'��=�<�����J��us݌_g��D-�����c�8Ko��q.�/����ή���qO	2�-c����<�}3N�w��ڮM-��SDsk��/�A���H�,�3v����p��2&�����770��6��F�b6��Y��2����\C��[�͘�ܲ�p��2�CƐ1���3!x3�&������1d&
�1!��n��q�YL�N~���3�a�2^����6cґe5�2��c�X����D�ی�[xYf1�e�S�2_ݲ���:��f1�8��TJ�{!2V��ש���1�����q_��fq��[��N�4��B�:�&��ތ��8�	�����@�YذJ?21�X�f�����zʘ(
d��
��q��M܆C�f<p�Ŗ�QT�Di �j)����}0q@�f|icYLԪʘ(
d|�X.�í�O��͘I��U�Di �;@�B�L�f3��]��V�S@���D�qB�f���QW�Sh��*d,��Ċ6�7nYL�Faƨ�qZ���(����l�<U|�VY�Di 㧰[��w�nM�m3~��it�q����l�ܬ��aK�
ߌ�ݸ�qZ�8P�2�O��
7c�7�bj�eL���DƷ� c�[I�r3f�ńk�q��2�H��J7c*>�T��S@Ɛ1'����z�G���X�B1P+/�@i
�2�U)��Xdzm~�8�!����X�B�C�kԗq��2��b�k�ˁD<��_oƲTL8���@��%۝DAā��_�nƲ�P��F����!㬸qT�'��1ӗ�8�4(�^�v9 c�x���2V��R�?�Π�q�I{hr]��^��?pE�����ʒG3�٘�BN��t�2�?~�B�d/1����؄�DG�x' c�=s"N�����ք�Y�Pƻ`\�
�Ao\��Me�bflMŔ(c^sCq�g> �j�	̌o�P̈2�}���cC�Gȸ�çqW�8�}�M�M�I�e	(��@Ɛ���gd��q�glrXL#�ۃe���:��~�1f�OUq��Y�(�@�^e,<�����MauX�.c�=H�2n�u+�
�x�p'e\[ƾ�Ŭ��e�>(�f@Ʈd=<����հ�I��͇��Qƭ�26u�&d,r���)jό
�#�+c��2�7ʸRƶ�|��5p~�a�e�sfluX̤ʘQ�F�2���5<E�g03xR1�2���e̠�[p�1(���ؖ1k8�2���E��fƖ�Ō(c�L�1�2��u��m�f��2�=3��b(73Ε1P�u����Xh�]|��Ƙ�‹��eL�!���2�	d�o���U�iF��{ަ�-c����2����r(D���]������5,�x�toS(��V�,5YƔ.c	ʸ"��3�e,4����_��Ə��eL�-cI@���U|���e�ToS(��<�|G��iH�q�q �d<�pTƵe��v�+c�T�	(�
@���"
����ؼ��dӰ����2�ȸ�ݶ)rQPƦ�p�b�e|{�ue����@�}��Z�<3���b&U�DC��i���w��7��Lo���_v�����elgf�AŴ���P�["el�̇�oa����M�o��GZUƒ���f@����_��q��*��a����%(㍰~���Sgelbf�C�w�2&Jό�P�[w+�g��M�G�K˘?W�2�2V�Ӂ2V83���2�D�2^ʸ2>:���=�gƾT����@#ell����[_�lSԝ�Vq$W�TV��q�q��m3㋓2.��k���I�q1(� �Ne|��x�����]HQ^���q c%�Gܐ��[���™��i��@Ʀe�Oם�U1���"S�E`f\�	+Y�h�N�2�h�3�nf��58�ږ1�)��26wO�Mk(��������)�2�MQ��q�kƮe��)��ة�_/�2.c��A�Zf�ئ�mf�R�3�)e\J��q�q��>>p7�:{U�2΃x��p��7[��13�e���ū)j�q(�2,��QƸ�"T�����m���&�E��T��"�����g���*����ct2<���X��8�U�D�|��ɸ�iSlr3��=�)��8&�x��ɚ�AKDO&c%7c�=�\O�-���퓱�q4���:oƟC{,Rx���8�ب�5܌5oS��Z�U�d,�d�2V�M�.ɸ�Hƛ܌y*�X��#a2ނ,d�TLaYƼO�=wWa�8	��d|��7M�և�7�tnS�g}����>K0�&�n����".
J����s�e@��L��u�d�C��qӦ��fL{,R$X��1�1���r���=c]��Ot�%�X$����
��z>JqtI�Ξ�=yW�nR���z�c&㲤��u�S�7c�'���q41*���t�d��8M*�nѦX��>���A��=�q2�u�d|7gl�f���sK��KR�'�wѸ{&�2>*�<��1^�ⱛ�KI|B)�tq��?3#�X�g)\N:�ۻM���
̓Cj/���LƔ�Yc�)o��k�9H1q.����d�z(cS2n��3Q?H�x��;n���4���O�i�)�נx�b2&�+kƅ�sp�M����02H��ga��&c�2>h�0	e�`�)o��k�>H�y�O&c�ؒ�$�m
��9�R����dLۑq�ԦX�3Obl��O�LƘ2��O�M7��W�x
s�~2�G*&cSͶe�.�jSL݌y�X�� E}��{XFJ&c;2�i�X��8M2~f(��� E=Nƫ��ɘ2����1�+�1H1N�Z��#sW�e|,f0�
^��y�X�� Ÿ`��؛3��1Z�⣧AI��1�k11H1."�6v0߆��1�6M�[���8�?�>v@�m�2�fѦX�&"� ��f�!��R�z�e<��}��ܦ�x�)��1���`��N��g��͘&�
LR��qwԠ�
�Cے��7�nS<��X���D�b���1�C����m�9!�+�P�
RtOQ�����نV4>Y��i�M18Hq����>vW�eV�8YlS�ј&�k��cD.���
elM���ڦ8���`r��{|���8�i
H���>Z�4�VX���]<��
���e|P�F��?�7�sT2~��#�A��.nA��@Ƹ�6�:��d��K�?�b�$�A��.��1e\X���8�fLǓ� �c��2�H�|�^ƍ�6E�/��/{g��HD�f�k��8l�
YL6��%B;�:�@wͭ�s���~:)kn]��P!Ņ2θ��nFƱ���o&��&Z-����A|�]�z/��S(�'�7��$�R\����A�.��X>Mq:�c9Q��)~v���]:����LxJS��(���8��ha��ėq��qv�A}g�r�RLrq�ͅ^:�T�K�'��8���Mɉ�PH1�ř?�tƩ�zO���~����0R��8ʀ�<�)$e���Z���W�B���T����j�L|�
w
i��\Lp�R���¿ٔN4 �XٶN��_v�PHq��3���t«��d#c��1��zPHQ�����͇�_����c�O'�3~��݄	R���Ś�v���vƈ�6R�Յܒ�Ce�:�_�@��PHQ�ř�����x��(���w��KPHQ��b���;ξ�ͳ3� bC(�������$+�g�!ט�7��
)
�Xveq�&ې��d�^�`
�.�<�E��o>~I������x�f ��B
kg����e����w��3�	��.�X.��q�l�C^QԞ��1���.d<`7 ��a
Yd�:�ɘ�x~(�0r12����L��u����R��b;� �2^�6f�Ș�8R�a�b{�u�,��d
)l?�Ŕ���G�8E��z���y�����Ae�82.�����B
?�6����D)h�d�
)\����� ��d
)4\l%c���2^��0��PH��b;���?�.ۖ��U������B
{��j�-V���dL�M
)\��{�e�&L���	Rȸ#�蘌cC!ŵ.F�7�~ ��8(R����ѝ�-��]�<�2|��WR�p12F�~&cdl�x�������X#L����8�C3�S!ŊB��.Fƅ~��2U��8"��񪧐�����X7L+N�d��B��?
)
�XI��khd|$��'O��)�d��dL!���w�
�Ōce�Na�	�12�g�����],�hPƑ��=0�╽��m���
|-"���(z���]N��JH�\�r�t�����/�)��؄��ҧ��ْ��n�qS,RRdt�
?- A�-����g�0c�"����sw’�[z�W$�NQ��A��� ۇwgƁ�5�e�7:C�r#�1)�XT��R���{�2~D2n��A��� E.[���2E[��k�X=1��A�\Ɛ�1�]iJ$c�)j�8H1?���b�2�ރ��	��U2������8`�"��ŕ)��!�F��T�1�!f�b~0H��Ŗd�����o*S +g�M���v�C�q�$c|��N� �`���Őq;��"��d�:+	��A
nO�?��_i�=��d�di��R���O�6�mM��X;7�3�{��#�L.��!�ؒ���5"�>��dR�\��݃�k�K��u
>�����.O�A��.�>�]���e�*C�R	��*�����b�) �4�E2��9�-x
���b�8�D"�N�d,�.0H��Őq
��{2NNƨS0B�e�J�b{�ߦ3�d�Ag���!�ry�T&���f��!���T���$��!�
@!�8��A�h��� >�3��X'�J�Cn������"�ۖ����8�\I|�;�fo��Ԍ[�q�&-C�ޕƇ�Cm�)&�2ŵ�ս�{R�{!A�H����
�����lK������d�
r\��k��� dܢ�w]a�ٓ1��'>`�m��Q3N�WpFݶ�d�$x�a�m��������� c$c]xW�0nj37H�f["��6��d�E�MW�=����QqHu��d�7x%��M���P[C5�I4ȸI�2�CpI
�G��ŨDZS"�;�D2��w�Tǯ���ڪ�SwB��O�SA���V\}�a�-�Ũ��WqJu��d�:E^�I�c�-���ׁ�n�S����).�1ď�+�3��CmZ�k\���*��f����dl� ���W?�����6���(9f}''c��˅���W��n��\�LƆk��S�I?$%CƌHs�s~�R��\���h��e��j�����+!��p�e46�b�8�^�9��ɸ]|��׺��cԌۓ��Q�&c����޹�6�Pt�bPdT����b���M��1)�AJ�lf���䖤)B]l�'���)z�l{#9YcìS,�1d\	�.�6���Їڰ��EM��e�d��.�v�
�Cj��;��K�j�1�h��l�\�5c&j�,/�����wq�񖆑�q@�Ǥ���|�b��!�
�w�l<��Kj�%8�f�H�H�U��b���?(g�������d��2f'cL��h�l�/��u��s��^3����._�LOƐqi���{��q!o9xܗ��L���R+�S "�#�`�x���w*4�b��!c1z�g��7c�����#�|��{�Ő1E�����d�	�!ݍ�$��fcI)���mRFb�[�,�
�d�&�M2����<�H�e
���f2NO�x�4W[ő��m�d�� �&M-��u
������*�$�5#C�<T���O���d�$���AǴ�1rq/�
�rf�ɸ�T	��f_��Rƚ�):��&cL�q��&+��mc̈́���`�7�IƋd��7��~��=Ec��v�X��I�^y!�'����oT��4��e�z~7����4Κ��ۖ�k�#�2������	G��d�	~"
�βp�s�1�x�T�������5C��MI<�Q�x���b��F�q�	?�T'��Ƅ��
���!�Ür&cL�)-��z��^��?]��'e��,�MƐ�*�\l�Q}����h�l�t�m�	�qq�83m?q�`�2N#g2����!��<_��Ș���W~@�&c�x'�ż���'_L% ��d|F2nG��b��\�t1�w4�5�۟
�$c4��8�.fU6�ſ.���L�*.�4>�1d�� ��;�x�����@2F2.���b�hqV�b���.�f�I�1��D�1��,��B��;X\�+c,SPQXr��11C��q�]̱�?\�� ��Xtٙ6xK�� ŏ�;�lS��ngEtٯ��%c4�98�
cf�j�qYc��ך+L�h�2΍%�M3,�+\|�&�5W�1�q��q$X"pq��2�i��3��%2��G��p��>4�n��OƘ��	��K��X�`����'�i�2΋�ߤ�,�.�c�B���i�3&c��DEG0&�h\���2�����m���*�l����r�0D==4�k'�|h;Ӄs�T������b$F@,R�����#}yZ�`�U?I���X�3T�;�#D�ə0N�1`��,�wQ-3�P���eY�a
9�L�+�'����	)���w�b�0�_fS<L�;c�xM�&ڻ��m��bSH�W�ϐ�1�qu1�q;��B
Sȉf0]�;�Ug��8�b�b��Sk��xw0�9L��=�Ɖ3�WTk�St�DG��bSH�f�ї�1�qe�������S���;�X��:c��WQ6f��3f[�,�%�h���qƀ�zjM���M`��bSHxf�)R�p�E!��1���o��b�`�yh��3Fh��9�8f�@f��XKbW�c <�3�����w48��`q�a�aM�AyÙ���v
��c���2�R\Eo�|�H��.�@�Ɠ�Ũ3�S,�Z�)�_�Xg6�b��0���3�א����E9�wW��~x�E{y��,��Jj5g���yQ�_��^R���ƻi�Vr�h�E�<���I,�)�)2�Y����g2��%�0~L48�VuJA#g,���;�1L��4xrg�q�eQhէ!q�,�p�b�w[=���:c�
��k^�Fcg�����ŀ��eV_/4�;c��!����x�p qh�fq����"�Wp(;c͑1%���0��b�w[=�L�g���������xX�6'�8�o�_i��Bc8�r��3V��%�ڤ0����j�1�)���yg��8+>��#�IF!m�gq���`|��w��-;c�������-��c���c
��M�e���4xrg��9����W���R5,�f1�)d0�z�-<�3Fh<M����8Y�6���/g���B
�wJ@����
���B�VGe˻�M�-�j^��
��'>���+9㓫И�8�dQ�T
ƎY��Nc�_`�O���e��S|�f|;f1`,�l���S����s�����r�8�̀�c׋�m�0�Ŝe�O����G�0N��a�1`������lh\���1�p���ͪ���1���m�/
ޏU�1V�5猕��@�A
����^d�=4�k���w�g��}~	����/���mO�]�X�1N1�
g�]���`|4p��k,�eh��_���=�8㓏ИrΘt�xaQ5R�gq)��Z�]�X�1N�h��2FF!��_W����x:f���8.m����3���Emr�[Hѳ�O�$�0�d��}��X�'�8c}1�¢61�;���kE��cW0.2Mq�9�i������1cQ��nY���n�_��	��w����w�i�@�z��F�5���U��"�Z��:;��Y�U�q�|���~�NL�����8_,��[���Á�K���!,�q30N�+�b9�ݲX�c�|��p�g(��f���e
)��kʌ��?gw')
r�⾿[�`@���
�͸�
��X^,������f�c;��6Zf����lhƏ;�r{e���ݏy�X�1Jh��3c��8w�.�ؒ���T�)��W���C����8�y3�1�g�1JX�罐�+�;�Rh�gƘ��2�^�xL�E�]�~��s��P0~S3c��xjƣ�6�CCl��Y0��b�0�ō-~-g߆4�dƘ0�3n����cqR���[!�0���~���c-3F�������q�����?�W��=2n_,۹ڭdƈ0��pq*��5a<
;��8%2g����%4V3c�
�}���GZ�C�9Qx�b��80��b�qa�w8�>��/4�q��BcaQ[Jd����>��icJ%�7x����5�B��̘Ρ�p�Q�b|�-χS���:��)
0V2c��8J�x�
^�T2/�
1�
�o0����:ig�V��c�p0^*�H��Nh��I�x�A���b�0���Ao�1Th�p�xb��UB�D��^̋�/^Y��2�f~K��c�`0a��"5�H�����ԋ�)��)����2�[I�и�Cm�،#��¢������t��9�q�|�d�p��\!E֌�+n�D��씂���|d<�/S�r���`�@����s
IQ[:����R�oA`��^��e�p���W��⹢��#I)<�)��}��U���l�x���gH5�((jS�ƎX���e�×���l�j�1\h�/�H���S<]Զ�KR
G,�Ӹ��:�k�����l�Df��XO����D�;����0����\�x�2c��6v!�����������c\<�Ϡ5�/C���,���(
3�x�G�q:�^QQ�P�?��xȋ�����Yܦc��)�3>�fu��Em����x�x1�;��굏*f������IG���$����ʼn�F�}����FŌ1�S�)�C*j\P�&��A���%��̵�W3����(4��Ը��M,�����Ëq�wY�IW=l�j�1Fh�.���YE�����!�\\�x��ޟ��@CcE3��#/���&LQQ��(}�`�v��U���ü�Qˌ1BcI!�4�P8k\P�&c��'���wPe���f<|�^�R������,�k�X|��^������k�1Fh�(��ρ������;�t��Y/��Eƶa�����d4��rh,3����OP�V.�,�x������)����XXH��TĂ�69����e1@d��������'.�`�q�����MR>�G/Nggƀ�;��x�`ƆCci!E~�^PC,)j��s�eqz�x��w=��/��:��Fˌ���B��+�����!��8���}�>��s
36K)�s�:AňayQ[:'b���9/�)^��;�0~�1cs������8
A�B�"�x�	!�����������+���L�ؗ�G���(�@�X+����R-��͋�Ș��Ix�����g����Wc=��Em8��T,�R�Q̰G
c����'��hE!���XQԆ�D����N,n�q��յ�T7�f��>�B
\��4~����
g�H�/��]y��}�8�WDÅ
5ֻ��40��~�f�J`��})ohleƉCc��Uc=��^�yyq?r�X��E�1ܑ;4��q����B�������7��P|��ɮ+�?�MJf���24v2��iBc��Wc=��Y"�x�`��ª�_3n�wh�0����Y��R@
z7>?:�Ż:�,^�=W>r�xÅ$�3����'Z��
)���D�Y!E��������R`���*DN��nv.f��+-�P��ލ����#3��/�姟J�󻠷f���bƉ�>�B
�O��wUH�����������7��1�W�?�
��q�� E!p��X�Xm�14c�Ż{������fc3N���R�A�:�(VR�=6iX|�B���m��s�ZٖX��\�0
�=�8A�<^H�*t����&/>�,�xY�$�`<���0n�wa#{{�q����*�y����^{pF�xK^\�<�_J�V>�>i�o���+
)�B/�߾����ңC�b�����0D��0&�M�8��G��*p9��/~��xY,��?�)E;�{������VRU!T������/>�yY,���X��Ø+465��k`!�7��2bQ�~{Q�k����bɋg(vM)��G��3��/f�[IU���K��߁y�R�yY,z��Km-2���o~�f}�æ�B
vQwe���=�x7��Lt,��woRpD�b����m���f846+��@;�=�ث%�=�xΑ���^|�"E!q���S!��>4�7��k@!���0�߯K�ȋq5iY�K.�������`�#��9�q��.�гxNc�U����#^���d��v/�C�����RA�6�io���>�B
=��ظ�/���!/��8	��YHQC���/�o0N�3[R�Y�,��J4^\�Q㉖�O��ŗ!E;��� ����Ҍ��}�R�. Ƃc�w����cc�/�E���@*�-Cc{3+
)�,~Ÿz�oĽ�4��a_j�z�x�����e�C�1CeUW�s3N��Q-��Y��q����^�1-��^|�E��"�H���q���8dh����J1�K^|@���,~Z[��y1Sd,˜d僰+�̌C�}@�z+ĸ�X�Wcj���B��ch�q(D�y�؇��f/4v.���&��1Qq���bP�Y,.ޕO	�[d��l����\�p-��Y������^��1�7,��n�Bc�ȸ�|��L��O���ڌ���ޅ���ږ���:[՘��5/^m�o�qB���Ff�ϱ�B!�埯�R�,�q�
���;�ޑqm	�c�ޥ}hlmƱBc�B��Ÿ\@�0�uN<��My�C�ŲC��S˜,4�2�k��z1���Y�Y���,�g!�|�,2~�����Ԍ#�����xy�N�ŀ3�x���1]d��w4���<����b�!^�1��P,��52�}~�|$�O���،���_.�p16�bL�����bR0F��mT���>3��1��򝦐Bf1��$,�)�j���7j~��f'4v.�P�1P`y1��,.^,��UF���b��Oe2�:�;��6� �%�rE���@$��䏐��,�Yv�6��е�33!��R�������סR���h\C۾x��q��8vW#�]�XD�YH������8W�����+U�m�|d�4�$�Wog,"����;��쑎�����s�v#�Rte���%c	i�.�(㙋0$��ѳ�*j[�ue�	�jW%Hc_g�"���`����PQ���8��b����b���-E_��)\��'Hcv!E	[������@��e��g&N\ܕ�*'�ݕ ��Xg��VHQƦ/._�[ϩe�q��;���.�{K�W>by��︒��Ҙ]HQ�����p6��c�y��Ƹ+cYLԻ,A�����������څ�o4��ր�r�X�R�c
e\�4�1?�
)��؅�4���ր��a�]K��m�=�WG2Vn�.��c��
�b�cf���o�/a)B��r��'ҥId�nۿ�c�h3�����ū��Zw�[Z�8{�88Θ�)
).cCh1���0}qZ���X9�$?�1����
���k�"�1���8%�F�o���̖V>��g̗ƻR�1PH�f1��Q�8��b��]+���'�kȘ-�%
)ʌ��h,�_�(�B�)�kW�M�|T%�	Θ/����`|((0��B㿡�x�ŋ,^�ɧ(�S��|T�M c���(��/���F�t�R���Ʊze��w�$y�O_Θ/����`�Q�f�?�q0~����㮌��E�~�/N c��)���ؽ�m3C2_^�,>i��h�R4���9K�#����x��`�~.N���Q��^�[����V�DQ���?�Ҙ@�,O�RH��1^�V��h$���P۪��~Kў2�G�1�-��B�Ѣ��,F�8D�O�^/{
��^��R��Hc���H!��wwn\�BB�Y�YH��MY��q]�؁���B�����	\�BD�Y�]H1�i0�l�򡥌�و�N cB�R<����}q
Q��8%��EW��P��Kc2�~v���I/���0�fqSwe\qײ�G���6�B
���s��h�����=f.��8*�GsEp�O!QH��1^HQ��8+g����(ֳ=�^� ��Ș5�&THq1�G�p�+]|��]��a����X��cBQ��� ��y_���h)z�����Ș�)�
)|��!�A4V��[�ws73�֨��h#��	�X��c~!��h�����Dƚ���p:!3i�;�Hc2�x
�B�0&�mEe.Kf����ݘ���b�ys��6����/���x.�0��8q�ӯ�������@<��ӉI=��s�B�$i�TH�q�u��|4���㉌Gd	��bf�kW�CЫ�x
��<�R!�y\��?�e�IPl~u�K�;fwe<A��>�Ņ�a�UHq	��ڌ�D�Q+��/dl�ޡ\̴kenM�RT�)>����B��c�Y��F/��d�z�Y<�`[W�Ӊyw�F�G�q:�Kxb��p�t�墱P'*6�ScY\�26��Z�mIc����B��c.~:�h���۾������U�5���ڈvpƾ�B��c^!���8�d�{�x����2�o)����4E�R\Ɣ�6;�q4�=�7|���@Wc)�V��ފ�|<����
ATHc�)R�1b8A��?,ӔtK�����,#��=>�;2v�r�+q1��Y<L�O����m)�2��x�&N_;c��=(��`�*j��Gc�,��Y�,��RlZ?�,�s�0M�+����[2Vc�OQ�G����V1>��lk�xrZ��6����B�s����y��@�E�ճ����c4��2�&���)Pg��6'�1���������ŧl4���!�a.�[��]�x��;ݳ��ht���S؅�j!�z\|�G�؈�QT@Y\��ظ2��pHƞKx��	Y���Emw������=`;�U,�.tS�AØ�)�ɘ�)�B
2>.����"�ƫ/�]~����+�j<EǙ�(���d|\�5
)&�X����;{���,�i���0<-�	��@��.�^�B
�U
)�@,Cc>���;��,ES�s'��p�}<�!c�`���hL����A
�����0���a���4F�)�>�B
2>.
ƪ\�1!�����4�b��h�x��@�/2.�FƄ,�X��4&d�]Ԗ^�~=ƀ5�OSƏN�Q�1���)R�d|\�e�����1��O���6�֔�ۊ�)�˂�B�[2Vc���I �h�s.��1��5Z���74����4�)��X�E��R��-����(�Ka�BGd���vƋ{
��Θ��8����O.��f!�L�7�֔���i��=�l!�	`L(�����4&fqr��<�Ч,5K��X�rtL2�=�"cB�`,UԖH>�Ʌ��G�x�0��/uV��x
��ƒ��`,VԖH>���w's��w�,�d��n�z
�06r��S�Rx����Ŋ���1!�3���ÿ�,f��كm�x���]��HƸ��)�^G���\Q[���|3�ښ��ʞ�ф�y�3F<TH��b�E�����X��
���[
{�-`����4��{9���6�5)��C�X`������,^�R4e|wb^
�{�=��aOR���\��1o��޻�q���)��K�St<g��gR���\|	7�9\��Y�,���@�k���i
Lc�|I�#3��bp�%�4f=	z����-E{�]}�m{����_�`��\��Em�Y|И�=W���g����=�e�<xQ�MGpƘ��-���X���>�4�dq�Г=,�DG��]���6!���a��…��b�9�fQ�($��RӤA��ՈtUdx
�B
�e��Y��1��b�i��($,ES��i���n�����]g1��B�.^��X�R4e\�pۋ�4�)�)00.j�f1��NY<r�����4,�`[S�W'�� ���1�)d) 0�.j�d1��l.>�O�f�n)�.4���C��qƸ��.���X��m��8�چ_�t���)�:=EG���)T) 0/j�d1�ƾ�w��,V����.Щd�m�$c�S�R``,�x��b4^����A��-ESƐ�	te�����-���X��"�a4��=9H�c)>��"FO$KQ�p[�4�)�)00V�b���|w�>\|&d�,Ao�R4e�q�K�H�<�f!��\�'C,����B�
X����]����/���8$_��Kf�틟y�G������OA���=�z!�Q
)nB@c�B��]�1��e�MX����/L2Nx
�B
�C�%B@cB!��+Y�����g:ϣG�m����8
Q�o�XV��T�VD��@���a�]�4q�׳w���.ϑ�=9�Tp��R@`���и���Q�Ç=�,�)�{��vA�S�mS8���Y�/���XIQ�4�@4�|�.P1��f)T���h��(�$�%O�]H���eq��;<��b)L����xy$���x
��)j��X9�%�ڰ,6K��>|4���$<��6E���"u!Ɗ��b��q9Oɢ6��b7���B3,�	x
	2y

�+*j�X9o�b��ɢ6����R(W�,E3�mIg��̅�*j���Em�\vd)�,�=E�m��)TR�QڹE��>UԆ����w
�pjO��qv���!.��7?2�;s!�j��h��1�����BC\�#K�P�{_lk�S�q�~\�
) 0f.����@�r.�N�a���'�B�i�w����OR���g�����cb.�s��
�qϣ���
_0Κ#���"�8J���{JB��q�S�(�������@�B
�wa�8���xW�”�a��s��BC!�:)�Y��1���'Y�y��,������0,5Ʒ)2�h���c�Em7Y���q��J9�G�3Kaa|إ��H���ӓ�B
��s�c�YHS,�7K�����MO!錽�PPH�5�����E�m\�v�-�̝+���%A��u$<��6��)�)6���,�G�m\�����<Ka�X��h�ST#�S�Rl�N_Q�(�a4���X���f,�-�����q��S�R<w[V)��3��8�K3O�7�Uc�$�w;:����ۦҘ����V)B+����,>�4ƹ��xv�T��\0�v���A�S����J^H��c��@�,>�Aи�ǧw�Y|�+lS`)L3\ �+%��剻��C����̅,��@��{DY�U�Y
ޛ��+$�)Ķ)8�.��V)8Ao�b�R�O�U��,���n=E]2v_�) 0�,ژ��)/���`|5K�s�=��H�Z�9�P������������,~�9o��\�Qd/�[�T��b[�#N����6��o���c���Y�q����pf)�n��F�5���d+�	)�;����@�Y�����W��H��-�-���=Hx
1g����ÌqEm�,��8���0�՞ݙ�������%q�>�mS�O�n�+��U�6�@�B��:��Z�˛jn�W�<E�Sȑ�W�B�]�h��-fq1�)n|�D�ƭY
S�<��;�qxK4_!��U����ƥh���C�~c���L"�Bl�"�1]!�s�6���qp4�ۣHX��Y��d��R�b�
����%�]!E����nh\�GQ���,Y���:�.�)��q�B��*Ef����Ӗ,v��œ�U|q{��ۘ� D<��6��_�pR���\0n���v��h!E_��"��R4���e��QRx$F���,vc��@�K�z,�-�Q])	O!�|�d��/�c�B��,��1��`!��Q��EK��=���շ)Oq�b8x�q�Y��n����	a�8�K�+p�Y
�`Y��["�B����	�A`L��ø�5���q�_U�z,�-���SH8c���9��@0n3��+��*j��%c��)�<�G�m
?�H�Em�qcEm�q '�8�變/n�R,-�i����y�$��>Ȓ�{%c��6���K{�@��؅��8r񪥨VHa����m�>O!��x��@�(7��a\���EEmQQ��3K�M�Z��<�_�Π�i �½ zCB�!��U������cTzʮ��nk���ˊ��f�/o�x^t�)��b��{�n�h��Kj����m��>�Y��b����y��m9s
g\v)��w�1���rV�����t�Zb�X����R�L)�6�q�4e1Hc|`c��6w
R�[����%�X���w=���2�)��)�S8��R����S�X�#�KWf�c_,ݾ�)EV��)��q\d�!�s�;`�Kn�߰��1�=�8��m.�^��63.���|����U���S_y��V��X�}'����\�1��ϵ�4�kY�VI,���'dq�W���B�J�y���~��<�[K��;[g\���Sg<�{�1�s�y,���~k|���*,�o��I)�`e��P�[fS�f�C�|1u�#��cq�:cHf�'��J!�FF��O)��8YN�>M
j,�;c�1v���s��34�/~�b�����w}��=�53�4������x���e�!�5>�d`���]��;z:ؒ;��9�~f�4X,w�#Ƈd,F�1�1�0�o���	���i�V��INa3M
-|1u�L�ӕUg<k�_��,�z���Ⱦ}w�q��,żk&���{Sg<�Ŋ��c�����v�G���pa�{J�O7~�8+��ff\�5ޙRPğq,3��jFq���vƄ�G����I)��̦K)�U�)�5�(�X�G6����hFA���)�^�:`�a�.~J��]œB��YccSġ�Yc�ų�� ~��'Jc�(��H)�1��^%ԭ�9��b����1�`�b����zZ�b� ���/'�1E�ր���;z���)�P��(k�UH�k�Q0�)2���0�)��Y����¥2��Θyg]H1u�";e�Em7|���w��Y���~�����nfLO�R��8�IQی/fØR�����ݛ�""QR�7�c��h��g�"M�B��F!��,����S�`,8��(���C����Ia|�ޅ�z>��_�ƃu!.!���V-j��Xc��o���ݤgO)�Rd�)t3cj�m)�Q�4�����Ec�uW�B&,N�}׏߹��<�t�)*���0�bZ�6��0�/�g1���b)E��bm�7v����3��Q�_,�1|q�ھ�%A��&��F�1�ݬ���b�	����6�/����/>˘��no��b!)��r���)(��B
�b9��9X̙�˜��,ε}׏�XX��)�錋a!E�D0�ܳX\�6�!΍q�9�w=�XJJq{eg�a�C�Y!E��Ƈ�,f�M��3�5}�/c�n�."KV��'��d3Mm�)����b!Emܔ�5}�3c�S
׺���t/��θ|�,��b���!h͢6qJq��-X�n����X[8�)���M�B
9���Y,(j��d�	�Ysm=�XPJ�)�XMS@����:�`�fQ�C����mX�5Ɓ��zJdq�d��θ-
)�0>�e���
zeݕ�Tև=�nӳ��J)��S<�SYR�w��(j�R@�Y�k������>F����,)�1t�X�F|��ŧ�,Nf�{�q����v��B
Y;�+f!���
ze¸9�9�8�]O)�<�|�?�S}�-��b���Ң6�X�RX���}�S�����)���ژR�s
�+�EEmr�6g1c���KK)��S<��B
yN�ru��Emȋ+�:�bqVc�S�@57�Əd�T�q!�<��~c����b�Ƙ�[^��<9�G�i
Ⱦ�B�S ���b��6�b�^��8�]�2������&A�ܸ��Y�3B��/�!g7B��]�*E9�����rs��<�1��B�8�H�1����u)�b{�M)�#�	�əq �C�)b�V):u�X<΢���*I�Yl0�T�;�\G�b��Z��7o�)�"U]׽{^tS�Lw|�R՗aq��X)َ9Eg��)b��T�|�a7�D�;�d�{c�ʌ��)bk,��/�b{�M)�4S��T��lS5�R�gRq[zmJ)��Rr
�Kn��eZH!k�I}	Wm��R�]m��4�xAM����YT���3���R��m�[n��Ma�R�]cɮ�����d�)��͔Sj�jSm�)��ƒU��,��ڔRL7�xv�Qm��I�m����t��b��xh|��¯*z�0�)Fu��6�B
���U_���c�W)���
t!E��QA�M��,��+�����2� �B���G6]���d���wJ)���0�s�n����"f�hlT_��&c��kSJqsN��q�]H�XA�E�,6)�>�RL<��ju�"Gf�Ư�X4Na1�Ԗ��k|���K�7�|am��׌�y�hlե<��3�J)��<UPN�D;�v���//�X�1	�mƘi|���.��
����d|��Ţ��"�ƅ1��w�ԘMN�)2�)^�ƻQ|�h����R��(�5�C;�v�̋�Ec&ی1�N)ŷ��8@�83N�ƣ�b��+���h|�Ȍ�e��Gx9�A�����T�e���1~�:�ȵ���c��Ýq[���6���^BS��R��y��Ϗ%*3j�cјf9P�1�N)����[��AȂq@��c���5�J)��E:f�3n\�8�W���:�a��s��R|��pA Xf+��[I>�t�1��)��!���֦j���wm�2�W[���!\P1·��ӭ��;���bX�O�OǗ�#�h�}H�`}�����o�&��c�ǫ)�?�b��s��R�/����Pm
�Ï�u�u�_(.�_��1W�M)Ő؞7�Fx0g��6�gf�dÊ����5�G���v���9��Ɍ7�'��1�q�x��8�b7�6�1��)��)��Kl�bc��_<]���#�M�X�;���<�!s�36���p_�^=�O���dq��X)Ő�Ry�շ�e���1��A]�����8��5c��8�5��)6�'��1��,6�O��RԤhm�aP�Yl3�d�;��8u�؞q��ϝ1#�]����6c��k{dڔR0�YGxXf����̾��ׇu���/_�=j5�Z�6,�+!�vk,_<��7���'��4�J)��y̰�g<l�w�3����o0�.Yl3��i|���3�q�0o�[�)V�"z ��/$ƺ�'�xS�a�7�������ޫ��Ό
��d,/�G/��B�Ջ}`LVצ�bWd�����Ma�U�X\�+/�PZ�`M��`<^��G��I��)�6����h,.��ś`��m Ec����R��8W���̸�Vk�w����i#/N��
7���y�	�h�<�0GN��M4��[Ŕ�Ձcz��g�\umj��_:Cc\���V��).~��Q_��rqc��յ)���s�Q{�	��И��8�O�Emq�8'�M+�c�Źبth�ͦ0��	:������)�0nڱ�X)�'�)Ψ:�m4�ť\\�����o���ݦ=py�^�
o�eƦ�"\�\\�8Ek�X�.Y2�kSJ�'��ơ1��žY\��G�xJ�b�!�x��E�\`��b_��
��Ó��7���^���ϋ��m�c��)�����x���ԧ�xӍ#{�
�N2�c�B�E�ѣr�j6�i14�p�~)�m..V�	��d��*2n�s�a;� �e?���c^\L8�[bܶb*0VJ�����g4V^ܟ�?x�,W�v�2&j��a��ɓS�\�
�j�U���7o�j������'c��;E��&g</I���x������q����X�wN)��ig������w) g0��1���#�U��gp5EV�m����.�{��^|�-2��
�1��Z��/`�1��"k&��)��b���>2�c�^&���	83.�,/��x7U��R�f�U߲�2c.0��1�O�³�6d5��$/��xWk���i<@�L
*2��s�h�'��!/V`�j�+�r�
�~2fj�P���I ����&>0����!R0����X)E�����Uȋ�sq!�)�}dLƺ�; �}��H264�w��t������%c20�����JZ9V�)��g�Y���\lo�ƻ�lž̘��R�E���M񌭦(?���]�b�����xq�1���%�1�ɸh�?��..��M�d`����B��̌
�����ы�������r}GU�Ů�p5E�,/�����Ș��R��H�c���)v
)R�ыW0�8ɘ
�u}w\|'\b4�3�s{�׮�͒#�-o��M�c�q����iqd�,��Rl�š��G/�������tL�dl�:��D^l`����ť���$c60�<$���'׋]�}�f�cx0���{�w����F7���R�j���xBVS�&yq�ƻ�M�˛��yȘ
�u}@��	��N�Y������V^|8Л����X`�"�;"�3cS����R_�ߨ���흃��x�����XC�U|5E�,/v��[n\ް)~���K�t`����hܑ��$/~���ޠ�}��m�X`�;�Gsb$/���@��V`���韣���JdLg�_�n�0D}i�[nD�
����[W�Xm�z�ʫYr&���>ϒ��[�캛�7p�M�ﴇ�ѽ��q40V��E�U�G4ޏ�M�^�?�"�p �����J)��EB4އ��y$/�m n�f>x�S_���Á��w4ꭅw�Όey�a E�&h��@�~R2�q80V��G�Z�J���=3ch�@���	�t��R�k��q80V��H�o)���nde20f��')neFQ�:�d'�p`\�)�R�pet,�ٕ��I^���pa�[��d�����J)��F���1$/����^���,�;#���F64ލ��I^�)2�p�;��@���2��L�#C=�C�=���ŵ��`�82�!襼�c3��Z�H�;���q�<��1����
ưaZ/^����q80VJ�&KNk��%ch���~����{g#�x`���,ohoh�'C���Am�ҋ�rM
d,0Y=��K��t��D^��ū��t��95���X�;��>��K��ʋH��w�7{����@��X�;BY���L�P�?H����k:��?���N��*4ޗ��Y)���;�)�ǐ��SML�;� �������y�a Ň���\\VS��d�վ�T��ξdM��o_�A=4d���F��X�;N=M����93�fy1�8�!�{U1��J�c��b�GDh�7CY^\͋q�����Z�X`��Bh\�sfM���7A����q@0V��V��5ؒyv&chX/~��8S{1�s�f2����ƫ�v���3c(��w.ޚ`�<ư63�;�Q�`����
�h�������:��f��y1���N��B�}G-�J�ߜ3c����1)�\8�2�j�1�y��R_��������x�:�Q�T��Y�X`��]��~�ysΌ�˰^�ׁ|^�M�95���X`�>��g2��R��z�%�&2���m���}�������_�C�Xͻ�)�ޭ����8޶�J&��ݯ�B��1tы�)n.L}��QӒSc�kc�a��t�9g�PЋ�śy13�kZRj"c���w7�\/]�ś���p^��#�`���V2�j���y�����C�yq����a�	��Oɩ��nkӾ�z��,�7Cy/���xʕ	�9�z�QEI����mk�CW��o
�Ư̌��H^<U{ࡼ	��o;�[j$c���w��Lh�B2���ͼ�oF�;��55���X`�n�9��wfe��G
����U7�q��]���}�{W���N�d]X��ȁ������)�Mdl[�����	��EK_�3ch/�p�j#��B�-d�kq�1�lyR04~�&c(���.�C{+���� �h$c���wE��F��2c<ۺtܼ�o�i[�TEe �O��N�Ѻw50��Xh4~1C�{��ŷ���ŷ�b�v2���M`���3Xq�hn�e�aa�������hS�Ē�����ݕa�9���j�̤��3��u��_�*���cfv���x!R�����[>�S(���G12z�^|w�B�1�XeF�\H��bSM�|_�j�c�n��gƐe/N\<��B���G�?'c�靃1���ƛ��
z1�_J)T�	�a��ն�����b�yj@㆙qY�ު��'>}�^�����!0^F�Tӻ��6ڍj~n�;i'FƷ[oӋϕ]Usq2e�Kɘhz7si��ڍl��I*3�ݢ�B�|^��^.~"c�����Y��O4�!�[0y�_r\LTH��b�L��� :�ڏޯ��2�/�漸�#��)��/"c��]i�Y�HU��d#GƷh͋�y1����!�R2v0v0�u�6��B��]���\��6����ˋ�+Șhz�`L������$�zK^�>��3�f��8�ϫȘezW@,�+yW���и�Ɍ�y1zڦ�@pb�B�o/^N�L!E�/�}�(����x�؍�^�\J������bH!�J2&��9Ӫ�W'��v2�qR���!?�Kф�7�M
)��"cc�MEy@c	2N�ɽx�+ǫB/�D)�XK�ӻ�î�ﴫ�>ʆ���8)����]���U/���:2f�ޕ�u���(���x�����̱)#0>�ד1EH�`�,�h|Ȍ��ދ�wS,V]�L��W�1���|@�C���)W�� c�7{��GTH�rnA�ƾ��E�˷9d�P�b�M��
)ƫɘcz�`L��_�0�� c(2{�.~-��6�B�^���B
cr�- _��Ȍ��10�)�C1I!ŀ��ɘ$�(��OU���h�5C=�'#μ�P�����B�^����}_[Aܻ�6��v�1I�\���)>�ZH1 �h@�[���U�����z� c(Rz1�x�Q��uzq������%c�!���YE�Hf��^\,���*Ag
)��XK�!����E�4�ۚ���ȋ���F�J{L�
�ڐ����kˊ���x���t^<SH�O�[�����6d����U��xTwO%D��RlJ�P$��B
几_
)���kC��C
c#�ZZ4��:3���/�H_z�û&dLR8[�Y4>Ȑ1�S�qU!E�j��B�^܊����fT��h|�Ɍ��ƋC�L�x�l8Q��Mm�B
�-�X��+���v-�h|�ME/~0q�!:j=�)�����XwHQ��)jߪ��ƹ��LfE/�O��RD�Em�B
pqK2V>�s0�����{�!c(0xq1/��NmQ[��b@`܎�U�Ɩd�B�1t!��_�43^Uf�B
�Z�����ؖ�q'C���E�_ô�
N���mRH.^G�D����0�Ɍ���݋����G�ރw�/ޝ�vd�GqH1ƾ��RU�A��a2��J����-����œ�ûVd�P�6�~�R�+ʈƿ?%3�/�	���X�2V]Ԇ'�Q�|{2�R8�S݂P��/Q2��E�������-��"c����e��O*3��Ջ�f���{q��)�g�jC
��r�0DuӉ�2ƌ31��ĊA��i�Kؕ�I��Zƫ��:�lGB��Tx�q�_p����xv��V!�����b�m,�Su�I��q�@z�r�δ
?����
)oCƸ!Ő�Xߪ������Ћ�yqc�B����d|?� E��"_���z���΋O�Ż�>S!�޼�(3
)�*�����13V����-AK//&�(�
)V/�'cCH`zq4�=3c�Ӌo��KнB
����5�0>���ɓ�Ug(/R42.j�QH�\�OƆ�"�8���{͒�f�* /R����q��B�x��2�0>��h�9L>��q/V7���Q�e���:��OƆ�����t=�8�f���@�w�[�PR|y�{f|?�`e�؄�/��سt'�‹o�I�+�X���-!':�&�Q�!��uwv͌U�ţ��fĸ���B
���Rp�S��Y��g].�ɝ�s>[�ا�M􅱐b�'�ɢ�P���73V�1�nܼ�Ża!������P���d��1IJ�0��}���M!E�q(и)�fƪ�?�)`���Ո��x�(�8h�-2^�؝�Dž���_H��Aƶ������=e����W�sq?��}*����z�����H�)��.�cvٷLd�ŏ�Պ����@�B
����-�1+2?"�s�Y�L��*�+��_�B
�e�n!�r1Bfl	)X�)6��A���@�9��)(�
�r1������#">i�+�f�J���B
�b���R��R��q�����8g�ً�D�A
)*C��d�X[��b>k�k��W��\�X\.D���B�����)�`��cw���8gٷ�B�ƅX��7)�/dl�j���)�O�g�&�g�U�〱~\�
�?e�SH�f���cVV
0���i��!�8g�q�X��������N!�zq�`�ؐ������xȌ��s�NJ#c�ŻN!�z1Dfl�YI)�B�G�A�9�c!E�i�-�R(���5��k0����xJ�q�l���b�%�n!�z1Dfl�b�-�c�g2�Y�
)�'����B
�b2��ơ@�[zCȌ�ħ�₌�R���cd�F/0Q��	��s.��K�d�7�ȫ	���-��}[N!�И�7"3^%e�B��_��R��y1Ffl�b�we�1����3��'A�'A��Xϣz1�B
�7����q�Ȍ�����ڊ@_��C�b����ż��c(Y�¼��B�"eg.n~��B�*�b2���w���d����M3Ff\U60��:��:����^��۽�t�-�L/��3
��Em��T��B
�iY��x2*�8hlU�Ȍ��&Em?���Qا�_R�S
��cV8
0����P�x��xx﮾�zq���z1[;�y�h
��|�ė�$3���م�aF�RH�y1Lfl�i�(�P��.�8d,rz�ˁ�^H
)��
4�����~�w���0v>��`�{H�����µ̖U`�!S��9IP�z0���SO{@�A2cq�u���[�@�(RHNfl�i�wƐ���C���k�X`�&\�C�=w^�D�/&mk0Ɣ��kxLf�t\��<�B~`o]H���d��"�8dGc�4�q)u��ď���l.S�7,A��ً�����.�8e�̸���ŋŻRiR������8E|�jh�2����B�x���
�/�"cs`L|���{2���Ɍ���g
��R`�eIOx7�
)3�x1k�.�X�M�}MHd<�r�q?���k���]�1i�.�Y;A�7��X�x�����2��=^L�C�в/�^�Pd,���u�1tF!�����8�ŋC)�,�Z`�᥌�O��r�lT[��2��=���hd��yi�W
Ak'h<`�q)�����e����u)$.F#c�SV�~������(3���⾑b��L)ޯ�����wl&g�.�^f4&ϯް������&h�=nR��^���W�0}�xs~ee7_�2�I�����d ���pd��b���cwg�ő��������"s��@��ъ��8��zW`L��I��c�ʌ'9�xy��~���C��щ���W��Šc
��42��c��
0�mpQ��Nj�)(��D�6�>ް2�I�&�S3�^#��Y~���ݍ�!E�1��h�^�K��K�.��tWP��Y�����[�c����`y{ۇeƓs����o�?c��%c#/���hL�_xd\J��x^�S+��b%c�(������\L"�,�$��1Tf<�q����΋���Q ���xG}ɥ����i@$�Z]��t�P�X
�a1$;��7�0��qmla�d�̸N����`<����"-��$c��V���d][X'�2�7-�.��%���AL2�6���d��1}P��	*3�y���mp^,'V#
L2v�xc����kx)��q��7A�3�h�;P2�y1���{�iOh< f�"˛�u���A���Ms1(;���Z��=��ح���Iƣ)42U(/V��,'��C�cB9�k�H�
23=`�^,G���S=���d�o����3ʊ��X.��Xe`,G���Ƌ���3,{���z�)e�{��z�'���6�X>������K�~/&��cRl�Ȃ�ld<HQ���b1$x�l`��_ӕ~̌���k�Ԑ�
��)X�H�
�_�H�E��++D�i�z���x��A�W��ݨ�`10���8�0�c��BP144�2�x>4S��Έ���r������b��]�1�vm���h2.�yO�tV����d��3�N
��|��e3����U��i?F����͊q���]_�@��]��o�2���ۊ�q�Q�q�y4�����6n9��m��ˌ��fp[�)�EE�b�bp2N~1�������3���'�Ň50�/TF!�;t2~����t�ٵ;4NɌgd<�c%c���:?��̠�3�m����	�R�>9�V���O\Z��8��{3�*�8S�էg+e�oF oPi���]������f,���8�aL\o��V9 ��b�/�ņތ�Y\�XF܆1��R�*��3�]�V�gaϷ��f,��jIA1f��W���d���Km0^Q����Y��;� ��ƥ�"�{d�*�EX�
f,�����e2Ko��������I���XQ���X\�ScK��ƐÀ�S��7�J�y�]l(T�1�kSʌ�0��e���7�q~�GN(���,�(�a�,��V��Y�z����`\4��p���fqD�3�3ȧb7�fc�z���Rg���d�8�J�X�ǁZ�����Q���h�Fyo�8�����p_�ŌX�0�(��K��Qƕθ��p1n@�3�`q����3����Xhf�?�q!��Km�z��ŕ���b��A �e�~���8>��Ŋ�-YL1��5�ST#�������bMf�↉��;lA�g��f�~!�ǜ��bUf����!�art��(�͌}��h�Q�ˌ�79
Ř�����È�WP��dq����{wu�16u�uTo�P��ˌ�G��`\F�63aq�2��dVQ��Ì�QΌ}�3�
��������
%Ř�J0�j�g�Kn�p0���}�>3aq]glI��c	��F�θьoK� g+4�e1O��K�m��J3~ćX��b�f�����km�LQ1�f�*�ٌSµ��X�����:�-�a��<�l�F�f����ť~B�ð�����/�&V�X|���0��v3���~�~BƝf��jIan�0�(_ށz�`�4��N�v�x�M�;͘,��J2�b`L�.�ٌ3�;X�i€q�C�E����AT@�.bƍ�q1>�Y��Ɲf��j�go�0�X9�/*`ơ����,�R�N3FcquCip�0�(_�F��+~�‡N3.����&,w�1��b̰�茓;�o�+	����`�i�`,n�2��W����rA�J8�Cw�q7�c=�N3�F�4c����<RQ1ȷCw�8g�|x&1&�;��Y,��d��0����E֌�s�c�EbXw�1�[�Š�0/b�bv�댏6�U�a�2c�X��0/��plտ��q~��=f�tt׸����h��-�f:c0^��.�����#�2ʷ^`4v"f��f���,)�m�1DQ18�7}��f��7�w���n,2�N�	}/����f���UZ(ƌ���xs�̸�vh����6M��D�����q|��b4�c�r�b(��H�����![-$vLZZ�|��K�V�cc){4�T(4^�ّ���ҏ�<�sfy%�F��#���r�Kߌ?�q�^��Z�xH��}�]�1׌�72c�3Np��g+e��p��bo�̘`ƴ��y<���C�1����ʌe�w��׋}�cQC�-�d�k����T
	�!}*FԀ��v�]�̘ދ}G��(<��'v�!3>Ό�q�kh�|�cJ�r*�m��=���W>�G!5F]䱃��vַ)���XH�Ez`֟Ɍ�0c~\��ŕ�D8�T�Y�#�X�>���L(�,�Y��3֦�b��(|�(=�pf�oS���(R� �oTb��fo�֦��(B��p^'��>j��ۡ�Qxݤ�����
Tl|����3�Q8C�3 "TRc\\f����1�{�z����"ȝ����Xf�(|^R��f3��fƺgL�^f�+���R��j4e�3��7��_Jʻ��R)�����x�=�)�Z��
)��_�A�)�>z�3�>��I��P�)TG5��^㯶}�z��>�=D��0���`�`�߶Cw�j8����	�X�{�Ei���.3�G~i !�	J�{\;�77c�{{q��A��#:6�<����͘|�8V��A���8&6�b���q�{��'w���Kɋ�D+�Ki���f��@�+���1��k�vɌۚ1\�3�B
q�����Cf�|3�kq@BzL�A��F�i;�6�Nf̾���]�C �e�n|��&�D!���ZA�B����=c�J��w˃
��v�j��Fߦ�k���*t�`yP�.R�#�G.R���Az|[͸Ϧ�Z�w�^U. ,*�uj[͸Ǧ���h/���<��Opc�m��>��=Q�}�8�a}PA/U۰�Ŧ�Z��Nß�>��w�aOo�n�/��z�B
����N�����wF�QA�9��W����?!Y�H�d�^~US�W(=���㦋��5��
����d|5�3��g�-�m�����|�3���U���/%E�c���t��&c�=c�w��o��@b��Bf���`C�s�vg��PK��fw��w�B���p)�C��A+��Ӈ랱PC�5�;��|4l<��x���)5]��M�+��yܟ�����6��v���/@Q!s�����Xu�R
E�̷�{�d����G��3����T�26�s�A(*�F���xb
E��))��xJ������Ng�g��▋7�,@m�d�1��&c=7��"` *+/:���M�req3�{9� b��c2VTq���BQ!f�Q����Y���G��8�X�r\ޓ��{��8胨��Nxu����{Ƣ*�%9@E�b��έmW�)TU�0��d,���P��"A�6�5����l�$��6�٢|&cew]�I"@A�Ƣ6�ZŪ���Vq;��:PYTm<�x�c��i����7�a�ƺ��Nj�%k
q�]��8�A���<d�PWq3�Im��-����CRy����8�a�6~~��5�W��M�� ���m<��dc��X�f6����0�
�p�'k����8X��J@�_,T|��
��|Z�]�����1"�ՀR[$|W�C1��))8��6���D��qq0g10��E��K?�p�KL���m�d�3��ҀR[�!c�~�b��"���+��r��
>����f,fY�ˉ26�.Ε�gc�i�1�_�x�����},kc?�Y�]�N� ��p�����7��7�'(.v�f �(xF~��x�z�{w��0DQ8�0/�T���
%3A�9���:*�{0�e��]�S�_�.���X?�<������x�<O�bœ��_���'1�)1��O":P�?�l��1�)�-V!��w��d�[J����l�0��?��b�✍K���qG�-�~�!*k������CZ�X�˜��m�]1�}b�<�b�"s��j�,�-�{O�!/1��P �CE���\� �b�
��{0Ƶw���M�"�%�o?*k|z@�M�Z�Xw
=w�h��!�Œ5�XGo�1-�?A0�u(�A�Oo�X�d�� ָ
�N�X�u�ƅ(�g��@Y���E(��w�	r��!�
��~�5�-l��M�K<k\��	���v�Œ5��b	�nH̼y0f;�q>�1[, k�kl�t]�'�@��|!�v�C|۝-�5���0[,,ОX�P��?R�)�8h�.��v�O�yH�0[,6ҶX�4��Z$6ԾX�(�_�p�4k����C�	�vƷ�1Pse�5���%�j���I���'`�'M��L�K	��ov��Z�X3��d�n"b(�v��h�@M����I�����Y�1�Hc���2b��4�|9�,i�߫��,f�ZI�jE�1#��d3M�?=�1g�ڶO�+ Y�T�frҜ�6�bk���A�4���V�&K�����,V3�;l��U�b/�W�m�m����%�˂+
Y��4�`M�rQ*,��ł�Y���s�&KJ��2a�d�yqܓ|	)��$��V%��,f�n����f��b�iv�n�H�(T%��V���,��6���������3����RՖ���U'V��ŪM��^s��NI�[9��*~�NM�bV�v��7w����i�U���A��[�U�S'n�d1tϥ��7wʀ�il8�љ�XsD=�u�7NU�
�r��ё�;Y�%�4����V�b.I7�����X��sL>�
��}{Y�5�ԕN�ž:���=��q��b.���Gn���e1G���p|��b������X,�9l@��|lY�e�5�c��b(�5p���_U!0Ͻ���ʵX�9�Pc���h�b����x�ڴJ5v9޾k1dj�q�;\(�دUl.�j5��NN(�+�jl��7��b(��"o�Z��Ь���ؑ:OH>�yS
-�r��*V&Z��Ul�/9�}%�c<3�p��Pc����B�a��f�k1����ܝP8<���YE���h�FE'���`���{)�'�k9�
���g���d��vk|;DZ�2Z�5����R|���J�o��k)�Jl�!��8wJZ��q.�Zgj|h��K�å�q0�Vwp��r\<-�S/U\�q�
-��5^�q2ŷV����9N���nn�vs�L���e�f��)�bP�WSlu20���h1X��l�)�bP��w��� +9N�Z?�x��q:�͏^/��f�)6.�?[�g�K}�ޥ�Z�k���þ�&1��GG������,�g6=���p"��R"a�H�bY��y�q�T�C�����VD��.�*��[�>!��i��V��bx�9:�2������xx=�(�&
xOr����,����>�8jN&
���s�㊞R,��c��isEO)6Q�ǣn�J�,�/���qU)6Q���#抲R<�{Fأ2�/�X)6Q�l�S�s;NL~�f�c�'�Ő�7���<a���m�ǒ�Dj��{�'L���=��M{����{Il.��ڧ���,�M0\�Tq,$��&+���؞�^Q�d��\�Il�����ߍ��$vs���;�
_v�( Ϧ��^�7%��Rl8�?;�=�Z-�ۦ�U�bH�伾��ŐE9����,���g��D{n��P�!�r\E-�\Ҹ�Z���ps���A>�8�Z��lj1�P�����D9�a�n�a���SýV�f� ��Vj1�E9^I-��(����"�x�^�o�û��5�bx���vP��u�r[@-�����b�A9Ϳ�p�C�\
8E9�I-�kl9�Ey�8�Q��$j1�e��C�i��,p�rܳP���Z�*r������U�,��*r�0�,��'��q�B��s���㜱�=Q���ŀK^N�8g,�qN.y=Q�㜱�9Q��(�q���9Q��(~�#Q,!�E10�8����� �0T�w.T�j'3��"g��� �E1A�b ��G7Q$�("�cQDx(�E1A�b �8~}�)���m/��W��ǢH���8E1�f�!��iW[!��\k�
��m�x,��|���@���<@���~�3n<�O�F��	�ؐ�N�z�u�~���0s���b`�º�P�TUWx>��R{>LW�džb`��<��	]�'�u��X=�t���,�?VO\�cI�'�OdI�}�'�n_�Ib�����	���v�35/,$1�%�)��t@>$1�=���v�` � ���i�L""��r�X�2NL�Tb��,x� X�,x�&t�`A	b����5���IEND�B`�dist/images/wp-2fa-square.png000064400000026230150755130600012061 0ustar00�PNG


IHDR,,y}�u�zTXtRaw profile type exifxڭ�iv#����s������z�����,[����YU����")������S�&�\RM��j����맟gg�y>?~��������Ux��@��`�����=��x���������|�^��kf;���=���sw��Z���v�ބrT��s�^Z
�B.�����s
��$����b���A��E��AO⡿�ce�߫=[������L�fd̰���,⯭��ׁ-��g��3U<�E�t��m�;^��=2�9��o~Ί�&?�¼�����{~�� >ޙ��x���ݟ�+~lzl-�?����c�y��"�z ���O��{�}��&��!�Uw���p"e�\�xd�G���<�mv0�d)��p�y���n��[�u���_>�J���ϊd_�8��pۓ[�RH��ː� �c-��[�|�f��S�c0w���v�ofo��l����&����g�"n�y�'���ٟ+d0�06�l����=�%'��y��+���y@��;�� 8��D����g�c!A��S�����P�&��un����O^?��ID��2��T�P�Qk10Ԣ�cL1�bb�-Q�)��rR!hYr�1��)�[�J,��RJ-��JU�XS͵�Z[�f��Ս3Z�K=��s/��6��#�4�(��6��	��4�,�ζ�Yp�
+���*����ږv�i�]v��#kwV�=�"k�Κ?����G����)�D����^sf��k�4gE=���3�f���|��#w��Q�L,�7�O�3���F�����7Y��С��ijL�P}��|1������;���$߭t_I,	����*��Z�q�=ru�6r[\_%�JP��]3��8{�i��f�喗�h�i��K�zh��s�t�yY9Wɛk�K�߸�r͏�{w��}�?���vZ�Z+��H��-i�Y[�f�?@h�bD�`RJ��1����*y=�-c�]�	��$��,�Z�/��]�e�*�Ʋ��.G]�[���ZVo��1��&��!O������~l��
�{�.�4&1iޮ����osi�VGo~��`�$����@e��ub@�-���T.�Q�./Ş��F��J�1Ax��L=ǖ������{Y�uF}LB?cҌ-;�����Z��u��6�Y���5�.��#k�(�p��]���㓚eg?�.�@����'cV��،��H��t���"�u��@xNԓE�=���Y�������wmL����D���>!e����N��6�S�mA�c�>]7�|T��-ӥ,tFJc|���նjϤ�w�y��~�A7�ql�ל�)�=�]H_�Gj�8��
iC昘�=I)��.]�%�E�	;���0��ʚi��&�ܳ��G���۷Q*b_�FF+��Eu���[hB�x��wm��^�W��ð_�V�DY�i���V
�m��^ �����v]YG��)���Ѳ�E�2��v��{@ӽS�!�>��rQ��S"�Od>�g��[��5]'�\�kX���C�c�<�
Em��85�3%M)כ'|��m�I-��=.#E_��
�s^A���!W�ͣ̅��4?�%� w��#~Ow����4�ڻ8|��}�(�ـ^dj��vtՔ�W݂��M�8*H
�_�F����6G�;���[t�EJ����p�&�58�JO�愁#��y-�ww~^��Q���F����լ'8�u�7���]�=�ё�ZL'��Fx^W��Ǩ QJ�3MyCs����h(ٯl�ҙG*��Á���Q��
Z'N3b)J�+K咆&���u�R|��J�Հ#;8
L��3Q�|X)N�.��x�Zf�� ��=�Z I��`;�zH�ʸMUjOq��4�G�Ӵ~+��w��g�D%ה'����j�7��$��܊w�JTY��2�C�Q�1	
�=)�|1x?F�u����p�
ѥ��-CX��0��t��t�\\��/P�v�ֺV�k��*$�66����v�\{���S�ĺl�!,��TZ(SrVy@�(���svCM+[�{!�+T�V)�"�議17n��!!��W�2~@��Y;�t�/���h4w�y�2.�u0�U�A5����{�6�zo�����f���:��;�=Y�HgB��YɃh<@�	=O�慤�U�Ԭ1(
8~��<r��ݤ�J��rR�I,}�i��%�BQ�I�tl	Z[��7�����@B���n��L.��ҍ�|�7
x�	 �-`^���~���%d8~����axx��Gz�}��e�a�F�MZ�;k�����B���z�hC�3���.��-�jM5M�%軺s���~�rO�������Ēl���zR�.NB��Y�i��&�)��o�:���U	�^��V�܉g9�T)��U�d���K�F�_"VD����ѼZ����d��%4�?R���Ŕ&��8&A������_!�仙�$��x�'.a#��V�&e��ae���3��p�#8���QaT�ս�2��֗�5�g��^�Ʋ�MՖ�m�1��vSa����� W%���SG���ؠi�'>�V�:,?�)��!̶�����v���Cp?�v �X["�C���F?L��>m\�R5�2F\�U݃UwPV5o����ф��p�/�D��ĦBBJ�z��v�~|�C�"i�6��w(�MEܟ@�No���^��9
�U4��!4F�"��tN�k�Vo�D.��_i\��k��Ȧ'i2�d(��Ɂ�(�d����Dž1��ZlA[���W��_B��T�	���$�Y�x!�i'�0�W@�N�`��IV	l�~��{׻U��m~�=�۹���g} ��D�^��8�V��0:(d"���c��__������Ź���>{��2�o0�^��b�iq�U�+����dƯP�u	M LG#��ߗo4�x�k!2�S�by���4�0+�ff�c;�S=�J���_\���*d���F��p�����}Rjq¬Æ���5+���6	�DŽ39�Xm��g}�@Y�ϥ6f�M˱>�� I�"�������̘3�,gpS4@��=�A���һ�^�%�U���g^)�Ҋ~�ⅲ�@�鲟��sUk��h�J&�h��cH����+�&A�j&�G�) �	�Ŝ�cw3=g�w+u���3��jư2Nﻻ�]�c�0���Ȩ��Y��N{�Q�s��Ͻ�l����Jw_i2u��VvhM�©����
-5|�=$S<\�����������z�]�2Kؓg���� �o�?�q��n]U"~��q/ǝ-��o%��ג�*�Dw~�'���.2NYև#�BN�p���@Y��vv��m$���_�Ƞb0LP�z�1�k�P]j� �8��C։��4�n�K��R���ro�PWC���Pi��l�.t�"����>h��O&�M��6����Nj�G{�_�o�vī�9lA>��#�'e3�;4FC�!6��&|v� t��TT���s�[��fq�L(�ъK����4�%���x?�����7�]�����H�Y!�}��!rZ��	"Ń ��P��N��jz�R��o��R��	�XpΚ6� ?W!���R,��E-����e�����	Zh�ڢ�0D�*���Iȓ�!T<�ݗ�F%fRj
ou2�蜔\��z�͝;>c�wF!�QEjP8��|��Г�[~�;�)'�/����G-a�@:l���lg'�7J��>��T�06� �IA;��Rg��%:�Q���Xo��M����\0��z;��d�pK�@}��1����Y�>�ګڹqJ��a�b��og���[+Zz�_~|5�p�{1Xz���V>��o��]:�r!��U��ˠ��N;JK����Fi)QC1��B]�F�[�.����?H􊾻��XK�-�C�~L�L��:�d1�|(�xZ�G�(��ԓ�m��L��qȖoǮ#�o�3�!TT8G��)?�&�����Z<}�7�7;w�D�ĝ")�]@��N����n��Q�F�B…L��Ғ��Ἇ\���G�����k���_�w�2��5ͧ�x2�B���u�}`Z�� �����C��-C���Ы}?���3�5�(���-DBX���y����E����&j�+��C.�0i�r��Z!ޤ���2�&�jU��uAW��wa,�k!�QF� �4I\�w��	\�"W�@��0��5M%���/�����rM����4&�u�3Er��OV*=Z�~��AΗm�N��*�s��t¬�����v����2:.�z�+���ʣd�
�l"K)�-M�Ϯ���oʁR:�_�P����oq�R���-r=�to ��*95��.>�����~�	$�}�]hZ(S�MÃ]�#@ŵI�������I
!�(q��Y4nV��&>�_2�(zS��o���k�?z���h�f5����T��iCCPICC profilex�}�=H�@�ߦjU*q�P�,��8j�P!�
�:�\�#4iHR\ׂ�?�Ug]\A���I�EJ�.)�����ޗ���Vb��6h�m�q1�]C��م~�df�����G��w1��_���Qs"�3L�x�xj�68�GXQV�ωGM� �#���8\xf�L��#�b���fES#�$���N�B�c��g�Ta�{��s���i
!�,B�l�1�uR,��<��t��rm��ceh�]?������q���q>���.P�:����O��3p�7��0�Iz��E���m�⺩){��0�dȦ�JAZB>���7e��[�{��[��@�z����e������o��4��RVr��|�
ziTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 4.4.0-Exiv2">
 <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
  <rdf:Description rdf:about=""
    xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/"
    xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#"
    xmlns:dc="http://purl.org/dc/elements/1.1/"
    xmlns:GIMP="http://www.gimp.org/xmp/"
    xmlns:tiff="http://ns.adobe.com/tiff/1.0/"
    xmlns:xmp="http://ns.adobe.com/xap/1.0/"
   xmpMM:DocumentID="gimp:docid:gimp:f5652aff-9293-4b18-856a-80070022d862"
   xmpMM:InstanceID="xmp.iid:f1aca342-f8c2-40f0-a2d5-1cd44be94788"
   xmpMM:OriginalDocumentID="xmp.did:c04c46ad-97f6-4e58-b046-7d025530dd1f"
   dc:Format="image/png"
   GIMP:API="2.0"
   GIMP:Platform="Mac OS"
   GIMP:TimeStamp="1717665928109824"
   GIMP:Version="2.10.32"
   tiff:Orientation="1"
   xmp:CreatorTool="GIMP 2.10"
   xmp:MetadataDate="2024:06:06T10:25:28+01:00"
   xmp:ModifyDate="2024:06:06T10:25:28+01:00">
   <xmpMM:History>
    <rdf:Seq>
     <rdf:li
      stEvt:action="saved"
      stEvt:changed="/"
      stEvt:instanceID="xmp.iid:e6777fb7-219a-4e25-90dd-071bae43b432"
      stEvt:softwareAgent="Gimp 2.10 (Mac OS)"
      stEvt:when="2024-06-06T10:25:28+01:00"/>
    </rdf:Seq>
   </xmpMM:History>
  </rdf:Description>
 </rdf:RDF>
</x:xmpmeta>
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                           
<?xpacket end="w"?>Hh��bKGD�������	pHYs���+tIME�	8W�3tEXtCommentxr:d:DAFG9MG6E-c:2,j:31147600073,t:22072012���
�IDATx��ݿo����ncUI��UE���	�
�L,,D�@��5JP;v����ؽC��]�
	�@A�!ƾ.X�����|����y<6�R_�'�ׇ��h<� ����,@�@��,�@�,�,@�@���f�_����t1"�k(�Eć�X�sbeB�X=�c,�����$��cu���b�`�g �)b�Ϊ
�t�3�3�$�@',+�?,H+wV�s���cp�3�3�$3',+�����;���aa�b�`��3���rgU�;,�@���D���8,�3+��;�㳻?T�Ueݹo�g�v����?}�����[B�Ǫ�|[�30C����&!f`�����|��Y����iNVb儅X��`�03�z��@��"��t��l V�.���U���E�X=V�LB��ʝ���@��Y����if NX��f�`�h�����E�X=V�.����;+3��X��2	1Ӝ�,��c���$�4q�B��@������ce�����8a%5��"�Nf`�X]+���A_��g"����t�*p�z)".����F���@��L�*&��ZDl���NV���������>"^-p��靕��`��`_���he��~S(�U`���y�1Z==]0�l�����h��(Z�. X>�Eb�m�q��9��:��
�U`.#Vǣ�ړwVV�X�fN�VKO�Y!X>�U��H+wZ~��2皇늖�A��������i��,3p��p�;-O��3pZ�����. Xf�:c59W-O��Z���U���V���X�jz��`��)桧�8�<\�Ƀ��`�����E�<��t��J��~ -�iњgz��`��c59g���V�Xe��Ӣ5�Ƀ�V��B�6��
�U��<g��`��/~��a��1��5��<�,"��>���	�n���o�ލ��qٯ�|D�6�Y!X%�}� �-�f �ՍD+��tA����{�e"X�!�8Y!XNZ��|�<],��4'+wV��	�P�ڊ�;+�!�2�9Y�`�0Ek��rg�`�-�p}3Н���<-3�J-��D�R�C�Z���D��-�p5��t�2Ӝ�@����c��
�2Ӝ��Y!X�C�Z,V��5E�<��`�q���`���yh"X�a���y�|�<]@���4'+wVV�y�s��Y!X	���<tg�`%��=E˝�U Z=��^~S����`��f`�������G��,��l;V���V�"⍧�DK��ì'��3p2V!Z���<��^�.�ULD�M��^��y�tguR�&9i	V7�0c�z����<����i�zg�0�`u53D�(VNV&Z��M�Z��f���C��f�-3p>���h�4{�p�h���e��dUݲc5I��<(V�<,�p���;���C��n���rg��h����M������GN���y(X]��UF��%Z��M�V1��e
�yx���8<�,�p�Xy��^�`��s���Y���`u3��;���e
V7њg��j���`u3g��;��OZ�%X�Dk�<��?r�5V�`u9O����h	V7њ��f`^��m�j���n�x�����*�<|�-�Ђ�G�>�;0Dk��V�h�m�X5����v3"~u�g-X�U�X]�����$D���G\�q8��턅X5�?�E��J0�9Y	b��d���d�+3�	�J��*ȝ�`!ViNV�f��2��@��@�B��@���t��+�B�rqg%X�U���;��s��X��NX Vf�`!Vf�Ibe:a�X!X�U{�Y	b��d��j��a!Vf��4�J����~K��J0�Y	VY;��x��V�zcT�����J��l�vw���gb���P4Z�. XEb��ٍ��x��F���v����`%w�X��<yv#vE�D�
��n��j���
�*�w�ĪJ��Y!Xb����=$V٣��
�*��g�U�h��B�:�U�h��V��-3�J�Ғb�z��@�����c�j�<]@�������qg�`����Z��B��*�<tg�`�U�h���X��VO�)��J�J���X
�^f�՛b%X�OV7�ժ����e�4Z�z���
���GkO<]@��*�<�t���rg�`��;�b�h��Y!X�]��7�f��;+����F“ռ�rg�`��n�X���'f �%V�Fk�ɃH��x��?���w��r��t��A|��~���Y�6����1b�B�����O��I��_��&!���J� ��ʝ�If NX`"X����`�b�`Q�;+�4'+wVq����v��E�X]�%V	f�;+�M���	�3��3��@��+,Z��
�"��ʝ�p�����f &!��8a!Vb�`�:wViNV�X6wX��8aa�`�t��@LBR��NV8a�!V�,R�@wV)NV��;,�@��+,�ʝ�E���;+��f NX�� X����`℅X�`�*�,Ҝ��Y�*wX��8aa�`abb�b��i��B�Hs�rgEF��@p¢�(V)bu��X!X$��`4��3���_G�>��Ͽ��ޟߤ��F��`����` X� X�`�`� X��` X���C{��\�IEND�B`�dist/images/wp-white-security.png000064400000021460150755130600013100 0ustar00�PNG


IHDRwp|PV�	pHYs�� iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c145 79.163499, 2018/08/13-16:40:22        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmp:CreatorTool="Adobe Photoshop CC 2019 (Macintosh)" xmp:CreateDate="2013-06-26T13:49:30+05:30" xmp:ModifyDate="2019-09-17T12:51:05+05:30" xmp:MetadataDate="2019-09-17T12:51:05+05:30" dc:format="image/png" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1" xmpMM:InstanceID="xmp.iid:bdf4e962-bbe2-45d0-b3bb-0714b77ea3a6" xmpMM:DocumentID="xmp.did:bdf4e962-bbe2-45d0-b3bb-0714b77ea3a6" xmpMM:OriginalDocumentID="xmp.did:bdf4e962-bbe2-45d0-b3bb-0714b77ea3a6"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:bdf4e962-bbe2-45d0-b3bb-0714b77ea3a6" stEvt:when="2013-06-26T13:49:30+05:30" stEvt:softwareAgent="Adobe Photoshop CC 2019 (Macintosh)"/> </rdf:Seq> </xmpMM:History> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>�����IDATx���n��v�K��p{�d����;�j�E��F���mp�+�����m�$?�e �֚<@Գ��=�,�A`x��a��-�]u>��l����F�nY�~uX�΁�H��������zu�}Y���~���_�f � �P,ܽ���mw�U�^Q���~۟U%j� � �]7pvR��U?���m������w�w�կ���q���{t3� �wrx[4��>r�O����qw%s��կ�H�+*�;�>����+��,�w
tK� ��
�7�c�3���Yv��2�G���
Ԗ؝V�>�����?���~KtY� �w
�k)���詋Ѳ*㵠��5<F�ݲ1��
�F���OUw��A�wn�Z�~u��:���U�:7�^�X�ױ+�����M�w�r�W+�{W�z��}A��~���AA�;1�5A����ItWAׅ���- ��*�Z[��l�;�l�|Ixi_=�ke�"}��� ����4���
�K���CA����]u\��
�7�Mw�28Y^[e(T�G�W�:$�5��g�AA���.2�4��K�%I(�*�)�>����+���WA��χ��8�W�P�+����߈� �������s��;�=t�G�T���p�����2�p'
�H
�8P#������ � h�p����#��kH�Q���!9˰��
���`Bs�Gݹy7�תs+��� ���Ýzf�,��%��}Uƙ������]9t�2��A�2�;.���rK��[� ?�`hр���cd
��w&�Ȑ�2��eH�(��A:�0^9� �O��`� ��#Crj0p�r,�T�U�L�	Q��"CA���!^kzT��cI*�"l$�I�Ǧ��Q�(P� ��=�;I��At�1׳��td�&l$ܥ䏕��@��8�� �w>X�x��A�揍��>�%���8c�?3:�i[l0AA�w��d����:le�F�AAО�&j��`��*�Nr,���.l�2 � ܅��4����u#��.C�AAОÝ�'g�W������>��Q�( � p��f A�c;+�w�`_6G������ ܩ��T�Y|�X!�M%,�AA��Sk�C�X��?��2DAA��h��;�+��Y�H:2��2���w��ǒ=��� �w���e ,AA�;3���T�@
䏅 � h��.C��A�E�X� �wfX�cS�@�X� ��?6 6P�c!� �{�ː?v9j�?� � ��f�ؘ2�?� �������m�
�@�X� �w&-S��'G�*�c!� �)���St�?VS��BA�ۆ0�w���Nl���BA��;�~�ÎsԖ��x��/䏅��y�H|�;N)�X���<m�e�B���)���K7�bn�!�R�A�X��?�d�=vF�{����.A$k�B�,رN�]z#�
N\a�?V*Q@��N���O/���q�#�}������+��+�]?�c3�1�I7(΃J�b0��م�9c 
�=� ���r�mb��"c�,�\�w���:���<w�FPo��~4�{�{�;"������` 
:��c�M�XAN�È���ܲg#z�we�he�qH���ɋC1Ɛi��^3�~��%Խ5������ vR�y��?���_��_����kon��z�e\�l���zT�,Ď����ɫ�M�m
�6q��RϢ}5����"m@p'�[,=�����jērαs��0о����{֥������u0�Fi�n�?֮&g/g_~�귱eH��v��v`�r�C0���z/3z[�}���@���LfS�����
J�qބ��R*R��X=V��"�:���@�<�S.�_��|�1?�O�Rc�y�X[Ƌً�WG�ǔaW�����T�6��V�a��zw�p'Z�dnUD��.T��	ck��5�e����Cg��`�g�p�ؑ��<����r�b7wy�~6�����W����;䏍��E)�ג����$x��H{�����E{S�gc�L���C��ƤcA��A��n�)��qp�!��D*[M
��fm�OΆ-c�+�._c�	�A2��[�	w�����#�[��U�n%�
�X/}�&$�Eӧ�=�:��	�P��fབ�Ƚ�p16ol�23�-5K797u��b����
�9p7��*Gm$�!,]����kT�W'�k��^1m4'�Lf.�_6C#�L�����^�n�^E�ݠ�Eb���Q�A�e�^pv �?��$��_��c۫���#�mU��g�E����R`�^=����턾���%��i)0R��N�(�؞�{c�
�:q��z^�^;�+[��A�s*� �H�%��2$�c;�Q;�v<1�wGѤ	F|�C���p�[h�@k�6�[Tn��B<�� �}ۓgQGR7�ʍ[� ��Q��M�c}e(��c�x�9�~�Z�H��I��s׽2�w2��(��
�9�~��׌
p�������O���Ÿv�ؾ�p���������nsԈ`+�`�C�{=	�����٪UG���pX��)X��0l���
>�>�q�:h�����
��#�*�x"��ư���Ib�_�&����wO9��Q��F�޿U��%��&Y���PfG���5N}0?8��gn�W�z��⃃�RXd{a�d��@�~�2\]*�{w��z�T�-�
	�~�0�K�]�.���?�<�#�=�`a�}���]�cCe(�N�?�6F�9jGԦdPC�����Z8�3r[Wp�jH��PvQׄ�{�l�?'�3��p�����qfVhB��y�I�ȸI�\�:��g�wv|��ۏ	������oLx���y�>����F�]J����>�6D�=Ix݋1�]ґ!)9j�p����6�V5�^2b�rh�?ȕRLT��Nu]�ρ��h�g'��\x1�,ɳ���4�ʤ�w��JW�*�>D�������,�R+W���sggg�Gw��V��l���N��0 2łw���?�j`!��?�x�G�D��I��$Bƃ�0�3X������SC{��	 p���ᄱ&��1�� \	��*�H2��2��
�3���>,#��*a����	��[���`�p�ջz�4S?�9n,p���N�?��`D��?�gh!����1`�}wH�x�Ʋ�+����[�z 92 �ҕ1ܥ��渡g}���C��T���.8�w���֥�]�	l�z�7z=O��N���&W	�Y�����mKM.ԙr!���y�`RA����uw���z�c8b^������`ڄ�^�Ѷת�3!�Huw�����p���ɥ�=o�nỏ]x�{�����oܼ1K��E�`�O���改��w��o]
`
�(���ֵN	���Ӟ�8珥>�]�cϪ2��ŀ5�
"�u�3�-x�3����
+�I���
��#��6��X�zE��p^X��.�����'>&nO�3���>\��y-;vߏ9�p�u�#�������
DrrB��bI,��z�ێ��]�?�^��:�E,Z$�EY�� 4�ϩq��oc�;�
g=(s�%��c�ە\��\��g:��\�%O���s{O�|��9���x׈τ"�쳇��d���=3�$ͽ���J����af��mM�.ծ+#�`��P�FJz��L?#ܕ�-sٯ]�]�Őp��sc;�N�W反�;�MoS�������B�;���%ٻ�
���3�~&� �b�KExJ��5�
,&?lR���4k	�e��e0p�s'�!������.�r�w���{ę�	�&��p�P�����o%w\�����֎
�fFv��:l�!l�v���|	��>̒��}�B��$�����
\۷�^�SA�)�`OiD{p���C��g�5��}��L\�.g�5�B8�<&���jO0�ҷ'3#;όSa<��#�[4�(ƌo7�԰5A��ʼ3T�sץ�n��g�cnYn�U����J�+
���lS��c�!���������}*�:;�	��IhO�3͌_4RƐ;s/'ٺ�>�t`pG��[m^Vj��V�g�4�Q���9HXО1�И�m��[�'&w��?p�KZX1s�>;:�t����J��}b`���
ۓ�c��?�ƀoMbԀ�LR=�]��7�1�U61��#Өq��P�$�d�̸��w�rՉ��T����U�[b����ǪL��^��a<2���Z�Eu��-[}v�p��cp����ۓZ�NZg7�@��
�8�'}��ڻ��@��.�5��W��d�� ������Dt�7&��"j�v��{�yn�c��~/f|݄�*��ظ��l�
�%�>X�83����w>J�Q�QA�cp�������@����H�x1� ���4=��/=K��l(H"�h_P�L��e��J�7��m�b9�ɀ�,�Q�!{<�nPpG�]My{�<�c���y���?Vw�$�ݞ�`jore�^���Ar]+i�Ю�0�����[�Ǜ���k����0�)Y��tu?H�k�ɷFlDN|�	w���w9�i�{�u�ױ1Z��4�a� ��!lwmIm�tvb�s0��;Ld�t��	w�<;!��3bN�&TPŦNCs0���s��}��� ��]k��cU�/ϷGp��%�Q�,_�v��0j�k�}j�X�!l��H�&�&��ŊU��u+��:�QL��	e�7�����bͶ��S�wY��xl��,ȭ��3�i�
��(����R�w�;�;q��L�R���G���i;�+�5MN��hct	'u�*��ۄz�:_l�H��&�,$l��}�;�������=�S�m62pa�|N ]i"�wӇ;f^�j�����Fw�:5'6,�c;m���#>Ý1�ڈhS��	�Q P�I�3\N�P]�A�Y��
pA@��0����n�Cm�/����	��Q�uc?[��/p�����?�[�g�v�~㺶]B�?���<{w�QYG��L.�����M���-�]T���v�}��޵�.��?�e�`N�:p7,�c%��i&(0��<�I�Ǧt���V �:>���}a�T+(�ᰓ�;��j�irrb��Μ��I��w�65�|��TU?��r0��,K�3�2��:�k;G��%�Ƥ�.%�‰}%��������1��0&ր�B0�
~�pG�vuP��Ҁ�̿�~^�ĔV��൸�����u��>:�X&^�{&�q(�.��/(L��5otj~�ֵ���p�
�%9��~J.@�w\#�����Y���ּ�e����A��0щ��Lׁ�y�}O��=I�tw�`ѹ���Cc�JM�D.��R8�Y�����|,��Hw�q;3�Y�S����O�Y�# �t<L�cc��fo3��^��z��ˀ�QMr�;kt>+�D4)�.��<e�<1��>jo�b����?]G��v�q,����UG� 9]A�uʶ��@Izm�5��|�p'z3�������4e�K	�yl�?v'����5���r�(��,�rm�O��w9̵��IL����u	�6� L%)IKvAg�K�xm�8{�Az<�M���x�=3t�y��y��~4��Kc�;�_�Ɔ���X;8I��p�$���mQ��?��N����q��?9�N�����\Fw����M�E�O����Z��^6�E�����=�O���}�p�x!��PW�w�V��,\��{���{W��'�)�m��mօm���Iʳ�`����~���c�;�v�-�s����Y��B��{S������f�{_�ݙ�R�k�#l8N�4P�ڏ�=\\	���;��(�Ϣ�[}Y��A�p��p�o��)��w�PWڅ�nW�n�zn���I��eg8=b_�N�Y��[H�=���^��kY-	�b#Ő?v0�&>`8b���UL�4m����lT��N�R&f�v8�S޻� �,�����:�x�K:[�'�P(�j�p箣}����胢�HS����$��66��v�no�jӷrO�z�N�4P�=++��u�p'�cl[G�ǥ<5O{ĺ�v����On��َ��;�9"�k=V_'w��|����N����1Z��}��?�<z�J`\S������=�;
��O�Wx���.vq��߉�Yk�3�9��-��#�:�gTu����pm0�P�T�Nx=^�F�	ѯGw���aD�-/p�!C�t2G��ᴙ�����3��
w
S�l\[�`��2F�k�g�Lw�?[֫s	y����"o�S0B,�� ��ԃdݘ����s0sb�� \�`��������%����$@����j�s�1�p�� 1�wO'��z�6�o�O*w]U;���)�e�䒱I���T��f�-��\���q�{5U��.��g3�(Ҍ}~��D`���Qd({�`��`�s[n�\2ea��K�sHړ[�{�+ +�w1��yۨrhL�+�>���ױ+A��"vk��<Γ5�L��=W�g�p/eW�� kپ�룍G8�:k�-�j�����>䏅 � h��b���o���w�G��l�˶+?6�����_���Ud��2DAA��Qj��3��Y*n<�^�"f�b��˫��c�����]� ���Poչ������fE	ۨ�
�N"��c!� ���ʷu2�Vd�-NAR����;䏅 � @�,������QǪ�#��J�&�^�ƍ�;䏅 � ���n�ind�KƔw�*g�D���s䏅 � (6����hw�F�4�9:%�?� ���+�ikE���э���c%��c!� 2��`__QX��5‰i�+S���BATsŹ	�k�4����2wۍ��?Vw�AAP�+lD�u�>��M�ҵ�?� ��6_,̣�h�grE9|wO?3���p���A�\��'��MpW䏅 � p7(��)��c!� �
��c-�!,AA����V���?� � ��@��Ĵθc��c!� �
���4�AA�n�p77�G�pp���A�Fx��pW䏅 � p7���Ɓ��C�X� �w#�s�R����c!� ܍�l��I�?� � ��H���Ѐ��
�.P3A��x+�;��4�AA�n�p7���}�ݫ߾G�X� �w��ݿ����=j� ����}����V�IEND�B`�dist/images/microsoft-logo.png000064400000020034150755130600012426 0ustar00�PNG


IHDRa#�H���zTXtRaw profile type exifxڭ�i�#9r��co	�,�9ځ����Y]�ݒZC03�p:�
w�3����?f��j��h-�SGy��_?�b�~?�癿�r<���F�^������q��~_#�����J��]Y�ϟ'���x�?��3���S]?������L�ד�9�D�*9ߒJ������:��Rt^*��ߡ�3���~?���%ȿ_������<�(�#^��7�������/\��Q������?���߻�V7k#���`���N\��|k<���k��G�3n.u⎋�N#e��B�餙^���N�)�|�����;֋�w��'�e/������7��Z�sI�u�w��:W>�Ssb��G��G����<�{�wJ
&�O��U�LC��o�"��;�~��?���(U2h_�;�q�bY�[m�/υ��_9N�����k�I�����9{Jı����s�y��d��̵����um>��;7[nY��&a�'7�L�U�Q?^;54�X5P��z�a��V��ּ	��n�ܽ���K��z��{}�<
h�
}�1g�Mƚ�?9��*�.[m��k��)�]��}�=�<��L�v��3μ)\���k�]���;��ʫ�^{��o�������㿑��eJ�Y�hp�=D��rF�rMdܕ
:+g��Z�2��ői
�LҔ�p�2F
�M�^�#w�ܿ��`�_�[��2�����R��y�'Y;�e�W*���}�u��[���L���G�!�/�2���B��ҡ�^%kk��g߄5Ȟn�ee�n�'�����y��d�n�X
�P/?m�Tv[��Qv���]�ϓ���]�R,;�3�ED�$QN�BmD���t#=T�is�sJ�����1���Hu�<w��=N�`��z`+�W�"ն��6`�8j���F���4��Sl/��Ҙ|8j�P�)�ͅ�Q��R�t߶^�$�nYò�S��6h��󈝵r�@
�`�\_]����iL�R�e�2�rg}N�h�B3������R�tݞkx�q���t���{��y��#���+����,0+S��u��<���x�_�k.(�](�f��)�=b��;���6A���t�-Y�X+k��S,��D��2���_��]j��}¡�-��p1��цn�n��W���k��#�E��;�<�k��I��fPv�1k2G�:v��3���%�/��׭>��3�
5��r�|�QW��ÖH���}��B]��]xB��^�:"͹e{�m�D�V�s�q�������a!�h��"�@\�ݹ
��t�&}�$�JA;Z�)L�#s�lt@�Q�Ur��C=��;0�.�x;y���w[�����-+oj�]��I���O:Lxw�(�F_;y���&�����H�k�v���iֱ���*u���K��j�
(}s�13�F.K;��Gِ���mY�'l�-�˥�l-�u�	�K���ۜ0m�/�z&�e��z$��38v�ZB��Fw{Q��y���ʥ�g����Sf5G�T���%��+ؖ�m���7t�T.�S�8��>��y��Z�� �E�C�V���(f2�=������(�������v��Y�.�|�|�w�ռh��M{�
R�t>��ojae"ș���У( �¤LҭA`6�W�&�di�k�B�zuUuiJ��8���!)߾`�p�!�T�)�
&tI1�@�\虢돺!d��ܬw�I4\�����T�Q�7�7��&���&9��6G�����<l�#e��	���#r�2,�Z�K<:�X��:����t1�?c+���b!<C4X�{�젷��
�i�
E���� �V;�G�uD�Qa ��J���!T@kst�K��H��	P��D�LZR���R�\;�5�{՞�.��K�]p.�ԣKY��bZ�Q|`��"�>�<p����$����2���"��\�qf�3��-��L5�¡*\�{8�߆R��{�b}%�b��7���v�z��
9Zs�k����� �qŢ7��#G*�OFT�\��������5	��IXz!0>βkyP���_�4�ZO���W�� ���iZ/�~���5
~P[{�3���)c�!�Wxѩ��ⅾA����}0\@�^�	M�G[q8�V`A������%s�wZ�P�fiJT)������,]]��!O����2���mX�﷠��7��,�_�w΅ѐ
x��!�
1A��i�~8^�:�֣�L4ݯ'��å��11W%��
฀F�Tי�1���3^ᜃ�d-��k�h`(X�N4�b�*��oc��<y�:���LI`��!٘>Φ#F3t��˴�S���H�&,�19Bf��H����(��Dn<�ـ�+�JpEo���B�(݊R�~�X-�J�
�0��х��c����{C���x�"a�(�n������z
8���|*j�s�����é�F&	<�\{uZq��<�
Ђ��4ׁ����
j<�����l�>�@�3��!����3Mx%E	��SShZ�1��u`��Fg���"0�ҎF-�:���K��I�A��r��LkЉ:F�&�``:�rNz��*y�&<r��О
����7�R��.��ގ"���hr�A`.�q�~D|��
�/ބ��Al�Z���.�j�#k}��~.���Ϊo��J���7�%�n�~
P��448��G�qiy$�
��B�oMP��"�X�l��������k��fԂC2+Ib�9������>oã�_y,�ݚ�,�˧�P@'C�dj�Yi{��J��>��ԯ�5�*�0����Bh��u��V23�C�Y�4�@}�*��|Rc%��Y��?����R����ܼπbS\I5ͨ��
�5�e��(����6
�-IW�B�#gA��`<�/t�zH@E@"�N٥@X8bc�/}\���&,b
�S&�O�2�΂4��+�M�P�P*�,�G~��1�O+��/xƄ(:�6���	Z"N�\_����nk
M�қإ��XG���%���� (�X�t���ȗ��r|vU�T=�&�2�) [��b���q�����)��Ik�~�	�B���N�Ec<��[���dx�^��&1˭H�CրV�%^�!s~�>���(�B�/J��B��̈��0��;����X!넲|.�h�8�	��
��p/�����3%[Y���(S���G�ʀ���5�;0�*�yKPȆ,9�x
L[��lb�)&�_�8?�G�]s��~�$�TdY�W&��Q�.�4P	 szZ$f��kgc|%?H��^h�%-!LW"�@�ؐ�ts;�IuA�zCq �B�rNc�Z�2l|��T���B��S{?
��֞�vұt��F|�#.&�_.cA� b��j��خ�Q8�i�`��m�ۖ
dc�jB���U��q?���W��w�'�}�FF����%�<�
���Gf�{
ч��7J�.�rb��̳��V��R�Ӿ�[>0���Q���. =M� B)��Tk�/���N�IW{�2��<F��
��	���!Ӧ����p��\$�`�:v�}��gߡiC�NL&9�j��jP,�5��J�h�U�r�֔.9_l��s>�1�&=(99�`��Rɨ���xV�G�:�YX�Q��ȼ<�:�H7W�,����)�0����uw�$��{Z��q��-�9��zbIT&E�FO��16�"hݝ�7\@�c�$�402�Z��d%��[e��$����$�7�l�w��C��Vv$�O&oH��Bc�}��	��H��=,L�,�:�M*��;	ƀJD-0�x��A<Z�p�S
	C���H��M�s@�Q�v�E��(K+�u�y� @If��)΋�F�A<U.�ҋ0!�!�[N�7s�
��u�@}����
)V���-�.q�����C
|x�?Fl��\�	H�NӿW�(�TzŴ�*���E{��ÎUf!�?�:-!{&�
w��44f�t:�9�-K�~�[�W��
㱼O�/��+ȲW@$�`�m��Tvo�'^a��������t��B��F|
�^�z�^��ן
�D� 1¼Į�m0�뾨�‡���T��z�㢎��g��l�MB��]4��Ω��{�>���09c"�����kP�Ҿ<b�D�ceh�
�xJ��d��~9cT�3�$�X2��\o��&�
2.F�1��Q�M�!���~ƪ4��lt�hG[Ud`h��Py�eԱ�-)�h��r�H;�����RF���e�]IѤ��퉊�S�f'"7i΃�F�g�*�t(щڛ0�2l��*��m��&��Ye���/�`*5�Wt��-�e�|�y�Aȭh8g�>O�G��C���N+#�]~���d���q:d�� I=1�x�������Pw@��ZP�"�Cl�i:�C�hsO�h,@+~0Y�M�C�@O\Wt��_[(ˊMd~�5"ڧ��BBR������.�R�T�$�����Ċu(ؒ!����57���d%�d����Ӏx�|���,QK��(z?�5ԟ�X1WC�ª
#p�3�P
x��{W�DO���irT��1�;X��V�c�'�k/�Z�Io@�r1A�<{�sP�t�L�BQ2,v��C��u��	-�KݽC
%���+�F��}7���
z3V�J�d�*��Fl�0h�n�@��� h����ҷ8$�����j- ��xh�X�x�N�p�@�qJ-�,�i�����C�O�C���Ǹ�8V��C7w]9O���\4xAP�X��I>M���(T�	)��D~jC�&�/�h����.7`�����H+���n�X�?t�ƣJ��۾�u���M�cxB��R�w�T�"F�&G@���u�W�՘�e����b����n�!�!J�Lj7m�9-�D�nT����D$&6z�r;�0�ZՅ�	k�%[2*	"��h��p�2^�Q�ɒ�J��v
yC���[iˤ/Th�^����}G9eH
�����`"R/u����f	:9r:%J�K*Hk�*y��C7r<�h�m�Y�o)d��
�ը��Z��r¸ th.|T!T=��v��ˈR��@�$�� ��u���]el/��2���lP����n����%�cڝmrzГ��=�h�Q�!.B
�F�`W`Ϊ�S�E��h��E�6��K�W��E�����b��\�*+�)�U��c�Hh�+v�3慎��;�E��l4GF�cG"��D��-�{�_-rU�aƌ�� ��E�%�Y��
���Z�)#�r8�І�Y 9�VBENVȻa�<���<vմ�5�r��%Em�ˋ҂����>8
m"Y�]�K��&�G]{��n��]��Нa��$,�ȤDVÙ"`G����f�Q�iq�h�h���!��=�G���F��@�kE��ّb��fX�Z�ŗ���>!<��%q&�t3��ҾW����eX�-�x��)l�L1#m�%���U��k�6��d��7z[u��3L9jO)��Ѡ�Oj�=����еS��|�mL�/�����eV��jN�T+4p'�0�x0�����s�+C�R Y���/C��Ꮎ`ڄ4���6��o|�-7޻`��O��"�h8�ںȆn��i�'��5z
p�������=�G���d�ׂ��Jg��
�,�.N�m0�S�W�"#Z���F1�.���LU_쫔�f p���i��d�S�u�v�̔f�/71t� ''�2�\ñ(�>��H�F`�1f�#vrW$�q�=i����|F���~e$}����V���d`������Ӟ��K}������O������G���L��%�x$�iCCPICC profile(�}�=H�@�_SE)U���d�NDE�
E�j�VL.��&
I����Zp�c���⬫�� ~�89:)�H��K
-b=8�ǻ{��w�P-2�j4�6���J���`��E��,cN��h9�����]�g�>���R3|"�,3L�x�xz�68��X^V�ω�L� �#���8�\xf�L&�C�b���&fyS#�"��N�B�c��g�Xf�{�3��2�i!�E,A�eP���:)�m�t��r�ȱ�4Ȯ�~wke''��`hq���c�U��qj'�����R��$����G@�6pq�Д=�rx2dSv%?M!�����@�-X�z����HRW����Q�z�ww6���z?R�r����.hPLTEy���f�u�q�m�y�u�l�qג��v������
x���E�֩�怵�^˲��������}�&���������������脷�8��{�p�������p��{��a��_�����t�!��������u��j��X��,��y���㈷�J��w��������풼䉻�Y��e��i��Q����;��2��#z�m�r����������闽�n��X��?��Q��t� �u�����������o��W��
t�
m�Z����������]��p��d��V��\��M��I��w�Sɹ��y����
y�)~�x�n������
c�������bKGD�H	pHYs��tIME�
!�.!8IDATH���{�8�OI���^ff�,e�l��f*����4}���p�Z?~�Y�;�F��H������7$�������?wA(�k,����o:zvVD-
�?א�������!��z!�8W�Q���&#M���9����	
��	.<�Q��BH�,E��F1”��C��	̟��?����i)��iϧJnJ 7�E+�2����ZX����N�MXW�n�K7i���[�t6b��2���Ô�XѺ��:�O��gڧo�LG�e�l���*��ё�J�%�sv���O�T�=sۊkqc���r���`;ct��h���ɇ�ɂ_��,��7�Ȏ=�\	:�h?��]�`xcou���i�%(���j�~�#�(Ӆ2$���A[��zcx@H���=�Y,N1�r0,][\��n��Ζ�f�K+
��|���VA"J�F/P����j�'��q����u�W�ʉ��n��8ĕ�w�N�oz���Gc}���LHjR����99��
߂ɹi_�9au�[<$���*O}]�,!�s�î�8�5�A"<O0�"B�F�@��Ian�5�Ȱ����~����c��J@�Z�HO4

��ɸ�"<�/R�~6��/'ħ�A��gI�����3Rb?_\��s$4д�)9
+�c1��?-6�IF�H��������dI��l%^�w�I��1fԶz�6|�a��)�t)��U�|���Nv4��^�ФۓJ��a�d���&�V��~�+�d]�z?�%s��&����'IL-��aeD����I��Zk��g_�W��^�r�w�<�1��x��T��LB�{����+�'>dyj�=�F�!���WE��!�\<��G�-�����P��,��~5�(N�����S˺�	�[0�M�������o�'Pg����c������}�OR�V&�)�ʧ$hr�	%�L~"yTM��jk��<�'5+uǨQo��dh_[�Q�e��8[���)��oz�`+�n)��;�<�$^j�;��82z;�f��@tB�o�-�Ɨ�"и���[w��Z��]��/������̵:���H7ΰ!|����b�q%�vK@���yM�8L*+k~J:[ڏn���x�I�����{Z�����5�OJ?��v����]�Z��qn�
<�4l@ȰɃ��Y���z�Z9��a��?$��!�	b<s֠ �x��nӭ7]�5���L�B_�L��ĉ���4�w����_OH]���T���L�`�R�IEND�B`�dist/images/wpassword-small.png000064400000001412150755130600012621 0ustar00�PNG


IHDRK˾�sBIT|d�	pHYs��~�tEXtSoftwareAdobe Fireworks CS6輲�tEXtCreation Time11/02/21�8v�bIDAT8���K��U����m\t�At5�TD�"�$������25�&F���&�QDTЅŒF	q�. �$(N`�JI7hP'2W�:���:�C�ɻ���k�g�͘U3�f>V3���j��53��c �`���\��%��EA5sv�d� ���1=T3؅w�=�+{{�R\�s�kK��ѽ��:gb�D��������=8.���ưoڃz��
���+K�y���۸/�S,�]r��{�nl,3�J��:�&܎��'Wz�u\���n�\]"f�]p��l�C@h�݅u��p?j��^|�/����Z_��n�,}�Cf�*>�i8�5{	~Ú�şx�p���5�KY�gJ�&͈)mh=��Il�)��`r�	‰=�j�ـ'�<n�T�\��������c�6/����fN��ok����k�G�߶�цsz�p-n���f��O�����#dͼ�a?w��������L]>,�܎��q�K�p�Öqd%b^Ӝ8���w�><�c[�F�x����EmFvcN�^�!~��X���m�1�:�F<��Z�ꡳp*���%b�"����;�{}�fN��5�������gIEND�B`�dist/images/2fa-apps-wide.jpg000064400000123614150755130600012026 0ustar00���JFIFHH��2"http://ns.adobe.com/xap/1.0/<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.3-c011 66.145661, 2012/02/06-14:56:27        ">
   <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
      <rdf:Description rdf:about=""
            xmlns:xmp="http://ns.adobe.com/xap/1.0/">
         <xmp:CreatorTool>Adobe Fireworks CS6 (Windows)</xmp:CreatorTool>
         <xmp:CreateDate>2020-08-11T09:46:52Z</xmp:CreateDate>
         <xmp:ModifyDate>2020-08-11T09:48:44Z</xmp:ModifyDate>
      </rdf:Description>
      <rdf:Description rdf:about=""
            xmlns:dc="http://purl.org/dc/elements/1.1/">
         <dc:format>image/jpeg</dc:format>
      </rdf:Description>
   </rdf:RDF>
</x:xmpmeta>
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                            
<?xpacket end="w"?>��C

		









""""""""""��C

             !      !!!   !!!!!!!!"""""""""""""""������`	!1"AQa2TUq������#BRrt����$367Sbcs���%4CVe����&5DGd��'��Eu������C	!1QARaq�"23��#4Br����$Sb��Ccs��‚���?�D.p�6�y|� ��nd(�\�w��)��.��b��C0�R�3�\�0��aq��1�
\���.p�\�P�*2N�u|�!�YcZ��,����h�L���֔�ԣ$������bM(4•��y��r������@4��!�TTj�1Ց��VT�)����hZ��SF�|�,3*\u���Y{��i�6*&�q�&�GE�%p]�2�� :-��:&�����b�|�  "�5BM:��u�RH�˄�.a�	j.W�;�zs^
U�2֌���MT���>A�B��v��gK���/]]׿C��������o�~_ʥ�������e������+�����m����KV�C��1��}�K��/E�k_���G[�:_����ʽZV�E䟤1��}���g�~_��c
��
Q+��~���(�l����������;��?Hg�[�k��n:;���Z@�00�B{�|X��f��2fW�
wp4E9�J�}(�ݽMje3���œ��ɶJ�M�D�\�­���=��[W�X�|#��E�*�-�/C9�y���o���:���z���,�f������K?پj����o���:���z���,�f������K?پj����o���mZ �@ii�@��K��ږ�=�3���7VE�Aq�>'�x{
G'��	WޘO	�T��������[��._�t�%�M�D�N�g^?`��.,�z#ۜ�C�ӷ*�#V<�eG�Z��>�%���:�O��'�
0���LB��*�~�S(�I�W3�31��v<�Q՚;�2��m�I�14��z���ӓc�iKҌ�OT���)e�/+b��"]�k��6Ε�o�g����u&5i���V�v����6�~׌Ub4Mt�֏5�N[�e~��7�-A-JB��Qm##�!X��T4�x�ڔ�R /,Ց����=��x
j����%���W�t���{�H,������z��4�r#��R�2K�\~+���n0�!��Q-�v*I܌4� Th�T9��$�eE�u[P���L���{���,��ա�M\	�i�H?�\�'P�9�kN�ZMb]-�t����R�meQ��r��Ž�r�Vq��ݻu�3�X�qx�s<mzcoGn��ͯ�6�I����3�צ�ڏ3�6�I����3�צ�ڏ3�6�I����3�צ�ڏ3�6�I����3�צ�ڏ3�6�I����3�צ�ڏ3�6�I����3�צ�ڏ3�6�I����3�צ�ڏ3�6�I����3�צ�ڏ3�6�I����3�צ�ڏ3�6�I����3�צ�ڏ3�6�I����3�צ�ڏ3�6�I����3�צ�ڏ3�6�I����3�צ�ڏ3�6�I����3�צ�ڏ3�6�I����3�צ�ڏ3�6�I����3�צ�ڏ3�6�I����3�צ�ڏ3�6�I����3�צ�ڏ3�6�I����3�צ�ڏ3�6�I����3�צ�ڏ3�6�I����3�צ�ڏ3�6�I����3�צ�ڏ3�6�Kt���6*�&���ʜ[dۖ5p�|�����z��g����5�bhGHP����.�(fp�\0�.p0K�\�*�����Bf����(��}���C���B��Od�����0d�K�&Lh�$ �a�8�w�?W/�j��4�S-�p�n��E31��|�e��x�ѓ�`a[�d��dlʽ�f�Ȼ�;Q�ɲV�)��'��C/ϩ+k��s����i��n��-���iޢֵ����X���,`���5���>���/J�a�:]/+��
�Q�r�ʮD�ŎG��Z��|�ZKJE�V����Rlٳ�d�"[�wp���:(�c(�Z�ʫ���hzx,�[
���׀��U��&;��QT|8�|&�9�=�q�X�7���R�����T��
���t���fme�͆\d9˖�՝��]��:��d�j[��s��np3-�e��̷8��2��f[��s��np3-�e��̷8��2��f[��s��np3-�e��̷8��2��f�
�J���+,�'�h����%#�x6(��2�u�7[��$�xL��i��Q�3=�`<��'a��`<���[K��	�Xv<wM�^+-%�rxce$��ނ�r�ͺc��>�fT�"�Ev��ڐD��}�¸q��#��mƑ�5��Y#���_�k�sR��4v�ދy�n}�!#�i�"�;������u�H�|�HzC���s���BGp��G�C��gE#���r;��:?R���:)�_�됑�4����w��H��'\���Ώԇ�;�ΊG�?�:�$w
>t~�=!��tR?�����!#�i�!��3�����N�	�O��Hwy�����u�H�|�HzC���s���BGp��G�C��gE#���r;��:?R���:)�_�됑�4����w��H��'\���Ώԇ�;�ΊG�?�:�$w
>t~�=!��tR?�����!#�i�!��3�����N�	�O��Hwy�����u�H�|�HzC���s���% ��ȟ:?T�����?�uj<�T)1'�9K-�h#���%Z�v��L�f��J�0�kP�|ˤÚ��^�����H{����P|�G��H�A�iϴO�:��x�oB�f�t�>l��e�r�;�q\�o8ޏ��i�8hV�n�?�
�yw�*�q_��M����{Rs���V�n�?�<�ړ�W�R�؜��0{�yw��+⒁��C_tj�#�)��a��%r7�m�\w�=���&Pf���9���Oq�+�.x����_�p�Io@it�8qr!��^�6���v5mq��6�|]sMßuY�����)���*�q_��M����{Rs���V�n�?�<�ړ�WŶt��,i���[�T�V��*oU��Y�L���gֆ꒽ۮ�CG/_�Z�'Tu��w�^�'-_�:�n��/_���y�R�J�2�����k���1c��:sO�9Ҕ3��!Ǵ�J��>�[�F�竈D������wM>-Fv�1$��H��D&��U�uzF�!U��X��9+7L�g�j�{M|�):~�+��]H���"�e��
��q�ke8�{r��ʓZI!�j����د�E�x|]7<S��)��2$7���ă�澑"n����(��}���C��{G��=���z#%;���q{�ۈ�T{���]���[l᫻V��?���:lM�4͒\E���9�_�`���1�7�6,��}���l�x~�*C�q���'�H��M��TيZob�ܘ��۲٫��5&�0I�{�%���[��F����]_�t⩻�9�G���|�ǡ��z%����o�tvH�Qw9�vU�nk����u阮�j:�o����p���Fܑr^��e��+��;[5i�~�Fb�����(6�1NTJ�D՞&��7�-}����}Rw�\.p�\���.p����C���$���j�K�´<�*�*��l���\��60NZ���K�[��N���:��-��n>�n8��R���ϔ�O��q�U39�,2�V�™��ޓc�w����V�:V�O�$�o�N��^��Uʯ���c��;�z�@r���^�Yn�Ud��-J�C�~m)�/�Z���g��G�.����$*L�d�i��W���@0*S�h;)[��@0#��L5t,wd{=Z�ẁfE�bH�a(��6����5c���
_`
��o������B�%�n4���k:r�iD������&#2s��e�E��6��R�_�#c=�������~N/�0�1%�F[��d��"Q�T�xV٣Z��~?�Y����[Ŵ4Pq,�27�d�uDDj�I[H��v�Z�z�b9k4ܝ��1JIo�<$�KI���ւ;��L��O��ǁ��R�&�MȮ�n۸�i��&��"�3U[���z�G�B�?J���#[Z��1p����c7(�g'�-�o�Ӻ[�+C�$a���}�	��8�:�m�nRQ�7�$�5s��M{OUMڨ��ի?�R?h��A�t�ƪ�r�w�+/��Dk#M��Nj�}X�6�FL�z�N�f���f��R�O�S�|e��+ax�l�Ƣ.�*0�2����+�!��Ȍ��e{�8��p��'�Q��"�߳�c}��~i1�@2i���׾y��G��?8}!��R��
�s>��D�6�+��D���T�a�h��ݳ�?����dqn��[jD�n�A\�Si��*�D+#�ǵFŌ�gٯk�Wh5:M�eM�T���zM'�I>4���b)�N�;�،5Vjթ�7�$����k�Q�-
�#6U9|��&���ψG�b"�\�o���z��VkT�tz��\�$ʊ�[��̢���l^��Eq�X�bmի;�!��U_G�&����[�+�5dM��V!�����Ny�߅�Wz&ct0j4�q`A��֥9-�q�죙b���cm��T�t�e��z�Q19��=�a�9�0�J�>e^�0���q�6n��]������EQE�]I�L5�5�:�B>})��&bR���㈅)e�ZM���.q�����Z.�Սo�;�cs@�h�|��$|��iϴO�;
�x�oB�f��3��X������.f(�@
��T��+A�-;Re��ˌ�bGG�x�UV�j��h��~Q��x,^�e;ָ\F��޻��e��x��}ю�nb(U@
�\�w��[Q�xO�s!���O��~��q�\/���؄>>�NC�J�/,��������8�\v/WՍ�F^�9�9�Ӿ��ej�=6㍸�Q�ĝҤ�2>a����N���1�4�"��\/�O�/�^��\�m��k��k���LIk�E;aI'��}"D�����R}&�^4������d���Mt����ى�����J.#[����D��^�}[���4��o������Y�V}�LC�Q�ؓ�Q�Y�Ǻ,DNs��-�UWN��o�~�s>+�۪��N�dƫ�:1�ƪ�i�GwZ�ͷ>~�Q�娪2��W*�V�4�ݔR�K��t,���ev���mO}#F�tw���Z�o{_W_�gƞ����ǓAǒ������q��j�5�t�G��+�^�~�+�|+[/�	0ƌd}\�h�����7�M}����_U��@��{?�/���O��κC���x�L�U�gqj�}���V�[���.+H�k�=Q�6�����]���Kdސ��Ĕ'�kW
W�Sn�j�7a�ݫV��]s�G@m.�L�S=�N�`����h��-r�cՏ7I��v���i�m����ޭ�td#�,���o�;��eN�n�?&
[Gx2��)4�R���)Ԭ�i-�Ѻ�i��l\�L|69�9�F����*u1;\I���\�E٤�H\��7=Z�U����Q�G�O�4�;f���♹י�;'�D�5���S��سjX��V"�F��!�j�l=���T���-\h�KWR\f|g�1An��E|fw˨�z�������7^*�IrB���V�7k�����}���j��L4�8�fvU��1�	�)ug����(,�8eĒ��V޿M�Υ���W��ڌ��T9���Ϻ.��)��p��M�@�)��h��m�[�cx��TØ��CR��Y�
�f���b���>.�����1���V�'�G�����!�i
�귑�$+�h�DM���'S��!��\�so��f����9[�~��ןs�a�CJ�S�R�'��[���Ң�Qr+w"��f3s
rm܌�������0�I#1)*�EA8�v��2��-�%�P�#0�*����D��(oyH�+��[H�dV�4�-1��I�L���3�n�W0)�u*{N�$ �e��V�0��P���V�&$L��8��2�ˀ�-6���{7�]�C!�CҎ���L�U�=�56���I+��E�
�\<UC�^�A� �U���T{ҕ�۸�����-R�U�?��[4�NXյgd��s�`3Xy��C͞f�I-
�J��`#�&*�a��Ȭ�LVt�mJ#;�[�a��$[q+I)'�'��\d|`#:���Su0R�u�V����d�r����� d�4�v�[����I��`5����%dFOsR{�d���q񀍫���>#N��
��M��	�u*��q���J"ح�w ��c���p>ڸ[���p��/>Fm�M�a��������R:
"\5�7\,�l[��}�}5@mMР6��1�#�h
Nߋ�_�������������~NS���=+�;�*p��6��%�\-iW�Y�j~��1^�^�7�-�~��2^%n�(��*��%J����l�b#1���c7�/M���V��;��M
�\�Ն�*Yq�S�$�4��!�+�L��v�sL�D�D9�q�_�����glJ/�z&����52R��IUs|������{��(�DM�N*��]8�ꢟ����*{I�e��Ğ�E�dSGy����)�
T]��Μ�qx<�uj]��c9�r֛+���3��GjK6�T�G�p�Wʣ��cj��,��}�U��^uf'����1	�b��V�-4��2KwZ�F�6DV�����v���-᱗���9�^"a�7F�,==i�\��h��㞴x)��v��Q6�g|�sN/M�c+v���g�ܘ��q��إ�<-Mf���8�j��4�a�.g����S��c$\e���f�ss>�6
”S�C4���8��%m��LQ[R[Q����ϛ�k�n���l�^��]��:�M�5�8�ly[�L�juwG��'��%2Ѷ�5�D��FV;܌�
���N��GX���vnF[#<��#jF��1���)rI:�O���.�nm�U��i��&b�S�ު��nrΪ�Xu��R��P�+%
Q�R�K�+�l��Wn3����+Ѯ'��j4�q�2A�*�m�[���o�!���#9D�ik8���3�6�{�h�԰ұV�8yT�m��Н�Jmn-�B0�M:�7tŋw�)�?Ջ��%{<�)D�5��y�4��Wʝ����b՚���ُ�V���)�Ԍ�Po$���K���c#�# ����'�~�7p�WF�X����ed�wF����n|e��-�}'��)?"��I6���|��S�I�h�
��H�5�����w:?�Q�a��_�!��g��%"�:l�[.� �����)��eۧ1fw���2�v�9�X]lLܽ��Vr�nbu0�E�����"Q�}:R'D�:<��2$!�ID|�m��ݱ\e3L¦�/�:��4��h8�R��6�S$J���ڈ�G����er�+4V�Wj��J�J^��g��s���j�_H8�6�p�]�>�?��D{����1��_Ô�W��hΙW��u�=K��Ѱi�}Mʭ}���I��-D\�j6O�����=$7H�P���}���[f+�_[���5�S��W���f|Z�|O�u:J)ԡ�"�%;�,��V�<�XN6�9Y����|Py�798����h��%�'Tvar-f��Y(�M[����ao쪫��MqVv�M���'���m��ä7.�T����լ���,�[i/~��6��f�������#m�LS�U�,�^˜>���X�J����t�9��T��KD���=���4D�y�]�=Z��T�g��e�JĘ���l�Z�T�S�e�HO���G�i�v-e;*������u8\��m��s���H���[4���e�5V�%���\-�r�J�
�]�ͽjk�5�(ϩ����̪�SV3������U٢?���N��Dy���E4��qJ3���+�8�V�NRfcl�S6oa�W��DD��,ô�&�S�Q��U6��Y��E%i;˱���`�4�wEQ\Ƶ9�$�]6�ULN�Yx��r��uCp�Q��'5���,�փ���{xCf
s���,���X�9�3�s�9��P;���G����D�C�пg��*�`
_J�?߷��ZK�|Qq����b�Tȧ�z�9����eD��e�^�W�VoT׫9����+�����\���k,d�k76j��.b~��F-��<'ǹ����hS�GF�s)qD\�s�xŕ��l�n�[y���!�x�:�k>s�(j�9ϊ�g9��ya@tj��ʣ�=���~rO�/�b�Q/v�՗eeԺ�\Ob�%x�I��'5����HN���ӝ�߬H����|~J�_d���r����t�!����b�"5fded@5 ���;Sƃ�!���Fʸ������Q9UD��ݺ7p�2aÚ�n�x��_2%�t�b�����R�=��c���niE�u9L���;��d�֧p�t�'b�������v�Unbr��<3�Ϊ��o1�SU��0R�,��V��ͣ�Y����FyS�U�oI���cZ�ԣ����Wzp�B�LA��ʚ����m��U�vp�[9�6�X�Y�ϵ���]�}¥��
�Q����W(͊c�R�?9���|�	^z�b��	4ǜt}\�h�����7�F�Dlż�r���Q�l�x�4�����e�u�BMN�NT�g��r���n��Ta���S�[��c-Y�d��)��%�I��(�4���={����$���V�3ܟEuSO�UN}�#'�T��II�*3��]�GZ�O6� ����&���V�۪5���c�=��mN��"��p��<�#7{1ª&���\�f*�~J���j�#ҵ}���,��dC4ۙ���E�~�QN|^���Z�T��I��'��yJ�R�c"A��4L�F'M�srv�:�G�$nh�9�r�P�[a�L�F%~sL�U��1�~�|��[V�kt��������E��*[���a6��,IK��S.
$����&������or�UG�E�2�4�;�;�R��
�:u���d)�Q�m��޳>m_&��֭�]m��9lo/�kZ�5����v�T�-��	QP�b��i��4���K�l�h�gnQ�e˸���yѯTU�)�gvy�&�Q�͓CT�e��9/Ø�:Ka&D�4�	6R/|�[F5)��z����U�M�Y���&��N[�{e�7���п��>5'���j©.�-���h�=�Օ�ٮ$�mkݦ��N�Sח��$���:��
�Z�I�j2JR[��a��}��Аň�/95�3_�#�(�C���y����tv
,[��N��"��9��t���
Ϻ{	��+m���>.��}�����

-��~�������}3Lλ�FeV��oQ�M�~S��سj�^�ѵb'����_���
��R�LDd�qb��-\IIq���
�U�+�3�]E���Dn�����*��=\�0���|�g�:L>�T���x��׭W�8'�5U8X�L���ҋ��^J�ֵ�e3A^��e�Te��+�Ǻor�-�(:lضY��u�s���q��5gs�X;��t?+Lz��gs��kG�b��9�B�	�&�m�"�?	��ŝQn�ۓ��V;\�D�]q�r���ZL�l�;)8����ѓ�i���{���|e�
�Vh�{n�@}"���gfs��=�W�:X��)��֛*d�b�e�y����w�$ٹN"��ަ�xK�,��ջó-/Go�p��’�B��E�*5f]"��F"a�m^��tn�zKF�&�ʺc?���͐��E��benN�B=���V�'�`8�p�(�
^�֧��J�2{�A�b�.\���H�C��cԡ�<Y-�֕��+�,�Op]�w��&���
�M�
���%�����L�с3Q�S�Q���Q��y�ܷ�p�J�v�`���Ga�M/��IJ\,��Ym�.�a�t���M�I\�֙BD{�dWe$��Ŕ3��M6�$�Fu��iFJUՕ����dZ�N�0F$�R)�y�"i�˄�!��P��j��pD����0>L�ѐ�+��1=
E����x+�ͬ���&9UI���j8G4��a(�m4�m{���.m��Co�.�D��~�f�)��[��m�[I��>5x����2��L53����͗�\$��0��;�ұ9P*K"/��F��n����!�Z��C2�E���㌀LCgS(=����;]��Ce~���g3i<�?$�}h��H���X�z����s+��"63�\��\xO�ưv5�겯Ь8d�\�.$�g�k�WX�V���[���z�tF����N���"�F�Sr�M�U��.˩&W�1.�+�����ZC����ί��^����
�ː�dZ�Y-���˝'���i2͸�[Պf���z�E����M����\�q�8'W�;èH��Rj;��f&Ԕ�p�w3Pͽ[tϬ񊛸˶�mULQ;ve���8.�Q�Th�݊t�m%*�%��b�df"������ג~'^uQs5S��Ƹ�
=����)h��Q����Ku���{9G��i�)��F�>�5�L�Z����Ҧ�O�R�p17��Bڧ�ҧT�I#*L���<���Sf��ݢ�l�j��?[��V=�f`ʹ��$�#�e���ă#��3~���#GX����9�|"tG��t|nS�O6�u0�N���Y��̈�k�XK���:s]�>����(�ڰ�E''Tqt)T�-�2�A�N��5�+_�I�oV�i�b���p��*�����A=@�b|GV�A���U9�DK�m�ka��s3j� թU3e�:1W0�h�U���M�~IlcS�5���5����[4Nn�(�k�jRng�.S����:�#`mW8��n�ڷ�;7g�L)����V*�q�hY.Cȍ��u �ϔ�ˌ��jެ��1��^���=\�['�Ũi�M��uj���ښu%�3���H�o2�؊�8[h�х���{��ZJ�8���>���+FK4���Ŗ�R�g���f�"��)0Z&��������7J���Շ���A�Sm�����V�q�ӊ��u^.�[.򳞳c��y�ҍ
nBH�e�̃�ثe��W1�����n���X����R�3*x�Z��*K��q�;������)�:��XZ��2��X��hs�A����
*-�F��C��n|e�Y��a�����?�$\�c���o���O�&=��(�M ~־Z��1��qG��=��[��(�j�L�Έԇ㭢en������ĭ��F��]3NS0��6i���b%�z4n<�5��b�%)+���ڧ�W�1LwCG�:p��1
)%�ɐ��$�Dd�q�]�b�,��z����b�˚�ޥ��1�\�}�����*⶚�t䲪�3�;�Z�O�����<T�J&��$jl�[;���V"j�.?KQߘ��}�)�|a�QsU��K]�[VҒ[�ǽ%f��f�w��۴ۻU����SJ�TfG�#_D�Z����:��.��Vt���S4l����.�*�+�%\'�XN(�֫n�r�ZocW��l��}�Ea�^y-ѷVc�0��媮��ʍ� S�e��t��G�G���YI#=�yv�<ª�X��Ӝ7s�i��~�mGV(X1�M�W��ԕ*%8�,�ϱmş����q���G���~�mT��)��ЪU
d�c��,��v2�73-��c�
U4�M}uU�5�UT�=T�N����t���gHJzc	��{�YMl)8V�b5�s��N(׎��IW���ך�g�*U<3�*��s�zM&"#Re8ږ�)*����V�=Z�r�V��u�uG��m]�)��)�6JVN�ip'QXT�+�	�j�% �[��l��k(�{��=:�QT�g11O	�R.c��U1��Q13�#���yD��\MmU���Kn:��-��ի�D��c}ʱ7(�5s�5f�n�-��M|�FLWqTy�F�����:�S���Gup�pF�puQz�_b�rͮ�e5Y�?j��{��;U�p)�jR��J��4�
A<y�H4�#�[h�v�&���jk�5�\��39UKT⸴U;���G����D�C�пg��*�`
�gTh]
	�k��+.��W��1��yF���TMTeZ'P���U�'�\��w5���v������n�9��P���U�'�cw��np:�ŝ��)?hs�ks��>,�yI�C���s[���gh��O������u�;E^R~��7x���v4��3j<�j�6�i4�5�?p�z��6S�*�LY���J��˘���Q�j>�	��a#�6u�����qi��^[�S{pIFf{D�����J�fmFMk�|Y�*���>ks��>,�yI�C���s[���gh��O������u�;E^R~��7x���|Y�*�1���8C���W���9��5���v�����������a��Т599%6��O��_�^X��#=�[11Lf�[Ɣ��2Q�{?J�a�>�����*�|܋]�fN
iJ�w?�D�U�Z^������y�}�FHZ����F�69��VY
��;��U�E���'V|��&F+[ջ��xU���.Kr"�򉕰_�I�~rnA�5vWݷ?��~��uD�߲i�J��5����S��t~��ڿ���'r���ï�?�9ڵ�z��~;�g�bI�I��%�:��rr1
�Z�c(���u\���o\=j�j�+.�|��������+N��q�%a�������^:>�~-�2��(�Q�|��濱�E����3�Uuc\��^�1:l�$�Ʉ�[6�����ml��pS�c����uoϵ+T��K��l��y��&+l�ᖽ4/1��a(�eˈǚg+U�<c����Z�r��j�-����T��D��-A$�X��8��񙹫2̤�D���誩�)��9�cNw-ѩ�N٘��=]�eܢfD���fVPi�c�}JD��F��K�tܕ�1�EQ5�{6o��'UW��M�Nb}i�2�rݳ�Uy��L*lz� E��Lb'ҭk�'���YKR�������b5❑�kV��Z��Mɪ�����#nퟚ�*Q����RJeM�$��e%*F�BSl�"#5DW1�b��՘���<��M���4���"r���i�2�V�m�V�L�XT�}U���dz�P�4�O��!D[��VS��b����%����|���2��?���˴C��1Xfk�b:݆y�F�W%�����V"&�y�njV�8�4e3�_([�&�\ZA�"T������
Y���I�	v+��x��'S9�|�rn�MW2�h��#ns�g��i�7~:(_��ӟa�4� ф#�_�Ll����_P���l�B�OՕ��58͇@�U�
�E��O�t�+�ҕ �I̟��"i
�l�'�z��{��N�7��	E�ըͻ=�7�f��۹�\�Vr�ഌw���ɖ��HJ�3��R���ɱ�
.#/�:k�;W(�"":�m�!~���fg�%۰�$��Z*4��m[���_\FC��b�Uj��p��/Q�KF҆�:[A���F�L���[l�)�w�Y���^�Sim-��v����
GG�>��%����Y��g�6��O��'cq�f8ԬѺ6�D�;(��C�ϓK˜i��c%>����E�[��g��s�SU�g��Wr�0���զ78F'��LITUBz�5��i?Y����:���^.���U�pF�oFJa�>,��\R�/����ъ��U�)8�o�?��%A�`='���5��u�W�5Y�T|W�d>w��m]��~��W�i8Z�צ2����Z>��f}%�]F5͌��qE�e��^�"]�)�ќos�U�	��.FtU��;P�tq�ʖ
��#�R�ണmYfTs�=��>+
��S�sr�Nh1�s�&���]1�Q�L=Kv\9�#CY�T��n�Q�b8F~a�ۢ3����!���Mʪ���{>2�XA�X�M>i�D��_޴�eh��fQ�VٞZ��S��QN��<��u����/�9&�cTq���`v�zM1�����߬�&W�v#"O9� ��w@κɰ�*��E�L�Dm�~	�u��3%4-*�C���}S%(�9�yM�����d�� H�08�y��bڱʧ�;՞覤�,�iG��\�W���E1����J�Ԟ�J��RJd�ն����������CTz:SG��l��W.D�����V5�9Rf]{������a�t��@���LӞ)L�}��ɤ�l���#vb2��]��F�����'�,첔���"=�+�A.�A#*2��f�G�`fa�h�sJX!Dѭ:�fQ �[�i�o���`TaV�L�iO�{b^E�|ޓ�\�L)�@�Y$�����>��k�(�5в��ڕ�!\�J�aʜ%�NCN�W?
w�L�[y�6�R�KK���>EZ�|�`3!Sɥ��;��@3�����^�;/��.4wqt�Y�)��R{ɟ~����.px��o�̯��d���as�w�q�?'�8yX�D�!�A�5{��X��Q��
�6��'c��r��Y�ߥ��Ck]3%��E�u� �{��Q�K�GZ����U9Sjf{�^��bE�o��$�t�N8���¹���p1�查U9E���T�z�RJ'T)�'X�uH�a�����s��I��>Og����0ƷUՄ]nm^Op͞��mf��<Ξ�o��e�#V_ٛ��^�S�)N�Lꑜ��1�5�~1�����Y�[<e�c�9ȧ1�F�D+��Km�|�<���1̣,���%�*��[����	�T�ӱC2��#p�CN�3ʳ�S��wT�s��=�S9¥�jGBtabv�6M~F�y��.l���X9�o�c����N|6��3AT3qK�2�Hy-!Yyl���'Lo�ꏤW+��3ٽ���*�ܲ��G�He��j֣;R��33�be�:I^z���GV׹�������m�b��:�m���u,��3���/6���\�M����ǘr��Tl@���jm�-\�J����*�Q4�So)��ɋ�q�"�W�Ͷڛ�\"]�q�F'������6��X�)�%<�#b�ξ���oR�+��Dᙄ`��6W��S�b����Q��LIX��e;m[.%��W�d���'��h�Er��,���rv�i�'gbTFh�)-��BM\�jYm	�Do��I��ٵ3�3*�>œƾ"L�a8�HZn\WJ̆i�D�.}&��ʫz�ߛ�� ��Q�OR��W\c9�Y׷�U9NN���zi�v�D���_����G�$��^�x>o���)��Ǵp|ɤÚ��^��;��(�0�􇿯�Kn�>U�ͦ-V��I�˕L��T+>��Ίj��r�i��ƯK��Ӫi�	
]l��?�_�ß�Z����_�nSE���>���'E��#�Pw�S���5���S!���n\����u��9<�t_֝�O�3'-ﳔqY;<�2cʺ*�ƩPl���4���><���ֵ,��#H]׽T��om��KD�t�
���WMm�꾛����Ji�+;_��#V��&���S�+��+qcV=�Fr�٦�~��E��pb�(}ⵐnv˴\�z��)�j�<Y�i�����6+���7c��l��Z���f�w2�b�"�'*������4kZ��`��I���V�6dәȲ���\W�t�7*�&z�~KF��qMQ�I��	�j��Z�t���b%'#H5jfH�i%-����n�k�ݼx��.^�#fQ�i�h�uS�F��4��qr�I4�Y����,��ۦ�y�W櫣	rk��k&���=l��
M��j�JK��{��I�_�NjX�77N�{���F���cF��A���n�`ԕ�F�����6F��nm�\�L?k{�h��vne�T+�T���f,wJ6CB3Ln�eD��n=�V;ISji՝���Z:nES1���r ��ڥ:e%�!���ɋ$���V�!*#=��"�w.�TU9�9G��J�Tj���晀1�Rf§-q�+���-�?VK25wt��')�k]�v��#c���kv�n2���:�!�kvR�es#���,��,Z�nn�n��b��:KP��
GW�T���&h>��e��4�4�Z֦bs�݄ѵ�wV��-�
��P�(�.�2B�q#�W��G�3��w�c�Uo_[d4�\��b3�W�x���� )��P�:M�|���ͽ#f��'�.h��Fs00N,�%�§��o�o���m�ٌ�s�B�����Q��Tg��4��{��B\"L;���G����D�C�пg��*�`<o*Dj6�;�m�j2v��3�i�NT���ֻq�(�-_.V�'O�]���a�U��j�t��ۏyF�\NV�'O�]���a�U��j�t��ۏyF�\NV�'O�]���a�U��j�OkV�x��17��r�qNW��#G��n=t<��˞��H��͆��3e�
Uht��1?]���}����GhmM9)�۱�Sn!w̍�l�_X�<����-FH.�ֻq�(E������k��g�������k��9j���\N�V�q�(Ö������k��9j���\N�V�q�(Ö�����N�V�q�(9j���\]
��ʑ���h%-K쮭�{�v���ao������N�Y����H��h���S��Z�7�{��"<Җ�n���~2>�.
>�?�K�4�QW���p����ግT����\Ћ���1�[�_��ai�.����FH���25Mpj�JJ����~r�|4{_�D�e;(�ӏ��j�^�X/��hՏ����O������Y�"�Iƾ�;�u*�'MT�e�����G�݈��U��N֥9x���Kt��[^{�Ց��7�����Ǒ6�p���Z}Q�N!/Ò������Gt���T��c6/�L��t��:�LNUR���Z�&+O��[�ʒ�ؒ��=\�ƮT�Y��G-��4��eM��Mԛqo��i��ZR�6]�\J�];����[�#��֎������g���gn�=�9�%�*�K��2SjZ��f-�g����7"��yӗͳ�����*����d93<�kОi�m[q�"d���WI�7p�m�����c&��~���L���NJ�B��f�P�Y��rF���gs;��)G��h�{_dFT����fΦٜ��uB������=��2X9o��-c��������ɇ������VfX�7r�U<f<��gr��D���56�����B���$���s�=��axn��&���������AS�Ǭ/ܱ`^���>>��a<��.����D-'Nv'���剧�7x˲i�Hѳ���O$�\m;�/���
��a��ͩʯc�Қ./Ƶ;.|܆
SP�+����N<��w���\G�_Un��9Lo�-E�fb&i�Ҙ���b�z�s3FeV}�5�����>.����Ř�T���V"s��F��!�`@�O��8M%���+m�q��j��l�v额ZvD 4�)�K6�P���ZȄ�Nw�A����p���+a�a�wR�-���YՈ����-�H��Q�w���Y�9M��O�|����!��B�"��_F��X�-�UC�aM��V�GwY��;�yJ-���y�\FB���W}_e��/�Lv�c+�;���o8�F�k ��ʑ-f�4y]Ir�Qs�{E��sEi�F�ϫٝ�.?�����������[BUo�s�$h�s��j�}{W(�LO��a�%D�4�H`�gzսk>U����V��C���ػ��g9��؆�Y��G���z�Y�b���
(���W KiIpRE�RP��5$�ӵ&e���$�͚�#�`BV��+���H@y4��������k��G��֌���^���/���u5-A��%>�|eʓ����"%1S��K'/ڕ65B)�e����\�H�@�N�q��?x�%�����h�B��o�Y�ͮ=Sױ����5��zi��m-��Ch,�Jvn"���o�̯��d���as�w�q�?')����8Oy�N�������պ�?Ā�A����l���]����]��Z�XM�DՕ0�&��*�UZ�b������jU�;��T����Eg�Cy$�y�Fa����{ժ(���o�3�Ou[�h؂��4�u!�?�;�<�G�p�I����b
+���\/�볁�nl��2QB�{o�=֣��{�d�tM���=rq���k�_0��>R,�����t�3��	¤��y�
�:�pKjJ�cD���fǻ-ӂ�^5���][б"�s��";m-O��f҃�v�f�ɿv�r�e֗]�cJ�Y�:�Űh�e!��n���6f��@4#ip�ȳ�0�G�K�\j��U1��[��爦���&"�i���9,�Tg[.��m�:���Eʨ��U�-E�D�Wn��ƙ����
\7��'���欔�V�����/(���8�s���uF~��_�OzR��|=I�
�C-��ZiJK]sؒ�A��deōrf�sT�3n��HNS��-C4na�����ڞe�J�T�RJޝ�vo{q����j�]�^���˹��15:3u�*ђ&��Nm�T׺>�]�I��;��r�jz�x<-s����v��G詩��[
M��1���B�铩խ:��:n��њ���&=YH�5E�h�MZ���s�f����x�I��:L^�V��%��{6m�a���Vζ�!��p�}i�g���\H�9�(�k{�3����[��Q�O.Eo�CEϽ�N�+�B�s��c�ۺz��Oc�uQ�
� ���F�C�y�y�sP�3u�7�6ަf�c�Gݢ._��ujݳf}L�Cr�/S�-ԛ�gx�F�1�m�V��Re�n��
g���4�q\Z��gS����o�Uc�>x���O��<���a�����?�$\�c���o���O�&=��(�M ~־Z��1��qG��=��Q��UF=F
�K��q��q1񍷭E�f��-6/M����}��&�}2	
���K��ͧީ'ϴ�q8�5xj���.�~�E��f�%�/��~���nv�{��0�t��V(��P��%�"�ô{
GȥĐ��>nׯW�hW�ԧڗ
-��ry��}Bn��"1#���1G��c�����-%{R���N�����c=Ѷ[}6�����+����
[.�\5%�e��M��اf�aUr�#����*v����r�^��;2���ƴe\��f\��;�}�C}7���5�Zk��ٻG	�K��x���ra%�3J�DG�q#?�[��G�D��~��.��*Y_n�t��
#^���W��f��z_�;�̺ՑDf/C�?s�iQ��<�{�&��g-��iK�r������
�L=�&L)7St��T� �[I���a$�|�*�k��De���ܴ��rg?��[ڝ?�i.�h��\9r�:��S*m���xIɻ2nB����)T����u�E�]Ju�9��8�P=-H|�^�,�[�w>Rh৓y����9���/��s���:�'��N��t��PfbQ����ЗO���e����?�"I�3�JZ_3=�
�ϒ�^3e�rꢤ|�5g�],m)ͨuyPnb��b��	ʔ0DZ�Yrm�6�tr1�s�ţJW_-1�(��f�a�l�S&|�j
�T*t5%	m�+�S�_��r���r���4DS�f[i�E�)��U3;c&�7!�g	��8ѻOg2;�mS�K�]���Wۏ���*N�?��葉`պ�^4���dz
I��2��\i�-�n2)��O�3��E��ُj7,�}�W�T�s]1��zd�;�S��_�q�zb�b�Ll�8ɫDUTܘ�晉��	I�8Jn(�t#��#2���RgnB�+t��1��r��n#f���-�N��_3�<rs���B��<[ЫY�5� ��G�#��F+��!V�dS`;P��F����2�O�v�֜��Ӝ�S���S�OQ{���[���n&�U�ѝ98�B��B{)���sw(�g%%t�9,�*�?��O��ŵg���0����ܼ��v��~1g���d���ɣI��Y.Gx��j4���⺪r��j��Ǘ�]��F��T��J#p�[��4kT�n����BI%���.!f�Q���?�1��Z��+4�٧��q]g���!�j�1���O/9eϫZ/�2M���\�"`2�1���/HS��u"��%d��kC#U'Es�6���v�lLz���)8j}K����n�H�U]pdj(oخg�5MT�"S��5���i�fB�mY�.-�6)�˷?�V2�c�~�J���U�B��1�UxO��G����Ĉ�2E����{-�.���LK�C���S�&�)+/e�$����G��w�1��XD���=)6lM)6����K��VP�i:m������g�u�b�v'�)(̮|v>0����&�CO$m#�Y/r���>m��\^?���}MSI�NRkm�1�v����*�I����LV���D���ٙ{�N̦e�%�.�.0
�^���M���>��if���q ��|�R_�U����sV�w�Wi�Z�z���N
a�8�@_�2D	�N�v�ĺ��A��5�D�w&�����3�!W)�p�쾛ۍ*��>t���^�6�g��a��v�����ۚ�%���S2�ҊRK*�eZ�ZKq.�����떣*w �e��kU{�p`ā�p�K1YNV�N�"!���w�[�M�Ӳ!xyzr�6bv��c�Vd�=t�-��{�;�Ff~y�0�G)={���f�Ũ��?���[9�@l�,���o�K���M����C�W5l�~Ŏ������k��eڀ��E�Uc�*�|Y�iw�yxj=�Y����#n��>V�z#Ĩ3&�Dz����_���毺綄~���6J>�p�5��*9K���m�$K4�*�O��`�i��u+��"63ݮ>���<'��v�2�Z�W��.Lc3J\#4�b4���b��sL���xzo[�un���	�o~��~󾐓Ϫ�
n�X�U����J��AϪ���ګ�B�A��`A.�;�>��?Flv��W����w�}W:1c�W��	�{F
�l���tb�j� �����A�E����F,v��:�1an��v;�>��=�گ��Ŗ�@A�%��U��Ŏ�~A�!1i���AϪ�tcګ�?d&-�^'} ��p��;Uy�ŧ��A2�=o�3Ϫ#�Ŏ�G\&,�>'} �p:1c�Q��w����9�\#3�~�^G\&-��@A#>g} ��p��;Uyp��՛� ܷlw�}Qы����LZ{�B��y�\���T���1�\�w��p:1c�S@�Lzt�3���K�y�n̳�ۘE�s��֭��1�6>��_����G�$��~�x>o���)��Ǵp|ɤÚ��^��;��(�0�􇿯�JJDgPq^�PL�S�Ăثv+O�Zw(��F�������5ګ:e�U4��eR�f��2��f�hQpO�qUo��:�8Z����9SK���ϼ��Z�y���;�F{�ό]SDSF�SU\�9����K�Tiq�dž����y
�u��*��/�i�15}ԋ8����}��[�oh�l-�&��u���Ȉ�V�)��o��'�_x�:2֮���'Ɣ���vKu,y�j��N�-�4�e���5grՑv7=��6tu�*���Q��]3OT�0�+&��0�4%He�J�i
<Ɩ�ޒ�\��)�m�b1��|�̘����IU��R��$Ԕ�+6�Jvw�n��������"��kN�q�'Mu��_IM}���r��М�J�=��h�m�ML��I]��R��*�C�X���$j�GC)S��J�k�ے��uu�S�\�t�UuDm�G�k3��+2TG9�I�&ŝ6���j�4[ԍ�1U^�[664�G�o��GL%]2n�6�J��Ҥn˰h�n�\�Tnn�]7&�o�����.$�jK
D�8Ӳe�m9�+#%���覨�nt�̯]Q1�*�F|}%bV�2��-���L��y�ȷeY�8�V��3���|D�
�i[�o˭n�qJz ��!����\�댩ú���Fkі�s�Q��E9l��v4�s����$ޥ)DwȈ��325|#;�-�-�D��[��i�W�W�U�W��6a�i*Nh�%���w^�;Iy��<Ə���9�w��Ui�V�l�纎?�͂��,(��&����s2���m�V�٘㵛�J���dD�؍�Z�:r��sF�R"���w>=�E��T�}�{���"'��7CC�h�|��$|��iϴO�;
�x�oB�f���X���>-�g�ЅZ�馜yԴ�MN�섖�1��=��f��M)�u�].��%��;5<Vl��$75�s�Vd��It���wk���q�]x�7���c{��ۍ8��I����l22��FS����b��
�\�w��[Q�xO�s!���G��~��q�\/�,?�l_��zz2!}ڂ�'�}d<bp��c{�9톌��4,��=�Oa����T`r<w�>�I��v)-�1L�1�È�F3_[�}_'�1Ku#�cf֬w�����c����#�2���M?��L����j_+[�v�?Q%�n9B��Ϧ���v\��>��a��&u��*�~1��o���eW�O	�	�ǩ]x}�y�ꦿdJ"������5󉷲�e�Q���E{m��(�Zҵ!dhqe�DiRO�����4d�����^��lF�ǯo���չ�XFkD��b�m>�2dS���Gc�#��e��s$�{��i�X�_f7�x7х���e<e�T�n�#F�uIDF��K]󺇎o]~�vvcw�z�M��{��;�>h����q��TwR�f�>s3b������v��X����=�D<|}M^�^����q����_�p���(Q���[�����H�Wa���nI���7���$���x}��al:Y�q&���*+��9�U9�R���ߡ��R���J�6{[W�&C��v.QqpX�j��=L
�
�ǁq�C
�VT��ǎ�"ߏ�ȯ�"c0tގGZ~��Ua��L����+�����)p��'�u��;
]����a���FtN4�Ґ��
n(���5�/D94,o���\>�ɞ|J-���?~���i��uU����֢�r�#V�ڸ�G���-�\S�����Ե��jQ�33�1{����s��Xe�[����L�[y6\�ձ���i������(��~��uh����xC����#�XX���"k�5šj[&D���Q[1(�� �v޼e)8,eX{�r�����턻z�Ϫ��~�_���n�K���L����)�٣�:ݰ�oO�A�):S�G��u�a.ޟ�3ꃘRt��f�?��v�]�?�g�0��M����턻z�ϪaIқ��<�s��	v�)�T“�7�4y��[���S>�9�'Jo�h�ηl%���}Ps
N�����n�K���L����)�٣�:ݰ�oO�A�):S�G��u�a.ޟ�3ꃘRt��f�?��v�]�?�g�0��M����턻z�ϪaIқ��<�s��	v�)�T“�7�4y��[���S>�9�'Jo�h�ηl%���}Ps
N�����n�K���L����)�٣�P���K��yL����(�p��t*d@�G�ٚ���B�y�i$��%�e(�W5�5O\��xP�|ˤÚ��^��;��(�0�􇿯�JJDT@`w�~/��L��9
9���a�~�-�U�����.�J�x���*��¿(ӈ�j�(j�F�e
O��I�����A敢sj΢q'��">��mY�N$���G���ͫ:���O���C�Vsj��ty9fJ��OQ�W�Ce9�{�	=m���i���΋�}��ڣ��vi�rU━��
���Vxr[�'���/�����h������}CE�v����G�W���⾽W���
]I��u��#�5Eэ��H̛Z��{	T���uz����z�h��q'��">ѧ�V�ͫ:�ğ�O���C�\9�gQX��	�h�4��շ/L
3Qe'+��r����Q4Ӕ�Z�*rI
�����U8N�#�O#b�<�U���]�jkSt}Sl�⺇����W��J�Uu"Յ��!`�G�����x敼�j��w1vT���q�%x�`�F
z岜,��jM�Ko,TYgٸ{T���v�E�h���ǰ�����&��?ӠZh(�����_���>n	�v�9mW�z�`�иJ57��J���%r����ƧZ[�F綖��܅���2D�����_�F�D�ju{����H���.F�_��M��I��ȯ�&�ꖘ�mD�j2}���$��������]�x���ǪwLoH��3��Y�������F���ܲV&��%+�RyT��N��1Mۻ���{���#�n���Vgԥ���y�+"����VJ�$$Z�E�ʘ��j�]u�uNlC�q�'�E5�3�<�]"���3���C��	�$��|a�Җ[Bnzőȷ�a����R���@TC�5���hE�}�I�b�4ł�S���f�	6}	-�1��h�޸��x�Iԝ��Ri�)O)O�N���H_�El�a�[n����v+I�T]�-�,��c�Q��������:�D�-O݄�t�">�LI���K��5�%�]qJO��{��{1�wr�T�ŊD64�`�	\'�eb:�t�n��9/�m����18��F���8J�"�����$xq�$�줐��)+�j�j��|��tE�1�F�RҒ5(��f{����]F���5x�ν�u&�2�._	��\�1fV)��F�-�2�śQP����cJyN�pWCY\���L��*�z.�!�QLԒu���'eː�d)ĥ&����>`�1N��6��e�*[���_Z��-H�$�ȕV�D�,��ԉ�R"������1�yl��4��.�3�o�T5��/6wI�O)���e�=F��
sj&<V��*K1�Ko9��d\�`*�@5
���-J�"�.;��)�:�F���Gb���)�bvGr�抱]ST��X����k=��Cߦ�<|�}��{Ehﵞ�} ��'���\?�+G}���;���<|�B��Z;�g���H=7���z�����k=��A�O#и~�V��Y�8w�M�x����=��w��yþ�zo���.�����{���x�>G�p�h����p�����=��{Ehﵞ�} ��'���\?�+G}���;���<|�B��Z;�g���H=7���z�����k=��A�O#и~�V��Y�8w�M�x����=��w��yþ�zo���.�����{���x�>G�p�h����p�����=��{Ehﵞ�} ��'���\?�+G}���;���<|�B��Z;�g���H=7���z�����k=��A�O#и~�V��Y�8w�M�x����=��w��yþ�zo���.�d�Xf��)�L��H���d���wY�J� �1ݫZ��|=6�էrLjnR���,`KX��
���T����g�#}:���]?���3�7��o������C�Ɏ��������Wc/X�ƽW�Zx�j�� �Ϋɸ<��U�YRWQ�IogU��e���c�eU��5�w=E:��H��i&�(�Y%��>1�G��g�݆����j\i�'XY\�p��=�X8ˈ۽?�G��}�m9��BY���^��>�@�IZ/z#�֨Mg���TD֏y���G)qw���9����/��D�<���z㇇s���[���P�0��V�+`a�B�U+�����x��G�6����/�5^�M�u�o�a��V������C	@�T�����Rdd�."�+U곝�N���
F�o�)�0�険��>��dᷙ���o�8��tA"�o��
K�!	TT�&���J�O!2� Ք��&���F}��X����&�#9C�BU�4�d$<D�-+q#S��<�.�1b�WS��M�Ԩ��m�CZ��Fg��8G�-(��v�u~��b�Y^�:���泖9Ȓә���m��^0���F��7&\��]gOj,�,�u���Wb+��Ř�t�mI���˗�T)�u7������mm�vۀߴ��$�0\���U^�e��mY���.3Jn`4�1�<�!��i��OT�+S�^�:��p�w�@2阦m�h�,�Q�9W6�8�	��\sgip������#R�R�ԝ薍֛�]�L���[v��n��S�;����j�P�O�RR�.s'e��(ȣ�I��D�5Y���UaTJT�<�A�[+y椩(���7�;�����&E��)�qS�ҧ�\�7�Z�i#4��IdJ�E�9��q,��h������:��3�_56w�.�B�%eYG|�fDb�U�t�v)~���vzT��%K��%��l���H	VĘ�0u)�"0�E�cF����J���e;_(�@*H]�Q�$QY}1��Q<��ȵn�4��%��_7�2�&�U�V�W7�n��m�6W��(��?�~��COk��n��m�6W��Q؟�=
=�%�j�Lt��W�e�x1�j3�R?��<�����j��g͕�FzQG���COk��m�wm�6W��M۟�=
=�#���ݶ|�^�c�v'�COk��n��mm�q�̯X(��?��z{^L��ʫh���D}��F�����Q؟�z�D����m�S�Ӷ���Z=����<��ד���ʳ�d����қ#�e�������:k�iԘ�2�=��3EQV��^�
刺���k�Ú[���5�
�F�t��{�*;�$����\�Y�s�����:�k��X�f�\�c\����5����7���u��?����~��ƹ�e��k��f5��,o3_��1�Ycy��ph�V���HƮxk��6-�+7S�<��3[���dF[f�k3�Fꈭ������4II�-s)j(mQ{��ʤ�b|�8]'U��z��������^R�u��hk>�CY2_�
��G�I�᰺���sٗ7��^��S���E��;�虫`�0�;�&b���=Ͷ�Z�Eq��#l�z���r�l�7<5����K�ut�'{����(�n#J�O��O���.׶�O���:&���Y&Zޣޥ��Z���K��9����[viբ2�p����'�q��3U>:4y���m�%g�DWr^�L��ɐc�-��>�HZ��Q��
ԨtZ���P��m�׶�2�6b;��&Ux�%Qt�G+ju,Ɏ��3e�fO�����E�LfF:��J�	I�r��N�"��<@3ч(
�N���QWe(�A:|��p/��!֝}�:�
��֒Q��[2�x�0��ԧB��re�Iћb��y_�<?A���A�;�>���B
w�FDW�5!�aLDe��JLcCiN�+�.	�E�����a:���r����5�f�*�L�[a�J��>3�H�e4�l���d�X��=��DU;�����mn��#S�[/�m�5�Y���S�";��q���J����ZǗ���
��~t&$���\u�-H���;���)s�i��I|�D␞�&��E}��
X��X���,��,�W�`"*+	�U�]6:�{�H$�ƛ�F.�;��K�>��j�F/D�G~�Ry����җ��B4�,/g�W�h�G;�5?�)k��d<դo��{�Cai���T�|�p����XE��Uo�ӭڢ���G�����dist/images/wp-2fa-white.svg000064400000007657150755130600011730 0ustar00<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 25.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->

<svg
   version="1.1"
   id="Layer_1"
   x="0px"
   y="0px"
   viewBox="0 0 1330.3 1903.4"
   style="enable-background:new 0 0 1330.3 1903.4;"
   xml:space="preserve"
   sodipodi:docname="wp-2fa-white.svg"
   inkscape:version="1.1 (c4e8f9e, 2021-05-24)"
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
   xmlns="http://www.w3.org/2000/svg"
   xmlns:svg="http://www.w3.org/2000/svg"><defs
   id="defs43" /><sodipodi:namedview
   id="namedview41"
   pagecolor="#ffffff"
   bordercolor="#666666"
   borderopacity="1.0"
   inkscape:pageshadow="2"
   inkscape:pageopacity="0.03921569"
   inkscape:pagecheckerboard="true"
   showgrid="false"
   inkscape:zoom="0.39560785"
   inkscape:cx="749.47956"
   inkscape:cy="1027.5327"
   inkscape:window-width="1792"
   inkscape:window-height="1067"
   inkscape:window-x="0"
   inkscape:window-y="25"
   inkscape:window-maximized="1"
   inkscape:current-layer="g38" />
<style
   type="text/css"
   id="style2">
	.st0{enable-background:new    ;}
	.st1{fill:#FFFFFF;}
</style>

<g
   id="g38">
	<g
   id="g24">
		<path
   class="st1"
   d="M742.2,959.1l-26-50.7l-15.3-30.2c18-10,30.2-29.3,30.2-51.4c0-32.5-26.3-58.8-58.8-58.8    s-58.7,26.3-58.7,58.7c0,10.8,2.9,20.9,7.9,29.6c5,8.7,12.3,16,20.9,21.1L627,907.6l-26,50.8l-23.3,45.7    c-6.4,12.6,2.8,27.6,17,27.6h153.5c14.2,0,23.3-15,16.9-27.6L742.2,959.1z"
   id="path20" />
		<path
   class="st1"
   d="M1017.7,640.9c-223.5-37.9-342.9-176-346.3-181C668,464.8,550.3,602.7,329,640.3l-38.7,6.6l1.5,39.2    c0.2,5.5,5.6,136.4,51.5,280.5c63.1,198,158.7,325.1,303.8,366.1l24.1,7.1l12.4-3.2c342.1-96.7,366.7-628.3,367.6-650.7l1.3-39    L1017.7,640.9z M787.2,1036.5c-8.4,13.7-23,21.9-39,21.9H594.7c-16.1,0-30.7-8.2-39.1-21.9c-8.4-13.7-9.1-30.4-1.8-44.7l23.3-45.7    l26-50.8l5.7-11.1c-4-4.4-7.5-9.2-10.5-14.4c-7.6-13.1-11.6-27.9-11.6-43c0-47.2,38.4-85.6,85.6-85.6s85.6,38.4,85.7,85.4    c0,22.1-8.6,43-23.3,58.7l5.4,10.7l26,50.8l22.9,45C796.3,1006.1,795.6,1022.8,787.2,1036.5z"
   id="path22" />
	</g>
	<g
   id="g28">
		<path
   class="st1"
   d="M1052.5,651.2h-83.9V366.7c0-152.2-130.3-276-290.4-276s-290.4,123.8-290.4,276v163.6h-90.7V366.7    C297.1,164.5,468,0,678.2,0c210.1,0,381.1,164.5,381.1,366.7L1052.5,651.2z"
   id="path26" />
	</g>
	<path
   class="st1"
   d="M671.4,570.7c61.7,49.9,162.9,112.7,297.2,142.5c-9.8,107.4-60.1,466.3-296.7,542.8l-2-0.6l-0.4-0.1l-0.4-0.1   c-55.1-15.6-101.6-47.4-142-97.4c-41.9-51.7-77.9-124.3-107-215.7c-30.1-94.4-41.5-183.6-45.6-228.6   C513.3,683.5,612.2,619.7,671.4,570.7 M671.4,459.9C668,464.8,550.3,602.7,329,640.3l-38.7,6.6l1.5,39.2   c0.2,5.5,5.6,136.4,51.5,280.5c63.1,198,158.7,325.1,303.8,366.1l24.1,7.1l12.4-3.2c342.1-96.7,366.7-628.3,367.6-650.7l1.3-39   l-34.8-5.9C794.2,603,674.8,464.9,671.4,459.9L671.4,459.9z"
   id="path30" />
	<g
   id="g34">
		<path
   class="st1"
   d="M1158.7,523.2c-8.9-1.5-20.6-3.7-29.3-5.4l-3.5,77.9c0.2,0-0.2,3.4,0,3.4c-3,48.9-10.5,214.3-63.3,396    c-73.7,253.7-214.5,404.2-390.3,456.1l-13.2-3.9l-0.4-0.1l-0.4-0.1c-86.9-24.5-159.8-74.4-223-152.4    C372,1216.6,318,1108,274.6,971.8c-53.2-167-66.8-323.2-69.8-369.6c137.7-25.4,269.9-80,383.3-158.5    c33.5-23.2,61.4-45.6,83.7-65.3c22.7,19.9,51.1,42.5,85,65.8c38.5,26.4,79.3,49.9,121.6,70.7v-90.4    c-133.4-73.2-203.9-155-207-159.5c-5,7.1-174.7,206.1-494.2,260.3l-55.8,9.5l2.1,56.5c0.3,7.9,8.1,196.9,74.3,404.8    c91.1,285.8,229.1,469.1,438.5,528.3l34.8,10.2l17.9-4.6c493.7-139.5,516.8-909.7,518-942l1.9-56.3L1158.7,523.2z"
   id="path32" />
	</g>
	<path
   class="st1"
   d="M765.1,1004c6.4,12.6-2.7,27.6-16.9,27.6H594.7c-14.2,0-23.4-15-17-27.6l23.3-45.7l26-50.8l15.4-30.2   c-8.6-5.1-15.9-12.4-20.9-21.1c-5-8.7-7.9-18.8-7.9-29.6c0-32.5,26.3-58.8,58.7-58.8s58.8,26.3,58.8,58.8   c0,22.1-12.2,41.4-30.2,51.4l15.3,30.2l26,50.7L765.1,1004z"
   id="path36" />
</g>
</svg>
dist/images/okta-logo.png000064400000021777150755130600011376 0ustar00�PNG


IHDRe-��U�zTXtRaw profile type exifxڭ�ive����c�@34��<��>dI%��-=;�L2os�n7����/���L#T���"�3/~����M�~�~��>��������
?����}��q����G��x����{��'��`ѝ3��?/�����^h����^��??�����~���E�y���??P;Q�ƍJ�^R�����
����[�^�J�w��w%�����������~���~_Q�)��7F��O$���!�Ӎ��V���	��e;������n�FD�oE��Gt�^�	y����|���k�5⊇��x��뤙2�!�t�J/����k�L�r�'��Qz����<�^�e�[9;�i�%�m-���w���7�Ҝ�X�-��W�?=��|���B�̞�X���"`ʜ��U$$�߼��?�~��TX�*�/̃
��.�-���ʗ��댟?-�B�� D��XL*d �T,��2sO�8�Xy.5o2���e����r�ydݛ���6[nY�M$�J+��̲HV�F��:��eŪ�5�6�M[��ڬ�֛@n��k��z�ϾFu�h��1�X3��l��1�\+�ō�Z�~��λ�m���s�C��z���8�o���m��q�]���^ݼy���ף�^y��k����[��oV���?�Z��Z�2���e�GC�\"	NL9#c�&2ޕ
:+gq�Z�2��ři
�,Ҕ�p�2F
��l/�-w�ܿ��`���[��e.(u�?2Hݿ��/�v�s���O*���}�f�������^vڵ�9+MRO�yx�e<���s|��pxk�b�٠ռB]S�?c}{��x	gKkڱ�kyo�N��XKΎ�h����IM;���zٕ}�G��4���"��
�6FY ��k��u{��I_�
O+sŁ»v��{z//�N�{L�|�r���	,�9m�ʒj�R0Bz�ڬ��2�l��U�[<^�M��ω�N\��e�J��0�s�p��QP����~i���h�}�[���ʺ7��{>r�Xo���a�q���ok�r���wiˈ!�@ӫ�)N"���@��Lo4�"x�|uݸ��]I
T��:��N�w
������}P���Ə����~�NR;+���S������1���z�"��NǏ
q�r�C�|���o?�~ّ�T�����"�o�h%��G2�4ﺍ\���Y~�����w���J�˻P\,x��y�4�o��{��|}���Il��'��s��ۖzy�~n'lo�ģ
�Z�b$s7O����i� �%PC ���ā���\)w�6H�@�G���<�/}�t �ʨ�Y4�2�����#���Ս���#m�6rN��C�M�t,s�%N7�3n'(l�����}u�Nق�p�Ⴑ�WJ�.1D���
�Uٹ%l���,Y�S����\�b}��Nv�%O@�<���^BL[�\�?t��̩�	x����%欐�fv���"o�j��[V�P�`m� �3���|�s^�4x��y{)�Ze�'��&p�|�R�	��Hi�A��_�����(�7F�D��,>?|Y@5H`�=dce�=;���������p<���;�N^��F͂z>�_}���B�ă$7v�}Z���"����T�t(��]�6�A��N��,�O����|9U۞a�]D�Kfp�y2���P��v=��!\��lTΛ�|Zqav�����5&�N���n�/�M�@$-;��ڣO2ho�-�#���0Q�[�!ʡ>���h��c��c��J%����ܐ(1QZ���Ͱ�P�z��x�G�Qw���≭V��%��R�(S�D��
q3F�S8��&�kLU�C�|���/N���F,i�Kw�����!�^�-�^T��k���l�6���a����"w�(�<�-���.ҶU��c��ݡ����z�<dX�pr�"��Wϴwm㌝	)�W��C0�,@")�K�+�������yU���&
�I�mF������'{)rJHw����ct����q.a�=R���<���d/�w���H}%`Y2�I/�2i�����-�����la7���E���ٕ�&D@�	�`O�L��
X0;�e�z	)��#s����ru.:@�hԚ�{i)dɥt�e���X�%�m�{�lCs
� Ĝ�Lg%�@���tr<ә�e�i��qjl�e���	���ܐzam�8��ZAi��A]oiZ*��%Y
��]�*�"�Pj>�r��s��%d��
�Pq���/�I!Mb����d�����4�J]�t���"��Z>�{�:Ws��V�f��l}��	54����Q��B�ʷ��+�
Uq�9��(
 5�DC��,��Fh�xi-�B�x	|�ϐϱ�&%��`�}�k��WS/���3��QiIצ�2,V��ńԸ
q�at�}bh
����kg��hA�gJ��\4�E��}c��~R (���C[����հ0��N9oj
��.[��
�vm��S,@B߇���UZ��_���G�`\�E�R?K̆uCԳ�[��(��a�����*�� l)@t��`	<�"��
ڊ(����j�����ӘE���},x���9��eE��d��1[[sKo?�����AD��RH�����*4ܬj����>?sȊ�l@�)\��ҩQJ��t�&�U��
�k[z�F�e탮~Y�D��Am�'����z	�K�e�݀I��CnzS;8z�EȔ�,=�-=��D���ܛ�LE�G�E,����]ѝ��	ѱ��$$ <�N(��H��sQ�N�I�pƃ�(����K��oZ
��
+�f�T	SJ6v6���]�$q��C���]hL���l�G����S��}��p	��X�K<U�?P��DS�һ�Ps�W
�����FZ�,�
�H�@h��7���)J�Ҧh*Ƥ�1z�ɑ���v����6�RbJYTd+jx�;��+���d���=D=�/T4N�u�ű��p�l�B7 .�(�-�%EI�R>��0����I��o��xr@=6a�P���h@�訹H�͎<���2�Y�F��H�S+�٪/��g2��q$�Gq����?���4=�����	���%��MF�@�1Ѷo�hdDŽ'��J\O�_t�T����Oo��
!��׈�Ơo�>(��‰!�L.�r@=�
�����Hb@�JR� JM�R�-]�R5j�G@,T����ў�-NbF���b�֋@S�rE�5�0�D<c���V�@s���p�9�Jw&2�M"S��aQ
�RJ�X���H�h"��Q�v�yM5>��\�d�kpE.P�*����{�@m�;�,#��#j�t��?�'SʾL��kuӀ� B�B�&7���m�z�
d�I-�i�� �چ���eOh���N�L�m���s�V�^�#�j��6�
�~�(�E������&ͷY�I�M�����	���Ю�2Ɋh*p��v��\D��/i�p�/M=<���G�ơ�tp�ҼKɨ#C�Z�}�à9��!�#[%t�Qh�1Cn�N+�$oD?i�Ag���,�P1���!�Q#�T�������g�A�#�*20��3���	H�Љ��!���f���u��E����l����2��Ǐ�@�-:��bh��{����&�"�>}|&
�#�`:@j���s��Q[k�ԧ�9OG{�OVK�f
������D�U
���Es�:K9(6�&M��1a���Z!)l�Q�
�PI���q
�E��!Fa)4�9��_,�<F�=U��OƬ� AθB��� ��r��3.�c��f��ի�)X$5�hh�����U5�JuRs'v5���ܔ=J�*��dnS��*���Eycm;��7�F�4���ai�4k����r�rˀ�kvP,+�@����EG�<��4���^�]w
4ڡ:ނb�N���
�j���R:��^�doY�����4JA~�Аʵ��a���r��Lp�R�%�bH����WA�&p#�f��V�sqd%�h8��+����t_�`���’ĥP�=!#x�^J�G�7y܌jBB���@��-c+�M�h�p]���O�$l���1�(Y�7�
�$15�K�~�:�5�R�h��������K3p��r��7��l���7�k^r��\�UK߸���L�-��$}�k����!�-�PY���E�jP���LL`-8J����).��Nu#�[�`��
�;�5�A�g
R��R��k��sL�fDڱD��T3�6�5ˆ߂�ܖC��:�b_胎�gwM�E�ٮ���	�w���r�+8�3rO�1���.k B����1;|�a��a;us��Kcy��g3�l���u.�t�AD_��r)�ֻ�CD�Q�Ǿ��H��� �=
:��e̜�]M��I�ب�?�G�}�:���n
���2���N�?Hb>i��Ʉ�N0��G4)P�°��u�H���'i��ղ�u��/�kD�O�7��\V���ո��`E�Xt�$�,����@�<M�`D���g
;nJ�{��Y�3���"$��s�vPL��Ak��D��Z��K��7�Z��5�>߄�BG��6+x�с����C�#�b�վ&���	�dju4U;���#����֑�����4n�t	q���&tI���H�k�8���@�E�=�}��f���Y�A�΍gO�:e�7�ĥ�)ɥƀ�S�QIo
��.��3�y��N1�&p�5��*Qz:�)�xػ����%|ъ:�_^���>+�H�I$s.!) �:�rM@
>
��9=)���QlL� �^]��b�	�D�\���h�U���2�:p�H�J3Nk���N���J)�����%���`���!
�%2[J!U���ٞipD��%
V}c22W�Ϗ�����j):Z$��O;��H[SY~��L��)�x�Xf�s���o�ڱ"{T���(�ql��-�D��w⴯��+i�%��w��8!F�	u��K�k��L���:(�H/�S��x$�Z@C:<׊`?�ynO��V(���9�H�A��XQ��n����@6��x�Շ��
�kr��A��j�H)^t�ڍ�d���a����3�|���X���R�LPZ�Q��J�g���	�6(�1����_�.'�zVe�p&.��(�qx�fi��.l��u�-ļ��`R+8�7 ʥӨ�ύ��K��B��&�<�t�Ch.�����
L+�wo��� N���6;��į�D��	�3r�u�� P�-��<��Tg�>Q���_���Pܛ%���N[��a�>���&�K-M�e�Aa�o�F�Q9�IGQ�0���dXP%�&^�vVu�)�J��z1�=K���c���1d��;1q>&�q"A����7\Y�Z`��%��B�"���T�z�Yd�D2<\�
qo;�p��fR�i!+^�����4-��%��#{�H�RI+B$�t���t ��8�V�P�x��OZX6�?5�]b�"Q��Z|�I9-�����2��l},�\�D��Ӊ)���t�C�����e�/�G��*�d��fШb�۾\��@,h�8�c�n�@�	B�^(��c�Rc��
NQ�D�zGW�(ݸ4�[X���[�s��<�#��P��,:����#�W��D С���N���OXHyA*0��O���S��C�gmL��2ko�<45�ʲ^K=D?��������t�`:�e�N�~N����cc~�z@�Hߡ�> ��$���n����<��`a�-�]�z�]
�4��ۛ?��	ʽ���S��F�d�I�u��Yegt��� u�T���ٚ�#;��
z���D�N�PA�$,�>�D�(9Vz���ew�"��u�9DR.�6�d�]�.Mg?hܛ�i:�\;jb[���v�:�˺�x|�~����f#}��,
j
����0��Z���O�o�~tZ(�Q+p0����	�qo0
AK.
���#�P���Lt�t.������2Z��<n�^U��	
%�XbD�´s �,�l["#3ᰅ�� �ö��~��I�ӭ�]�����h����fI�ㅐ
���'~c�Z��>)ż��\���覻
�?��w��3��5�?��t�#51�����1�
VBM���Շ���ᯞ�l��6���`����iCCPICC profile(�}�=H�@�_SE)U���d�NDE�
E�j�VL.��&
I����Zp�c���⬫�� ~�89:)�H��K
-b=8�ǻ{��w�P-2�j4�6���J���`��E��,cN��h9�����]�g�>���R3|"�,3L�x�xz�68��X^V�ω�L� �#���8�\xf�L&�C�b���&fyS#�"��N�B�c��g�Xf�{�3��2�i!�E,A�eP���:)�m�t��r�ȱ�4Ȯ�~wke''��`hq���c�U��qj'�����R��$����G@�6pq�Д=�rx2dSv%?M!�����@�-X�z����HRW����Q�z�ww6���z?R�r����.�PLTE�������������������������������������������������������������������������敻܄������������������玾އ�⇾��y�ᄷ���������������������������������������~���������~�〼�k��j�����������������J��>����������������k��n��[��j��g��Y��J���������������h��[��b��\��Y��u��u��t��n��h��8��A��?�������t��3��0��3����������y��t��~��?��?�Ͽ��������?��A��H��>��A��?����������������������އ�⎾ލ�݊��y�ᄷ�~��y��t��p��m��h��a��E��\��>��3��8��G��U��K��-��J�� ��+��E����H��A����3������!����:������:��;����.������������$��0��
����5��,������	����2��+��������������(����������$������)������������������ ���
���	��}���{�{�~�~�u�y�y�
t�u�q�o�n�i���0�tRNS@��fbKGD�H	pHYs��~�tIME�
l�@.�IDATXõ��[�H����z"�	�"���*(*⊊��T��( (%%X-�Ђ�j�j�BZ�K�n����9�p�@x��'��L>��w��]����%j=�hq��T,��)>�>�=׾���ƃ��xd.��GwƮ���8D�#������􅔍A/�@�qB<X�'d(WA�x�E�����0!�|��y����	2�����k�`����B�� � �Pi�N萛�آ�p�x�19)(�gCܹ���xD�]N�:+����&�;���kw�3�����g*ә��s6 ��AVƅ�<�h���a�!i:���Z��68�`H_���Z0ҳs���~g��!mOW�ܟ�-Q�u�fVս��҂������*�I[��f�����_�|߽��G�0��P�W �ߑ��H�մ�8“��ϗ-5�F��	�f��Nk�ss�/���ȡ�6�dEvN��́�����”�\]A�X�A�s�M�/�
�?O,PҌ��~jC3H����M/�)k�F��=��}��hX��b�?�Al�>'�<�$0z�PNR~��5�"JR�<E�N�gDQLaJ���H���&BSq�/�@�E4|�JHC�ݳ"��	�>%J�8�����*j�(���b"!ՠMw5
_��{��]
x�r�ՇV����E�I��E�"�G��ͺTB�#56{~�6�1�,��ھ-sZT(Ԁ�\!����
\�!�"<.�A�B�������̀����0H !�/���yq�=1$��@
�qP�
H܏��<]^_�q2,P<�2ؗV ���uK��1%�.�)�Y�- ��-tUI�^���{��B s�����O�P)�8[�f���iL�%�O�R(֏,;Ҥ���׹E4�0��84�����~H����)NJt-��0�[�kI�L�<�(^�����ӛ����a�f+C�[�8G%T���FA�왓��8��RR��B�.���J&�֍���&W����� �&�o�����a �_P�b@�~�8�p���)�����a�$���4�:������os0fȀ?�C$�awȲ��Z� ���_����2�Dؘ	�Wz�����]�ɛ޸my�N���E�_�R��c���}Fτk��K�Dy�
`Ķ����b���̾��PEnoj�)Vb�k��n�����37=��&j�K��c����A�.,3M��Yk;���(Re��5f���h���
��l�怪�6�7�j�RPXp��1bA���l��3�}Q굩2���vË���m2YJ�Ӆ��*J�Ra�٠q��� ���].U�ԚB�ɤ�:Ƹ�X1�BbeR���}��8��"�!�x��K�����#:�K�4�m�Z�E��l7�R�ZF���X���n~�A�mY�_Z�v��xcu9�Z;t��ګg���I.VWW_����:�_GIEND�B`�dist/images/wp-2fa-white-icon20x28.png000064400000001454150755130600013334 0ustar00�PNG


IHDR�U�1�IDAT8Ou�i��s���:H�d�I��dW�J#�K�,�X�N���ɾN"2�A��+�R����-јf0���>z�߻������s�S&�a�a:�…خK��܅ϫ��5�2���a[|���vhеp/���G�'�víXw��Z���4`�a܃�U�{��4C�\�Y��W���VU7�V��;<��>�����WՏ}�̨�9���O�5����͏x�j�a!�Dtys�j�0��6<�U�|�I<�Z��1 "�||�@�4Њ\�	6���ۦx)�Ajt>�븽��(��-�[v�xK<Z�ڝx�c���͈�g�7����	�@�{c��J|�Z
8�`{,PL�A�;a�1�I�xk=�1/@�&���6���=Z���
t:v
P�sP1#=l�8��n�N����S���H܄cqBuT����=�t�5�"2��,
P��v�>���)�n<����.�ѻotG3��EDO�Cu�N���!��V6~�1綞33���BF���[�xː�s6ëM��^3WLEd����fӌ��7���^��|�v��
0���C�����:n��>�y��/������6�<��zi��c�a#ń�N2�-��VU��ؒ������8b����S�桪�u��jc��FKg�G�U.����`�6坪�sg7�F�H/�L��Lp�^1٢K�����>ðz/9ʪ͓fW�]>���j
��IEND�B`�dist/images/duo-logo.png000064400000012206150755130600011212 0ustar00�PNG


IHDR^Y���zTXtRaw profile type exifxڭ�k�#+F���Y @����Y���WwuO݈)�*���DH�J_������O
R]�ZK+��Zj���ݿ������,|}߅� ��,�?���??Nx[#����$�ׅ�����z}����~H��W��U��x]h����~�{Xϓ��}yC���,$1n	���<���篈$�:Jp<%���Hȗ�={�9A_��������O~�#�\�W�x��!�򾼯?/,�ů��o�y����9��]O���WG�d���p� �rO+<���k��ƣ��'%_~��c�"�?.��B'�<�$�wT�c��ޫ���)O�x�U�,��l��(c��K�붻���W���X�?>��>�'wδK&�O���5aX��/GQ�.
c���U~���hU*�o�+�~<�9|��:�e���N�����3���ɡ�1j�R�N�QRT �A�$R��X���9�1��m��Bd)�ԦI�X)e�GS��z��r�%k�.�܋�Tr)E��\WѤY��Vmګ�Ts-Uk������������z���P�Z��;�8d��G:�h�O�g��g�:�l���dA�,]u��wp��i�]����^;r�ɧ=���߫���o�P��Z�����j��T�.�N�Ռ����Zh�h5�5��rV3�"�ȑ ��ƭ`��i��Ox��G�~T7����W圕�Q9G�~��7U[�s�V�A�����5�R�Z�k�崹69��w�k�I�FLG�X�g���{ݵKc�{6���\�mM�J�WH�Ȍ�*eY������=��s�r�[ʠ���H�+������K򣇼�>նN�MvZ�L���F��JH�0h��K�R\��)�^��uΛˑ��%i0}�D!kTV�'P�AV<3ɭׇE\)	-k5Lʨ~n�}��g��3�v-��뢪c�)'���zQk���C�q� B�%j���zB���������̺>�8m����Vi��|�?��%�]�^�3�k��N�'�J�֔��^�*�z{�#�V8�L.�Kd��QK9%�R�5=���i[D2%�V��g�Qm���%N�M��ɲͺi����I���e��^��:�Xƻ�@��ڃN@	ל���Ů�=�Xe�2�(ˀ�B�
6d�����(!̻P1�vY��8S�������3�(Πfr����>�&�Ӽv_zů�".�o��Q�V�~2i6M��ul��W?��n��L���;�3{�,���e0�9�~�ߛ�R����e4�)�3�L��,�h��Q�T��9=I؈ICe��J���(�L�R�>!��3_����=��z�V��5�~_����J���!ELV�T��L��1��1�+��a���:y��!�[�\�����@��ױ$R>�Ȇ�!���Gc�y1�j�4j�$g��9���h:kz-�z��1�
�[���Zn�k�&;c�N��^ɝi�:=��2K��u��k�@:S�8�y��!*�.���L��_@$-�E:�O�@�~J�vҋs��M�C�/��h;�-�x��g����*���a&�l��2��4<B����nA%�K�ڣ��9�,�4�Ff�����,����PMI7R2���`d����lE�qƩu��F�c�56j�G����c��:�ȃOE#��i����;^�B�5|6
���p.B�̤�a��+=D�`�S��8m�q�ˇ��=s&4��A�"��KO�y:��)AA!e{N�j<�Y�k=dK?��p�{	v�n|��O4k�JiN�P��.y�<'����hp+f��ڜ=����I�@i����m�	�)�M7�p�q�Y��I\O�g�?@�gF=$*�>W��p
,@iY���/|V�4���}枴S!8�d��k0ThZ�[���(HG?+@�m�Sa��"�q԰U<m�L�=`T���h�!�v��y.}o���MPk��ˆ���zF�qX��Xy�^u�{�T��ΞJ�=ض����5ܶ�:�S	�������b�]����t/�8Ѣ�^����0Б��������\�rH�mZ�Cw�
q�gE[0W��m��5�G�̼��l; ���B���+e*�#K�c����sr�����aX%������"���/��SN^)y���� X��~,�We�S�H�p���a���$���`	�W���e�;`^h����
���%�J���C���H�����&�,o��jC�	�$��c��(�&�A5�9|���yXl|m���"�I���%�8 ���r��E��"�D�B�8Cqȸ1�6�a[�&�׹��D�$�.��������D����/ v�<�c��LE��]�BDZ9-TR"6y��y���'<��ۙ�����9ou����U��=���@���'��	�O�<���'xt?�7xl�+ĥ9�-\�1���wȋ��jcV|�ԔW�0L)ӗՙ�g�A$��zO<�M��~�X�������>2a(�����\&�O���4�\��0!b��oV�"5�Dw�YǁT�ܼ":��H*����E�G���3"���^�ȑe3#�
"9Zxq�1��(�x2݇Z�'���:�9��s�y(Y�	�NL���F�'�΅0
81�{
�u�?3Հ�3</E��M֑���ߛ3��Oa_��5��}ޜ�[�k�j}>9�6��=�sZ��9=x	6G=�,�\�IBOw�:2'�$�1�`c��
�8e@���6�OP�w6���oC1�Ρ��؎o����/�}8e�j�m/�{L��0xPQ�"6O0��hXY�4�S!���1�.�6v:��eI��ꄅN`����a�T��{,</\����s�7�$�b�-�[�-,wJ�g6d��<Ccģymд�vpU�Y}����73��]�IB�f����u�ܤ���e��n�7
�T���fF��֘bb�k�1�xoJ�y�1H�]n�-Q��t�|6���6N5^���2S�[�c�|B���2W�Ɯ��Cx@���D�LI�np3�A�#�m����!�m6<�f{8�B���e����!Ʋ��$�Y+a7Y1�7Azm$Ɲ�,�Zq�r�!}�wg���Ӫ7�ecv+��t1�L3�X�������5]���/l�
:DB%�� �d��3����	���GC.N��2<��z�"�Ю�(훑&��]/��4�s�0�m��b��� ��i'M�!|��Bܘ,��l{�C�{��^q>,�n�w._��02�ґe�qk��3d&�!�k��*HD�[)�2�W�3�򦍧��O+8#m�bE����!�nd����
��<ņ���Fb,�?mٗf�m�u�*�Cz$r���� �}��
�ż
��t��7n��;��ˆ��J|0,�i��9q���NG�('�#�j������~i`��6� 9�5�
�#L���.m�@g�6��&��ׅ��ux:���m�Z���q�^-M� �3����f�t�`�$��]8P��8/�kOلmxxPYl���F��=�}�^*ɉ�	�W��`�3�}u!�:jRM����*���T)h�̗D�n�g'	[i�nx�*�3� ���|��;�����[���0��u�)�zS]����NX�9A��E۽@��/��t�z��Fь�&v'
<�-��p8���
�>��+�{�=�k������1Z���Y��؝�j�-�jM�y^�ܯB�\C(7@�4'F	tum�Bx�_<�{L��0�t4?��c�/��Rd�Ϗl���+���/4[�`iw�݈�HCh��ٌ<�)��l7@��#�RC4)��h�͖��xBR<�%�7��?w'a��@�d�7<R3b�j���”$�<a�� ތO���+kþ�٥�F`��^��Ee��X�v�vN��
ȧIz����������iCCPICC profile(�}�=H�@�_SE)U���d�NDE�
E�j�VL.��&
I����Zp�c���⬫�� ~�89:)�H��K
-b=8�ǻ{��w�P-2�j4�6���J���`��E��,cN��h9�����]�g�>���R3|"�,3L�x�xz�68��X^V�ω�L� �#���8�\xf�L&�C�b���&fyS#�"��N�B�c��g�Xf�{�3��2�i!�E,A�eP���:)�m�t��r�ȱ�4Ȯ�~wke''��`hq���c�U��qj'�����R��$����G@�6pq�Д=�rx2dSv%?M!�����@�-X�z����HRW����Q�z�ww6���z?R�r����.�PLTE|�b��~��j�ڹ�զm�Pw�]�׬�͈�Ж�Ԣ��r�W�ɀ��~�В�ܾv�]�˄~�f�ʁ��t~�f��{��jo�S{�br�W�ʄ��}~�g��|�΍}�e~�f��x�̅��i�ۻ��n�Ό|�aq�W�ɀy�_�ˆ�ʂw�^�ʂ��s�̈�Ԟ��ok�O��l|�c��y�Г�͐���ۼ��|m�P��zl�Nj�L�ʃ�Ɂ~�g�˃v�[�͋p�U����z��j}�fh�J��n���)tRNS@��fbKGD�H	pHYs��tIME�
1ׂ�IDATHǕ�Is�0�5�\ ���fO�.X8	-��Mqi�������a˲�:h��Ǥ������M���r~֗>����T�/Z��x�R�]%��D��4��+O��j ��9.�w�R�n��Cx��E��g��A�sҸ=ޑ I⅐�M����xm���~���L��x!�6��x|�h���Ƿ�U�l�7�?YZ�s;����8Žϐ?��*��x��;{��{�%�Z�4�֓G�&v�#��(����h�#7w��@b�����%�,������Z�M��na���`<a{
oF�Ҙ��:|��h�����t�L����	�o
x�yT>���^�_T6���]H8|#݄�a�_Μ�`�Ɍg��t9��4�����1�9vGM�ϙ�%�w��Nj�}uǺNih�Q7����x��%��ߕu�
�jIEND�B`�dist/images/2fa-apps.jpg000064400000075246150755130600011107 0ustar00���JFIFHH��C		



          ��C

                                                 ��, ����d	
!1"AQ2Uafq���#36BRSru��������$7CVbst�%4T����������Dce����&d��������E
!1AQRa�"23bq�����Scr���#$4B��s��CT£��?��<ڌ-�&Hn:9�Q'�����|"�ئ��mj/���֓0���%m�����#7N�Qj_�f���Ԩ�7x�u�[S����2֣�)�
�i=���.#E��(��Oqv��"D�.��]S�+{��_	��:YS�'����I��|�1�6�ȏ��ҢRO*�r�a��oÚJ�ST�ju[�7�$�5^�����K����D�.��/����E�e�,I���r�M��SXU�Q~9e���R{��{Q�mĹ�հ�vo �
�
z	R�ʣ�-�Wy  �����uܟ��x��?=��O�,�$`&)���PQ6�ͧ��.�3����eN�eɒ��#�3�I)��%�Z��I7U%�7.�G�SW��w��ۀ}QM�é@�>��IM��N�%Er��9�̱��9�̱��9�̱��9�̱��9�̱��9�̱��9�̱��9�̱��9�̱��9�̱��9�̱��9�̱��9�̱��9�̱����*�X}dʲʘz�O��ˎ�y )��#+���E,�iNܣ��:��@4G�Ω��n:��Z����o}�@tQ��:l�W
x�&K+w���}��/o3�-[��²W~\2�~F�vy�Y���x��zZij(��|W��	���4�:�R�l��)]�jU�OG���Y���g�̺��BuIw;�� #�W���{����w�@;���
}���.�~y��_���{����w�@;���
}���.�~y��_���{����w�@;���
}���.�~y��_���{����w�@;���
}���.�~y��_���{����w�@;���
}���.�~y��_���{����w�@;���
}���.�~y��_���{����w�@;���
}���.�~y��_���{����w�@;���
}���.�~y��_���{����w�@;���
}���.�~y��_���{����w�@;���
}��F�4�Ӎi�gB��2��xF��V�e˫o�7�Mc�NYd��M	�m�z���[���e�-�@0 
�K3T�n,B>$v3�~s��@4p�~aˬ�]�����#g��2C�e�8�M��` �Iu�58��܄��Vd9���LA��
�c-b:�Fts����@6�ՕM�%
BU�vGs��#o������g�%V?�}U����}@�GG������wQ�T
�j4�Xf��Xӄ�R�mkN�[7���Mt��%�iJ,]������jdw">q��uN�X�j:'
�|)&�rx���[��mU���[����Yo�9�ts\�
�r��vQ
HC�!J+�dfYr�\�%�����������_9��OM�9�N��9kN�Kn)+>�t%>,�k�Q��.g)֏g�]Ȏ�iG�ڙ�ʍ4�����22�!Naߦ��3��/0l�<��&���Ӳ.�<�c�O�<~8�K�a�x@pfI#3;o0e��tӣ�T�Gv��A�e
x���x����S��8^�q��֊5c��oIa�$�J�1J�!r�op��}�ٽ�-bm׺U1����Q:��?F��W��T���)�K*[�6�##<�$��X��1w��9�f��扷��pw*Ո��o�F�M��Jy�"�W`������A�9C����i�s_J���=m�ZpR�(�IQ�b��������(��;�x@q�%��<��2=�8%$�c����%u��������[�pfDW3���2�4���G5���c5�m�k�B7[����@*W��f뵋��}`�P�l�~�%�;x�2��85����2v�C]�[������D/�0�D(%�D"�; R�
��:��"'��O�Ls��k�S#p�3l���ɵ;3[x��|���Uc�7�Z\_�����t{�[��?���#�,a<�_���ӿ�Y���GX���=t��S��8O �ɬUT��t4���r������~Cڪ�G�˗�����૊(�o��N˜`j�w�_�Ti���s/V�a����d�n2��X�TWD�Le0�[��p��m]�.S_��q������
6Mu��P�&T�i�u9ȏ*�DD[.��j�(����f�n'��ժ��QϾrk3��]r��T)�}[�Qe
γwf�-�k�w���	�S<���1�۪&�*��/W.kMg�o�E4�	�B�̺����1�����@q�����kg�؄�ַ�\k�ͮ�L�͛�.�m���"�y.i�͎l��BE�ǥ����X��k�ڞ��f���w-�Xm}x޻�8��=L�ٖY�ɖI�uP��X]v��Q1%����$�>rJ��2r�	oUo��h�OGZ�U����(�;�����P�cL+�qE^Y0_5X�96���v25����kv�o^��0X�E�/~b��Lv�j�CF�"�L^%�6�6���}�YJ��̓e��븻L�mU�v�5���6q��eʸ��i�����q>;�R0�ɅJ���T���T���	��)"+���W\E;�*�7�j�^ʪ�v_�m��Bt*��~}"���d98����_2H�Nbۼ�a-qf�՘�=ZF�v���y?�|�d���^8Ĥ�έA8wY7~&cٷ.�J^����Q��fD�/�bF���Е��MDDc3L��_��������I�V��O���`�<l6�2�e#�E3;���ʪ)ϞX�B�Z[m&�yP�����am3��ɓP����CyE�-��6�O9��C5S1���\�阪:��j|�u�g6�2Dɨ~AiA�RQ�r	��88��R�r��ܧ?)�cg�<zt��k�Nț������	}:�
3�
�g��F��'�OT
��d��l��;\˗`�i,D��/q:"���\۔�OW<�0�ΎTƤbg�v{�%.+
&��I��jQr�
���r��}���&�<DQ��e��A���~�aW��1��g��$���ԛ�<���A���5��U\&"&��<��?�&ע�+@ŰzV�$��N6m%N�j�"�H�v�ܜ��Y�c"�jW���E��x*��>'=���hzB�oF��q}���������N��l#=���O���Z��zM�l�[3���e�:]q���^�9ƌ�0�P�M�#6f�[��7۴ddd:�k�%�q�4T�qo};鞦�,9,m�#a|1:����E��~��lm���D�ڹ�0�,����T�l�q��gcqNy۪��̢�"�m-a�հ��O<�4-J�Y���)�̬�F]��\�6�aq�YS�u��zKqJ#�s%(�n����Xm,B������U��ʻ�����l�Ěv��r؝��g62V�Fqq6 �T�i�ۢ��eƜ�,��^k���ͅۀ4ė4�S�[�qG)y��+YV"�(ճjOh]�U���)jQpۙ��������e�ly5�*�3+��U�\��܏�̶-k��-F��R��~v\�1"F�Hf�F�J}ki�������ZIek��Q��GT�8���u���˜��iU�(I��^�s��6�t�^��L��-
�~rzԒ��2�-�*��p�V�&��2��–�a��[��f�Ӕ�F{�`ՖX2�4�	M֢L"�Hc!��4��`4[��q3�X|��o����+����}��қV���>pq��n�Nc.p�z�pm��K�Dt��[JJ�;n>L4�$�JŰ�۴MH:��hl%<F_�Nv��������g�%V?�}U����}@�GG�����=����E5�#�ꖔ�goF��}�RLY��q���LM��ɏ�J�e�$�S�J�v����S�9����_��f�T�#�T�T�ʮLQFA4o<��D��\cթ9-����s��tFל�4b1�Q~��ӻnQ�۽�@��*ƍ�\�P��y�&.x���c3;���Q]u[�Y'����g8�3۟+H�՝.azdfZ��Q���8�G�S���,�B���{�)�������gc��fk�nG,N[�{�Z`�tX�:."f�T:�EiL�YX����3JlD�Ẍ��b��QVYL��x��\����QN��:WNxr�U�PߥӤNe1Vڗ�p�F��彮C|e��c(�[��U�t��η+����b�-��l�IE�;l,�e� �NT��tt�w��$���3N�C�
HK�)i�F����MjY� �mɇN��0�պg�-��2��p=V��U$��T�T�T�'
�2�n)G���j��tF�oq,QF/\_�V(�M9���=N�Z�"��6�;2l�R��6T��'��y�hU]UY��{6p��i*)�9��ϒZ��G�f�YÏ֩yR��I����B]I-'b�D�;h�v��á��3r�ȷ_<O�;�#�+P�`خīT䂦G2���$�ւ���FF[�g*�S|��ZC��\b&&�r�i��5��T�ø�	�Ɣ��NF[
g)_2�Eb��[D�몹ծ�Lf��
{)��<��Z���xoˉ︔ɈShp�)4��Y�ۭ1J�1E~�zL%ڱh�vU]?L�m�I�]Ø�	�&��!m��}�cV��c��Q��9�F*���=�.����1�g��㢍*a�/��R�<�uː��#>�9[J�+X���lM4S���:�&�WD���5:&2�H�5=�E�y�CE�З�Ӹ�H�~����\�u1��pV�X��I�/���SN�Ie�\5-�%<��u��$�۔ĸ��^Y)h=s
�5�xܐ���p���#��S땬?��

�����o���;"\��Q�x�q��ì�0��M�v����W�5�YE��2���I��pq�-�O��'1{FM��h����uL*n}.M�YMx���x�ٚ�"�(�\�j�����ͻ8��QMYU���t_���J��h��3��S���N^�������f�f1:�ߺ�(��	�Ln��r�v\A�J=GǮR�t29���Em#RO6fL��f�r4}5W��vWma���xI�(�����4�toZ�fY\d�h��騵v���'��pS����Տ���?�+I�1�AP�K?���#ۓ|o�T�^L�ރ����>����GU�?颿3c�^�
��*!���IJS�����GbB-��chb^X���1vj����)+�%	qZ����.�����,��N��'!y������%�s���߸IJ�F%H��憬��������3q-6�9����܌r�y�bB��n��̤���;%)-�f5eOh�:��k� ��e�Q��-�%¸�X��5dMhK��<�o��ᴱ�j��<%�*8�c�Wd��,�
j�=���e#��نV��cZ�`��9X�uU����k��$�w���$\:;�P��d5�[
�N�j�?�[Uک�2�J6*ƴ��g��{���3�4���������3d/�>1�PG�S�na$,Q�@ΐ�K�a�5)�.��.S�\d�����:�GMJ!#�}h���h�ho��w�Ąo#�A�w9Ɠ��ȿ⫔�A>ӌ�h^�C�.p[L�[L�	Z|k����nO1}�3_~f�"�b��RQn\�bB��˴��$�����g�%V?�}U����}@�GG����*��1S0�7!%���B�,��+�"RL�J!�����b�Y�N��\��M�_d����'����h�G�QTlC��*�U7�����e��qgu/"�4�����;�Y�����_Rrٿ�b�*�"�$���4��qT�V��Jն�2>a��ݫ~m,`�V�Z�L����.��e��ltNJ�KR�ʄ;Oe"�Dn6��w�i�v9ػ��ܜ�(��e�=mx޻+�U٨Mx�*
��R|�I �����z�淇�=����Lz�5in��vyGi$��q
�%%�����[;�H�v#-�w4~���S9�=gH4x��#JCUk���ֳY����h�rQ��U�s˗�5�[�L�4�F��*p��L���n�"Ԟi]��T��e��J4��SQ>�؊y���f�����v������`�խ^��>�_2���]�qj\9qTdja��i��� ��dG�»�jߛ8l
��F��?�2&����e�q$V%=X��)���6�����
z/T��*��]SH˭tqGS�D�ԻrG�"ɔ�����Z�y��n,�8>����?�e�"���œ���Xֈ9�y5r���~�_b�:/G���o�O]-jZ��V���kS���33N�1_R�iub���=���:�c&|��5'�Y��T���:�c&|��5'�N1o�Ol�v2g���CRy���T���:�c&|��5'�N1o�Ol=�3k��£OelC���m֔ҳ=ם�Dj!��r�ZZ�o^���uU�N�7�3ǧI����X/��wO�<~8�K�a�x@j:H��;Q�3��T#�p&Z�{Үt*�ElN.ӗ+����x�Ѷ���
Cc�E�B���t��|��m����#�b�}�ɱ~�=Z�n}Kh�^�_�1�e�y���ŏ���q�=�E��U]m.���-�+�1�*ە�j�+	cZ�����\�͋��q9u�?뫱Z�t���Q��5���5�y�$�t�m2�(�^���v�n'@h�#<-m��D��\s�乥=+Te�TPU�d0ھ���\v��O
���ѵ�t51><��\�u~���88Wƣ�<��fy��n:��Y��;C�b�[�V-ҺJ�e��W.��HN��nؕ�i�>Ѵ\ �NR�.�Q�<n;1ԒU��F�z���LfdncL�:5��q�_A��ū'�K$6�����_o�J�.c9��`j��F�w�m5$�����4�lN��zN�%&��3����yu�m��,�2�ٖX�=���q%�ќ��iP(�)�])��
�⠶���R���F{�Ƭ�@i�+G���AU)�+�o�Ѡ��,�]��y��߰g1��
Gh��5]��Q5rN���T�uE�J�r3͵{��3��N,��g$�p�����M����n[9� H�)(�� R�Ẉ,7�V�V�9ne۰�����0�#��d�o&�$J4�(�Gc����S����
+o*B �M�"I���Żi��@�0c��+�AE鯫3�O�\=��Q�X�RL��7�Ӵ���@"&��No$���.�[���i��3��Z�^�Ē�%�3�?fB2�3ک
�#���&��L�0�vp��w�O:O�h�	���^�`F�pGW��Ҧ),(�R�[��cA��Y����0�&bFBZa��JV"���3��ɾ����侏����R���~��߯�[�y3�p;��~��q.����z[4�$uEf�-Y���),L�)[)ܹD�1����S�h�na湙�'Zi�x�zobY�7(I���U:�dk�ͻ{�le��$�riˮU4~��&�~%Tz�g��I�qHo�Ƨ55�X㦃#;��.`���j�dW��0��ۭ5�unk�q�ьQ���6��˜��ܜ��]ՒUǾ�k����~�6p�7�2���N��IlU�Ӈ�]��ŤOfJ��z�hSI-Y%W�]Gm�$�{V���*�=�خ�s���#�{�cUbZ� m���6�!���X����w2>1rٽ�3�
t���o<⺢s�������ʻMF����:)Ok�r�C�vYD��I\�XAoUS��ۻ���h�vh�V�YS��e��I|k��8c�)�GB�#�2�n�F��$�`��#R��E\��F����G$��/ZK
bz�jn*����Repjc�VU�%�N�;�c�6�7�rj����xJ-Sj��=9��ȉØ�H�U>�)t�j��r�����C���7Y�ȋ���rj�ٱk��Qb���gr'-ܜ��ؖ{�D����zsSP��u�w!���kv��rxM^L���SH��Zk��܈w�Z�"�S��P�CZcɓQ֨ސe�Ђj�R�q	]ULS��u����ڢ���Uͱ�=��_��øRUUIC�Zm(e��%�$!%�cZ�-ۚ�棁���G���LL�j��;%r�f6 ���3��&KG��sVS#I�ٹ5S�B]!�����9�TD�>R���$Mųhr�I�H8˨-��v�k3u��D^�GEw&��l\��p�X��p�>ym�Nv]gb鸶^��CC���ȟ.~�I5=����̷��3Wr��V��"���E��{[Ǚ��r��;^�|y*���،�!��=�I~1������G��&eq�ogo[��ѱoM�󢩧)��f"�W(����Qf����t�s�B���	F���)��V�L�l���q�&�몝zf��첫o"7�#�ƃ�w�����=�yW=P׼��&���Ӳ"�y~������	}:�
o�b��Mb9����2<��r�7�>a�=<�WFi�F
g����'l')�"S�d�X�KL4[������C�z�W+���s���B�-$��\e�LÒ""�� ��:��n!M��[k+)
+���# �)�[��ʠj�,�o�,��
�\YP�6&2����I��;�qm!���s6G� �'�:�*:l�YGk\��"*4�*�pE���	��8�~i����}�U9D� _B�iWZ����Ki4Gέ��BP�4�1�e*2b�`�����J=�Q���	U���Uhqq~r_G�?���)X�g3.�j�}�4�˗k(�L���|���,�UR=vEB��F*�}�����N3<��y��i�g�s�P���ۊi�J#-�gה�f��>�W0�29�C�[��37&v�Kc5 ̏*�bk���c�0��,ݪus�\Le˔���ѱ\�I�&đ&L6�!�M�	$����9�ыv��֫-���5X�6����=��{�sF��
;�8�<�;��}ְ߃�S���͜��z�2u�a�����q)mIm�#1�o��&\�S.Q��g\O38|o�������0�r�[�3����
<�
�.�-���ɴ��ݭY��1��ݺv�DLz��0��8}�ǧ�i�RS-r�DQ�պ�u�XK"�c2#Qf�wLe��'Ia��uS^��[*ٲ9�M��Rj���ĥ��7��S$�RN�'����a%Vs�>L�S����pq�k�Q>�6�0�=��
��� ���#%��'"R�3;��1����J�(�M3���}l��M�X����q���6�I)�Cf��{���o*�y�b1�rŻym���$|/!�}3�8�i��L{rRϚ��o���*�D�b�[b��?bN	���R��*�bG�-/M�5�=��,��F�'1���qT�3�r�!b�T�~��m쉉�g4��~����`˜H��Ȫ�D��}m��������ݵ5�08�p�]T��LeOW�������Y���&�������!2rR�ꯘ�F��L�I��1��DLe]��z��|1"1�W���]R�$FJGA������ʹ���\Wbݬ�ѭ���7���XZ��)U�PbcF�j�z��dd�-�=�:�U��L�Y����1j�3TS9�S���ȥ�.���e�Z�SY�O�q6%?(�2�%�s�6��Tj����LT^��)���(�7r�)6�G�CoQSu����!dym�1f,�X��1�W�u��5g4ܜ����mS�"��*
o��>v�D��Ct:}�yW=P���&���Ӳ"�y~������	}:�2��R�nC~�Ē����׏7Cɳ?c[��%Vѭ���	���t��7O��)ɴl��#��x�/i��X�M�V��%�}��򖘇E�kR/�*�?�Vu�3v�R��ߌ_�3���
���b���Ԏh8T�gw���f�sA¥{;���35#�+�������p�^���/��H情J�w~1hfjG4*W���C3R9��R��ߌ_����
���b���Ԏh8T�gw���f�sA¥{;���35#�+�������p�^���/��H情J�w~1hfjG4*W���C3R9��R��ߌ_����
���b���Ԏhu[�,��Գ�5���3����&���Ӳ.�<�c�wO�<~8�K�a�x@\�+�'I\���R��mc�GiŪŔ��$��Ea�ۦw�,��^�2���:�^#�S~HLj1������J�4�#�S~HLj
��w����i:G�}�����89���#�+��t���M�# p4sGa�G�W������F<@�h�þ����I�>�7�x�����|1i_擤|�
o�����;�b>ҿ�'H�/�ߒ1�G4v��}��N��_`)�$c��h�;�J�4�#�S~HLj
��w����i:G�}�����89���#�+��t���M�# p4sGa�G�W������F<@�h�þ����I�>�7�x�����|1i_擤|�
o�����;�b>ҿ�'H�/�ߒ1�G4v��}��N��_`)�$c��h�;�J�4�#�S~HLj
��w����i:G�}�����89���#�+��t���M�# p4sGa�G�W���NØz��N�D����N�a��i3#�f�#�ȶ�n����܌�����fR#t<Q���e�+SSO���D�p�~F�JQۼ��x[�E��ޏ��6�/����*�l���!��u
�Β�(�R������׶�@"���"��e��M��ʾ��5���{^�%�GN��Uju&K���]oo*�>�9��X�\�:��EJ��Kp݌N���IR;*4,��� �]!�zcu��}��%��I(iũ
��V".5��cmiq����dJO&��z�M��dU�n0b��T�fQ$�$����g!iZ���+���d���F�&JĊm�ͧ0�92N�Rh3׶����������q4�KY�����פʨS9'�����ㄒ%)J�7�B�J6���U.��*�#5>�����.ϳ�M��˪Nj��1�W1T�-f�)��dj�M&�/S��q���b]~��!cČ=E	��%��	H��Y���D�Y@DC���#���x�c��D�!f�-�OE}�{�l�>��js��ިp*�)�G�뵔���U�ORr�*-�j�^�ȏ��Z6�}��~��̔�)-�t�e��a��1�5V$3�(�KUT�Rj��އJy:�A*��7k���%�!i�ʧ��@bM4�{#*>>e�FdY�u�@j�:{��)�Q�4�K�%N��^o8�͝�o�_�N��ؓ���Y�1��b0�T<i�M97V�M��=��ϼ+D]2F�'-�1]����kZ^nsOZ�Z��6	.E�����U�:X*T&ep26T�,��֙��%�Ԫ�����K
ψ�W�4��7mZ%H�G}���5*�IX_Hl7]�W���SΟ!�
p�]a���V��۶dFd��X��bI��=��Ap������!��cuM��#)5*�'V�Ԫ�J$���ܙ��rlX�Ը�����_)I%$��Eb�*��X��j���~��%¿9f��z���`�=�m���������\)�bT�U�J��I�瘆P�N���W��P¸f�=���TYS���K̡n�q���9�z���Te�4���φ��>G	.d,�F#	�D��)�����:��iG,�8�e�o!�m�$�a->����DI3�l��esf�1����
$ȮD����r:ÄJB�{��{�t]6��N\f��ԜCBuZ�eɓ��m��?�j�1Y�I�%�DI��YBɤ��尶n��U1r"�\F��2��9�%Ug�n[6��s���67��.��nl�̶�l�o����)��:;�P4�� ���Xn\��y.έ�0�am.�J�9lleO��
%�Fe��֡��Uzi����E�Eu����ݕ&V+v�y#
a��Ȉ�&""�$&K	e�C�ס��k^K��V�8v���U�Ɯ�=�id�������z#�
�t4�sV���S&��R2ٰ`
�U�lbM3-�N�*⒒�i=�c��lQ6�f"g���tX�E�mt�r�i�]�T�y0���g�|��_�-�k}�q;�[����䫕���>5h��ю�;�[�����U������c����a�\_����=����F8���v�����W�w*�_�t��_�1���c��/�n~j�۱Vj��_���^�F;��������[�k���c����a�\W�����+�;e\�����_��g
G4v%�LbwM�4��c��m��ϭ?�]�h�GF;�~/}7�L~*��Ϣ�_�b�ѷGF;����?5_�Mi��eb��mz��yN&<��%8�#2B��C���)�2���bn]�\�T�1To��'�X'IZJQ�8��g�����z��t�ߢb�—�
�+��<ץ
$(�5U��)�|p~�H���HJ��ʺ���>�H���HJ��ʺ���>�H���HJ��ʺ���>�H���HJ��ʺ���>�H���HJ��ʺ���\e�a�kX�|Z�n}J2)N��%�y��Dr%\R��Q��`>�^iK]'֙�t+�k�&N�_��e�r��=���x���m�v7Hin/^��{3ߗɫ�H�;�� /�9�U�{�uH�;�� z~������T�s�<����O�{�uH�;�� z~������T�s�<����O�{�uH�;�� z~������T�s�<����O�{�uH�;�� z~������T�s�<����O�{�uH�;�� z~������T�s�<����O�{�uH�;�� z~������T�s�<����O�{�uH�;�� z~������T�s�<����O�{�uH�;�� z~������T�s�<����O�{�uH�;�� z~������T�s�<����O�{�uH�;�� z~������T�s�<����O�{�uH�;�� z~������T�s�<����O�{�uH�;�� z~������T�s�<����O�{�uH�;�� z~�������y��Ux�_��g���H�;�Ƿ�7t����ġw''bP�Ld�J�C2%:|���2jorTfI%�Fkf>�k�M3���G޽N����F{�'�RF��b������n�U3��v%0�0�L;��I܎�1�19=u�sbˏȤ�2�<U�<(�4SIMr��VŠ����}#>4z�B�>�[W?|%Q��V�"|?罃���E�$T��{�Q�}p�47�H^���a���������ʯ������
5X��W��18��)pxz���M���~1Cj�JX��瓦�I�I?��W:Rp����F$줟�Pq��)8z��ݬ[��Y-57�ˑJ�_�C1��Җcs�S��(Vزf���r����./�Z���+j��~�B�Tz�~�rϑ]q��^�)w�V�&������^丰���u�
A֖m��w��a��N�F"�9Q>�w�*'ԧ:hĝ���s�\�K����'M���~1AƮt���瓦�I�I?��W:Rp���_	b
�G�����e������!g	��nDL�|=���e)��Uvk��fc���"J�"-��-b.����UoE�]�b�yE���'L��?�5|�p�s��w��a�
_<�-\�pԇ�P�����T�2ռ��mk��Y�鄘�"&��)T^[�x����8~�!v�A{E��nߦ����
�L���E�/���M�'\�;���N�7�v'yy�R|��
�_lB����J��.s���S�Ӕ�osK�N_�>7�mF:�r��r9[UJl�Ij�Ʀ��
\�d��:N'ˌ���M�<��YK��K��jA܏�!զ������A�@.9U�\�_B��tw����ʻ����0�%��Nı�2eC�*c����ɵ�u�As�g�%�ܮ�7����zr�3�4z�t2��\=�j2K��*O�[9W_���g�y�W���z��fȖ�K��bo�%̔�ėxIE��2�<E��U�rs���7G����<��QG
Bv!�吂��=Qv�"�9m�gó�-F7^5o�	��=Ur����p
M*D���IY�H�|�CwnUx��O�Z׀Ν{3�Q�T�*~q�1	d{��rPz�De�J$���
e%��;!�RI%f�/�r�u9��y;>*C�w���f�Ҡqt���w!�k���U�9닀\���.p�\�w��	/q��`\����
�����;��w?�'�|����~r��u�s�B��h?Uc&K��1��d"Q�Zd|�/�wk�r��gS�cB]�3��`��7si���옦j�\�A�(���I��n�^L���h���=���U�IP0�cO8�8D�A�m�J8���fE�BF&�4�\�	�j��T��;�y�̩M���J�v2>񐚚���7J*�g)��?��x�j�R#�A���J�Gk��d[�C���f3�r��|-w�*#7��b��:�E�#Nr��S���r��
)��78=��z�uŽy�,�D�J�lXwʬS�U��I��2��TiB�V��Es3��q8�TQ5W<������u�b�c�Po�[KW�l���H�k4��r���T���O�0ڣ�O��>z_^�����%��F���>���͝��N�G�ST��ى�m�^U��U�Q`����}y�!�0x�2�*�6#^2��\O�r�����1c��J[�D��X�ozi�����[�y�X��K��LS=�~�q>\�_��+�Z�O��i����ȇVǑi�QD��`�U'��S��^�_�!�b�(��J,N#R:��2%H\�.��w[��f<�UMS��q��fs��Հ��3hR�H3r���r�'�B�6��=��nz�,)���jTe��:Y������7;����l��ǫ�.��+�P=f���~2�otq��~��+$��%�P�]Y�
���>��e��qh�US�1��K�@����O%>2�g�+�}�nb��U�#�~Q�׸��>zu��S?�W'�3�����-�����"2YZ/{�m[F�X�g=�s�{���F��h�퍑����䩓�:1���:1�=��N%֖m���i;(����S��MkS9U��*�)�j�5O�s���Z�J�� ણ�������=�t��t��)yJ�":I�+ѕ��<͟j��}�
��l�<ӽ�v�[eTN��IꝬU=�I��Js���0��O�*G��~[�����t�%R\s^���.p�\���.p���R��F�����ޝ!{��Ӽ=�s�b�?�?��*�w\%���=
����$��;Iu�_[�p��i$̻c��o�M��?�]�b��kO��4��\GT�J�M��Lg�tGV�Nd<��֞1�2��a���V�TgT��4�ʫ��r��"�me��0�vQͅ1*Lw��ۉM�ϮJȌ��_FQp��)�e��ꪮ�؞v���=���D�j�"2rP�f�]���C���v�L���i;j��nj���
�v6&��gc��e�ٞ���e�!��T�n���VW&}c�*4yr)ضr�đ�R[���e}߄m��b�5o�?�])n&b�n�?^V�V�@E
�IS��/�"V�m����9zZxi����l��*�)���W�2�JN�4��9YzE",�ڢQr�3�:�q_ŋ|�NnU�.v��.�Y$�aL��0�\��֜B\�
����%��+[�r��"����و�#��%XKr��:�\��|
�*���]5gX�Re!)��euFQ��n�7v�I(^�ݢ�hՉ����gj�j�[ŦGHXG
T��W�P�So�J��NR�/�-!<S3ٷ�3^2�M17�ݖ옣f�뉘�OnoX�Ob6e3��Nj��>�
�[��7�jor�_��j�b,�M؍I�Fcb�O3���xc�p�.��$�5V�Zr[�6�����}�i
��^�v�,�x��Zу�n�Ww?��E�Y��ɨ����SM��|��m���̌�7���N�Nwj�䆖0V�֮j��=��g	��Ebp����BiR
�RK|6��V��+�����߳1�Dj���-8;�x)�h�T��J�M<��SM3��Q8�Y�p�Q�I��.K^ǼS��nr�u�}�.UF��sV��듌R���ar\��DJf�-D�9֬�my^�,۲��j�q��)�ٞ߁����l�%P=̀>�2}4���w��!/6�02�u)ʃ��F����}��$�rh�Z9ۮi��q��3/͔��i�n-�Sfc�^�Z��s;Wj���R�]�M�M4��q�x/;_9	|[�g�/�B�'˕��\�t GHn6�!ӳL��}����Ś����Z���+G�'�>|gU�O�O�Iw�y��f���Ĺ\�Vl!@����C�wO��ݍ�Qu��h���u
��Y#��)�  ��4,�'TkA��n�FO�)��e�^�-狮c��a��e:�A�Y�֡�[k-�oa�8�"i����Tt]SEɪ���6�P/��T���|�g����=�>�����N�d����oyq��҈�g�S�맱�6�D�<�6�-�vW�V[=��{w)�v�n^�k�J�y^L��L���N�2W�v'F2cU؝Ɍ��┴�$jZ��Im3>��L�9��N�=�gM��(��?�Z�w���nk�}�����_�TZ���)򽼔�z�LՕ"�Ue�����Sq���fZό�[��uYծ��:�����b�L�b�1EM;#�Fٝ�(5��\�q�Tz^j��M�H�Wˇ���U���*�Ir�W�Lm�FS��׺FhA4ٸ�2N����tkNOE��ŋSrbg,�G\�Nço�_��f��~��vJ���ׯ�Q��.�P�ҕ��B���\�DG�DG�����4�q��pd��.vOb�Y��E�,�C'���G�!���9��+����Ꜣ\�Cǚ-QUکߖQLuMS���.��B�Sb�!g�L��$2��Ű�':�g`�[3�u�ZCNJ.�U��ݞSL�EQ��.)�~�2<u��aÒ���ĥ-
Qؒ�!&���)������]�g+5�Lr�ӷ�k.���t�R���xl3[n�(��G�%e�{��Vb��ќz���˴�U6+ի�Z���#Y��$��1��1%�%�.I̦�ӽ#e��u��uUTQj�⊦��6ǭ�X��)mF�o16�37�DQ���z6�T���*!�v枸��62��4�4�N�j�}q�ṡ�I�{��֣��]����
�����;��w?�g�|����~rа��8��I7u9�of�~[l��b�pv�C����܊y��/A#ye𚂏�ּ�!V�Ʋ�<��7~�h�=}�
f�yIL��+���Φ@��.�I�����S��\����z�U��Q�4ǭشk�*�T��"#ў+��s2J��*mD1�M3��9�CTg�]H*ր��I��+"��
�ⳗU��.Yӗi�?���	j����'*ԟZ�/��=�O+�UNM�De��V�o�H��"������2�ѣ�EV�:M���5�&�"���͙�LE���\_��)�'ѹ^�l�Ϝ|Ҙc7]�t9Q�-=�v-9�ܘ�0����U�q~1>T�3�K��p���tl���LM�-i��lX�V�rG�6�u�s�f��lg�u�l�*�En��&��L)qo$�H"ʬ��de�%Ѹ�tZԮuj�d�_H�Ww^�֦�HP�@�~-�-�fK(m���]#��f��n��4�ۚ0�"w���G5�7Oʺ��3�K��RIQ"��6������Sv�"c/Q����Un�/<�5��0�ux���e*���u�I6�ŭ���*ƥ� �Ib(�Gnu��cs}���sr�j�L=0t�k� R��Ӯ�Z���Zu��i/���/C�s;+�4�Scmtשv��RL%W�SU֪g̡B���>�np�t��T"��F��jҕ�}y����B*1Sr��wUNIk�MV�o}5f�ѵ��*�1Ev+��].3ٕ)&Ѻ�Ȇ�J�j34�"��ūs�US��n�5Mˑ�LD�`�3V� *ֽqӷ5�V�-��v;��~6Ub�T��9L����T���$ת3r�{u�d�1�����֜��%�U\��Ռ��]���G̟M|�,����y	y�.q��s�\��.\�.p�8˜��&��T3��jC^�L����מz�t�՝���[#���^�i�����^v0�r����l_���O�+��D
�@ufgѫ	%e9Z�Vf~/זֵv��+k�8�e˜��r��s�\��.\�3�UY�/5��Rk��vW���.j�֒�z�D��ꞀGiv���?@��tT/O��/�i��r�g����ܿ�&}���??=5_���F�Y�CU�O�́��Y.�<g�˞����!�b���^X��X�Wk��;�v��M�*'���M���G��ϙ������DYܷ�x���lr�;�6oyï�>L���U��ԘO�<ƕ��6�+�˜�d'���3�l)]�U��k�Y�gG�;��.��S�/������Y��;lAU��V���=r�Fuu�OG^��4���]Dd)�;G
*+9-FJ���屲�#����nx�_����WX�OK�����{Q��c'?U'Kw�.��a���z<z=s�^��+߆��Z��,d������
�o}*G��\{^��h�~��4�	~�`�oR$��v+�3�+�&�GNwb=_K�MW�?�����BKYa~���^�m�nz?��)g���lw(��+O�����˲e��#[�ԟ<q"p�|�#ٴ�g*i�e�S2���w]�y��SD�߷=�<��WZr#�f��&�ڕ���x���)�ː�_;�kn�	Nc">2�b��72�-�G;7�&�=5�9���T�TS�3�3��͗GaOje�l�q��
|Hˈ���q���(���=�czmMȜ�՘���-܍*�/]3Mɮ���j��������w�d�a�Ѩ�mRST��9:�,.C��4��4\SqyMJ5n+
x)���]i�n���W�ݮ���۷9l��j��sߔn��j4�*t
�C�����N�Gii�	�)���4�*"��3U�jb��|l���b�ڹU�~"�������}31����Z�c
��b|�0�܆c��h"�"JDX�;WT�ؗD[����9�նd���RV[P����#ΜB���*'��9�.�K[����8�Ӷ%Mz�XI�,L&���/�ʼ��湭�o>z3�'���cZ�t��&*2�W�SEFXK_�U�v���XE";&�-�2V�2�e�,\�ܪ(�c8ՅL&2ͪ��uE3��?�1jj*^F}�ݩ?P:��ZY8Q��uIJ֛�X��D{x��j[ԟ+<�I���L[�z�3�[n{#�9�o��R��F]W׀>z�Czt��kN����_(x��<�~���1�xʉ%͍�5���R��x�c�ֱ\u9�
�oS=o�G�_BQ�h��e"y��OKkd��WKƬƬ��2lW-���۔��s�AE^,�6-S����>�YrM�l��YRI"Q�[�E����t���6�h:*�9�镏P���dI^�0��Q���3���Dg1�ʲ�g��=�{�y���Q6�v�#ʕJTg�”�B�ڍi=�3+l>R���|fQ\ӻcd����%�I����4jC͡�Q!J2�e�g8�F:�˳g-ݎ�x��E����dEy/Eul<��֔hQ_f�M���芣)��Ϣ��s��C�-LpsyÏ�[�5Ma����v�"�9��vf�Ye�ĥ.N.�G�6�*dZC+Kr�a�6�8��ؔW3�!
�i�UqMq\�,ڪ�4MT��1��UGH
�'Px�MK���k2Frug~2��"���x=X�Ny��͙��&��s^e�u/2�4��ٚV��#+�SFSßMSL��,��n�S���&�}a>��"���ion�&"��W_�3>�h؂�!ËS��Eo���!���kV�S��>�i�\�2��!����y/GqL�����J�}����SN�T�19��JLwV)�T�?*}>����uN!�֤�G���B�1f�z�MSԳ\޹F���0츱�RD����p�APx�T��}�|��W<>Z�2�L�G�����%^�
�cÛ"4w���Z���%&D{�آ��b&PS~�c(����2�I�R�pJ�uF��.+�bJ��3��rZ�M�Ν������Ul���Q�'�@��</s��o�G�9|-��9�-����<P�t!��D����(pֺp��'Lx_�-��C��Ѓ���:c��o�G�5��-����<P�t �m�N��`[��
k�o�tDž���8k]8[}�s M��w �L&�O$�M��W�a�Z&�Z���M�M����(�0ozi�����[�y�X��K��LS=�~�q>\�_��+�o�̅�ݙ����j������c��j3���-�q�/�</��x��B�'Lx_�-��C��Ѓ���:c��o�G�5��-����<P�t �m�N��`[��
k�o�tDž���8k]8[}�</��x��Z�A���F!�+ZP�f�H���m��h�!�t6�WluQ/�}]��4O��~2�ޝ����00۟~K�>oЋX��c���T�S�U�Y;����rs���s�-����p�bg��ԗہ
r�%��K�ZRJo9�(�n1V)֦wkU��8�u0W�ժ'lQD�N{r��vhIdI��]Sϯ�u�̣��\��SF�snMUխT�3�.�᜚j��F�V���Mw����U�=�\�����]�4��"{`�0��M7�4��n�"G��Q�ǵ��U돃H�u��3""C��l,���U_�.ΐ���?�t��D���S�^��1-�<7Jn�P�÷�R���U��mG�=T|�49�N�2�Z��q��EQ8l����ɮ�c2;��Ls4Un�ծg8ۖ|�2�<-7j�Z���vg�,N_Yi�I���zZ"�O[���Qn��g���7l��f�h��~�Y�VZ5"�n�~�e:�>�NM"[�~<T^6�d�ͷ���1)'c��-3v�x)�jc)�'/T���ڻ_Le\�ES��y��N��m��>�_��>���h�JəV�1e�g՜��0�^fx8��(�fT�9]���0�뒸�a�N����UfB�i	���<%��;����=���\��5rLrz�S";C���L�ˑx���u�qғ<�I��-a�<%ݓ���A���x+{b�*�H�_\��#
>ҔwQ��)]�Y��Q�G�>.�~L��L�L�9�j�ܗ)ÿ�,����J�����G������Js(;2�3����>l`�.�I�"��Eb�\š��� ��^�=���>��ךӤ/s[�w����O�C�A����X��vQn2�>qݗ''��>�l�|/�J.���o�/����담c�,ػ4�r>�����QW/+F���n)��*��DLCJ�˦�f�Jv��&[Us�:�
-jŽ]Yϕ��軷�kg%4W��M��6tw���"67='�]��.�� ҚF�"#(�����<�s�2��~+n��WJi��)#ږ?�|���
��.�O�O���֯�T��{W�Ia�#��
C[�<��e�[��������[���p�8[�O:΍_��q�#�Y��*qzI/P�i�z�'����?U����'9��_�n�g�f2�cH�Tr(��ڃsh�R��5$�{X�/aZ��GW��+��>-r��p��5�m0�GT:eb�P�Qg\��2Z,�M�n�U�2�q���U�i�vx��FY��sm�e�a��F�����X���p�S�ʟ?�[Q�K`�b,]�tƾS19l�Ε���>&q�n��8Z(vmB������-:e�F���|l�#33�v���n)�|mM��b���]j����$l�f�X*Er�I�EV�-���i�8�ɒ�B��;�OE۶/�կMQ3؂�V��ע�Z�b:&�ґFV��_i):�P�BkQf4F&Ӗ�#� ���~5�H��?T�ٷbb�k�Y�2���]�3P���B*�r��gr�˭9L�7�4�%v�(��g�[F��Mu���kG<#��|��>���i��rM2��yjS%־J��O~�FV��^&v�_68�#:"�Y��,�c�D�����̊��)��ϳ�{R�"�Y�8���l��9n�d�/[�,���n��&��:��cT)�J�|��:�Ha�]
m�d�>���]��MS��o5H�Sr�M1�U\ɗX���/Q]
�=
Z��JL�5:]zYB6$�{?�\Gf�N"�_R'td�xkjjk�o��4)�]*�m���HZ��S�j��~��WR׼��X�E�܊#.�A���ܭ�s�d&�MRbб3�q豪�g���u���\U����#�nQS~��{Z��n�sbݽK��D�'�S*�ռ?���'p&ګ�T��a��Y)�蹪ݙ�5x����Ijܻ�rڅ�O����Dkti�lͩH��n��v9�J8�";�Z�oz�}}]m�+]���W�����+M��{�i&���f��F�n�D�L�;i25j�
�4��y.S^SֱoGZ���8i���_��J)qx2̎���㲑kfqjύb�:�[w�u�U�|��N*�Z)�.^t���T|���(����8+��g�Y7�[��~�mJp�J_`�X��<��;4x�s��q��)���ajmE�I�y��՜��:��rx�XM�M4��q�x/;_9	|[�g�/�B�'˕��\�t �*��i;V���.sB�V8�Zϱ���ҿ�<-��*]v*lf�խY�!#o�Nu�[4�T-a�u@>�I�c�~���o��j}�x
7�W�`a�>���l��q���⫁����"	݄-d���X1�kF25Sy۳\�)�lU�F��G�W0t��a��S�5���u�����;���;Q�}i"���%�\�����]�1�P�q��œR��ά�(_���KN�q�{^�#�uz���h���#S��d�Ӭ2�l�5ؓ�2��*}�2!�z&��� �ju<�giO�l-I��J�Օd�<��}U�rs,̈́䨋i4�?}ѬZ��e��M7#O(�螴���T��)���qkBP�MԢp��4+��.]�<��uahm}��8y[JZ��d��Q���*�E�G�7�b4��2NH"S,�՜��[��3�O\|� ��R�DB��wеkTe����$�.�j�den�o ������^^d�UK�g���>z�Czt��kN����_(x��<�~����p�.�5,!W�q�]
�&t#;�-�Gȴ����~�EO���c��՞�g{�|3��x�M��'��Əc��ָ��c�b0�٫*��lbh��9�񶐨XN�[���/���kV|��Σ��/����x�,G����>$�U<CX~�Q^ilJ�mֶ�����8\56hե��X��W�R,XWo�8��J�bո�N���2�,�!�]d��I��}%Lݮ�?�3����TZ����mi�4�n*[.JR8+	�D{J䛕��h[��ҝ3{>N�m�[��q~<y�G��)M���9,���-֙�����f�[�f&f�[}N��s]q�ӳ֎��X��������z�)�p�}�DdF�iؒ�I#�w��g�D�Fs��	�MS]Q���\�Z=�a؏����F��w�KZ��DJʥlٔY�W�b(�1:�LlW���خ�Lkkf�z[��eSy�M��N6ۉ_ZѤԛoM�Vᵹ��k�9M�.��ML�8���
TZ���H���-Χ<�
Q��u*;��1��5bh�թ�ا
_>p����O:�
Ħ�<ԡj�sJ2�,�D�-��X�W�?f�K�x��<��5�׳sŞL�!�r�6�i�[n�-MB%뵉R��6�Z��*�e��Y�L
h�R5��۪�׭:�к$���K}Ĵ�,�8�I\��W;�^��[:P��j��9�e����kG��;�QV\��a�ֱ�X��Jٳ(�_�����e���]����6]>��?���Z�z]'k]��ңRZn�4�zOm��ir�
~��Wi���f�9�7qv�Z�\�u2�'Q��2����iu�YUq�6��TE���ӳv�1:?��^�Q�W^�*=Ux57�cǩę �����Y�N��Xͬ���]��j�L�3�Աj��
[5DU9��#G�����=��8l��o]�+1%J�R|RV��s�W:���;!>��F��QVԆ/Z���^��]���&�F��Z����X�"�m�Vs��>D���i�FSF�U{Gr+�U|;>�%A�IL�$��cZ��K�W������1j���5kӳv�R�#Gp��tUN����)5�M�)0f!�u�*OQ�y�%9�|�-���=|�Te5܉˩v�"�(��E���u��P���}�>d�h�W\}������EUjIJ��Vx���O�b�ֽ]I��֞��DDV-ð�+m&�ա�GM�rȖEȭ�_x�`�i,>�x����?�
r��
��O�'���󰱅�ž��{b��.�|�X��� WB�p���>��c���!ձ�CE�tE�*J4��Ik`����\sq�*���g�+�X���P�k鴉DYKַ��㫄���tp��e��k �t��i
�_��Y��4-O��/�c����C�*Qa�Z���;�ߊ��"R��FL�2͗��&��O��&'�K\QW��bc��}\H�9��}+qT#]M��:�㴮��b�"��V|Z�����b��cZ<j9����F�E���s���8m�9�{��Ҵ*b�m��#�+xH�s�9��Z-d��k#U�
�se"$6W&J��Z,���cJꦈ֪r��Y��ʘ�S8�jeF��&�9-4�#y�QɧI��=֩{-��8��k�����=��8�dsz�&�mQU39�VQ�te9�S^q݂�ŒR��ʡ����$x�?���{��uz���V=Â��Z��ҵ��r4��!&w4���w����u�`���҃� ���%����͈�|:���������}j0��|���������;����P��y��?9U��2��*�6Re��Rv̨Ыs\����+����-��Q9�9<_}�-�)��<�:�KQ��cjh�c(���u�S�험٨b��W�7 8�v�-����eE�b��2#ߴ�6�3,W���1��JOy3��'�v�3����@�a��3�b���Pg�$`f�ғ�Dv2�{��32����p3piI��W7&D{�܃}�>d�h�W\}�����aڅY��'$oW!]iw�q��֬M~���MK2�L�M��ѓd'�W*�ʥ8�ۢ)��Ӣ��2�Xݳ�̴�+eԒ�q&����!���11���xe1j�NJ��=��+��h˔�c���Mi�F#	4�l5�SM�M4��q�x/;_9	|[�g�/�B�'˕��\�t W	�]��?�1�����!�R�Ĩ�TY)̅n>T�"����FR޺"��U�s
T)+3q:ؾ�BKg�rnؚ=Ne�3O� D�)R�HI���R[LϴC#v�-HZ&����S��_�:|/-K�p��7Q}t�f��m"U���6=���o�^/KG�{>�JE�����ͦ�'Ӥ�|�Gz�Y�r��\I�+OiD5�f���Q�������L�)2��j�$Ъ*�i5S�?�����:n��
��mn�%<�����n�¹�ī���l`U)u*S�nsY	ͬ>�'u>��St,��'�z���c�.���G��T����{���4!����G�Sace����ͤE���-�f|�\�-䩪�*4��#}Ps�f�l�.{�{� ��$������ײ�kzS��gj�a"��gW����׌�L���5����H���������lOhoF3֯ǯ�y=Q�>-k�NZ����r���7Ғ�y�V�
�[j��-*�(��9��;�9O���W�>

R��+�>������=�8�IJ=���y��Qm%T�Gt��F�����ޝ!{��Ӽ=�s�b�?�?��*�w\ ڣ�O��4�ѵ�Ϣ;���s��Z��>����?�#�q>��m����p�=g�>����?�#�q>��lW�Q��g��G��}ix8"��)�IYr�w/效OF��MN��O!BI("JKa$�)܀���=TQ��}[��x��B������0�T���݊ma��3��0����#3ٻ9~��W��
��Y��^�T^��uZӾMVkl���%��֫<���kNy�>����?����O������8�Y�Ϲ��G��È��O���`t>��<��Ie�l��}�1v���E:��,n�’��Ң%$��03�M
Q�Kj���d�_�;��^��AV�D����Z�����!�C�ޱ�q*���u>�	&��c4�c�[F9e�S�4�vرҕ�!��E�iܞ�Tӹ $H��LK�����Z�BG�{�^?JG�{>�u�����kl3���qi�?E#��՜��M�u3�)�*;F��:w~�%:�����X�w��ܜ�U�'��JKWj�f�y�s`�H�n5Uu3�CT�E\\����+4wQ�Yq��lQ�N"&���5j��6s�O\.Y�<m��Ӗ[��ı)�k
����uiW9|�-Ď��G�)Uͷ�?F<��W�gR�
y��ҟ+�v��"�R�f����F|�g��̲U���o��[j8֌dj�J��ʂ5(�:�_JM�����fmEZT���-��wE�)�K�hXʊ�pЖ��V.*=i<�<�^�c��]O�g���#�8wԞ'�4ȓ_Jr%�6�$fyH�Fv���5�E�#*j���Csn�Ϊbg��$`��S~HLj7�uv�N%c�Od$`��S~HLjv�N��8���=�t���M�# q�:�d�V:�A�F�7�x��o��퓉X�S�I/�ߒ1��ӫ�N%c�Od$`��S~HLjv�N��8���=�t���M�# q�:�d�V:�A�F�7�x��o��퓉X�S�I/�ߒ1��ӫ�N%c�Od$`��S~HLjv�N��8���=�t���M�# q�:�d�V:�A�F�7�x��o��퓉X�S�I/�ߒ1��ӫ�N%c�Od$`��S~HLjv�N��8���=�t���M�# q�:�d�V:�A�F�7�x��o��퓉X�S�I/�ߒ1��ӫ�N%c�Od$`��S~HLjv�N��8���=�t���M�# q�:�d�V:�A�F�7�x��o��퓉X�S�I/�ߒ1��ӫ�N%c�Od$`��S~HLjv�N��8���=�t���M�# q�:�d�V:�A�F�7�x��o��퓉X�S�I/�ߒ1��ӫ�N%c�Od$`��S~HLjv�N��8���=���@0i�v�Mc�?�ZA��Q�)y-'ǫ��e�`2���}�~�>T���bx�K
5]����po�
sgU�X5g#X2��$�j=�}y��G5�mo�ܮ�HI�
�r��g��[�mg>f��W�L�Z���w���x�]�ytz��оE^�jY�{��=qu�<˴��.7��E�`9"3;��{��YSzI���GF{���0�@U��B3bi���pN��߃k2��K}v��K����QoS<����r�:3��j��>�����(�g�ſ
>���;��~�S���(�g�����i�oK�\�9�e��m��_���wM�>�6��~�W�SY�Q��ˍ�(��{���7��}N���e�����i�oK�Mg�G�?.}߽�N�z_���k?�?�yp����w��>��|��au]-��o�����.{�����De�ߧՎ癹���JjY�?.7����k^�z_���SY�Q��ˇ�w�~�;��~�TN"�&�eqU�V��6m���{���6�8�OIq��uur����	���y��-����P�=�������T�7gրp瘽wF/���u���:�{����T�[ݏ�o�������u�c�[�������@E��V���-����PQov?տ�:�{����Tۨ�*l�/�|��u��@7]��G8�Up�90�Q�8=��ә�랽�6��@\?��dist/images/google-logo.png000064400000021776150755130600011713 0ustar00�PNG


IHDRA24�F�zTXtRaw profile type exifxڭ�i�d�r��3
�>`8�ky��Mf��'=I����̛�9@D�&@��_�_��ؚ��Z�z��{<i������'��7���oD^J<�ϯ6���^~�{����]���B��ߟ�;�� y=~^�{�~>Ojo������o(߿�簾��w���Uڅ�O
��oL���ߡ�7%}.���{o��+��x���E���~�N�w���wkY�kē?|#�߽�~�?������o�ȑ��~:߿��v��n�ʊ�oF��?.��HN�k��oṽ��O��/B���z���u!�F���C��D�1�E@�ZK{\�'~�zک��#�9şc	��o�Ɲw�1p��W����7�ɏ�wi���ЇO���a(r��O�p�q+o��|���X�*,o�~~.1K�%�ҋs�s��O�g�{!�b0!_I�P��-ֱ���c�q�PJ�2�jt�RԽ�����Xb�zl"%�dĦ�A�r.��F��J.��b���˨��Zj�VrÒe+VͬY��R˭�ڬ����'0��ڭ���
n4����Wf�i�Yf�6��s,�g�UV]���k��&vݶ��{��Hq�)�;��3.�v�ͷ�z����oT���D-|�_��9�5^uf?.'E1#b1"n�	3�B�Q�S�|�E��(6nE��b��g�~��ߊ�+�o�-�U�B����#t��?��ϭ�OjM}��x���b"��W��^/R���B��4n5�tk]}�w;��=�Lwo���4��[��|�Ȅ8��`+��g�ts镦��ҭ��i綥���V����ߴ�\����Zf�X�4����qSb�)��i�e�׌��{�37���ZUی^�!��^v�l�jL�����at�tf��fuOZ�lc��X;�kvΪ6��Hz�%�:R��w���n���u���V4��V���7�y*�gFj�IN��o�$v{kJr��4gb(cvr����Ҟi�F�/	���\��pf��S��Ӊ�z�+�CA���M��M}�%���bR�i�J��|�l7.�'g�q�8u3,"���㘬��a��i�q=��7\��T=�?k��T��}�����@��m���y��HϽ#�"��|:˦�r�dn;u
d�I7�M�t�!�D�nj3�8k
��΁�q�|6�rk�Lp����+��Uj�J�%��G�`�-����ܵ�?����ć̋�)�,ˆuIt�H�WX~�Nb�q@x.%$�Xe��`���<~Й����Jm��rK"u[�y#*�R��@P�Ub|,��
��Y'�c+�}��\��;��N N/ ��zO瀺�Gi<��8�´�[�L]g���Y��@��
��k��GiQ�ǣ֖��`֢Ő]�N��u(�qP�q�B���Df
ڹ,�!�F��<'?ؠRuv�Na���d'w%��@��V?��2�Fm�6����@�i���$� �թ����5H�#K� !׎�R����4u��
n���!NP���%�V��eeJ&���t����Jd�[��5�CS��l��(�/$Q�(��)�y��rA<��k���R�v�]�F�B��u����4���LЄ�q麀����\'&1/�ḀZ댁kk}�~@
?��G��I��:Pm�vx�6��b���(�)u�Vp�6ٰ��l���ܢ�2E͋�����&�.�l+5��p-?=���A5D�@/~X�N���D|�D��F�CQqfꆄ>���R�9���G8�F;�ޝ�.aꙥ]-�/�4<�$�5o�|ʱ�W�U�}�K����&�
�	��"��&;8Q�� �Er��?H}>�����,B�'p����-*A�z����b�QMIi��Ȃӂ�|��;�#n
�⦌�@}��l�7���"�%o����3�u�"GY�ѽ_��I�Oj׆�f�Z,��rj��5�<&�{tN��s@��򏼜�	���̂�;�f��B�8��G��&^J[ܓ6�>p��2Փ7�˵�D���!͙�}�J�@,�qخ�8Q��_�E�
����[�$���K0���<@�3�	�6�)��_ѩ'z�	� �����L�)VXr�j�\���z�W�(�zDL�Y:����z�߉x��f�b�Tv�H>̛
��6�z���)�~�̠7�F��K�5�	r��ck0-f��͝�JvR}/������?R�dզ�&�� &�6��rp�^��V��㗑tqI�c�|��8�<6�u��{�H�*��@	�x#��J!���xC�����z�l[8)
C|�$lh(r����c�a���N{<)х�Oqߏ^\I��]���b~nXH	Vj�ʋ$8�!��t>�
Gqf4�S�\9�4)�H�0�(I�x_ȏ'���Ӌ[T�84���A�C���vc���Ͻ c���`D�"�O�).�$�+�xtK��`�d>�&n	f"&�2��pHí)LV`�b$:�2���l�!�Q�HD���$��Q
lg�c����DH>�ӈ&?6�I�l�Ѵ�MZ�b�f��zN�
�N#�"h���+�jQj���?�Z�a�B�c=�KP3z�.��@�4Y��Qc� ᎹI�#&�q�j�Ȓh��V�

���H!�Zz�E�I�v�]ycs&��`�@��(����ȭK1y5S�
�ǟ’p%�`{���^�L�DcR�VG��/�KTɊ�
?V�R����D���Z���Հ���g5@����E�#2V�G�����1Ja؅oyE���Jmָa'�A�?�P�roI�4�)_��\���@u�h�˜�}�'Y�_ߙi����¤�1!�%��.����s'�(�5���:�G>��݉,�V�A�J`�Y\?���>7@g���W�0�ΐ���
#ၸ�X���Zv5sй���)S��T@�� �E�H��;��tp�Y���YD��ds����DD��N�:��\Vv�'6�aD&�?0v�eez��Q��%����k�u=W�Ȏ�3R�EX
����I�*�#a����Ǐ�	�VI��w�������i���n[���$8tq�����`�3r���Gw��e�z�;����W����dy�""�LHH$Ȥ�=iÅ�t/d~�z��|�p�ƅ�	:Pv����H����H�w���L@�$�A�=O!�|b�8e�F�m
�����W1	���́J�jVm]�H� �1�EG+�� ��f��c�G^j��:	��v�Zv�;V�>�8�j��&_�Fҕ=9a��%T^��(.��ʫƋT�-$r@WVU�{1!�	|�:]��
lV���ig�=
@|�*i�V�<jF����TEB�t���x�}b$a �GYϔp�L��^H�1���6�T�At2��P�pͼ]�Іׂ�GQ�z�{F��Ty�D�@dw]�{�*��uDC�k��(��Ɲ�����^JR��Y�N���Up[���nB�q_p	q�T��wQv�/i��d�4���Sd+��$K�ў5$��NZ$Y �nk8��@� ���T�x�R/�ٓ$�)�5`9B�ȼ��2�j�]WDm�ƍ �`\���G2�p${o�2ש7mC<�Y�nյ͋����X�)����CA<���6�n�,
N��i5�BQ�c�P]�STX�N�kB[>�׉ev�K=�Vڇ����I�,�'�87�j�ƩQ]�<��{B��N��.VA�"��k��-В��`d&t$�R�H����?jѠ��c#d�*vGf6�$��[�Zx���n
�U����XZi�;�5E��G>@`꼈�/�J<����ejV��m��>��j�K�;7xF��6����ػ6[��D�.A遈� ��iFK��d�
���I�N9��
��py
Z��E�c��I;�lb<���I����j&��-d�?�-�T�em5���n�r�]�$�1���T(�Qr5�z��=��Z�pY��qi��ш��k�!��\&�Vb������J	��&m�����OX��?\+,������F��!'Muώ2�j�g��v����0Ag��@�.8w&0���Jώ������$U�!2K!�؝���v*��G<�
��3�g���N�@[%n��)٢�U��(�d��D�K������=4���(��n8>��E�@���P�KH7jU#�j�
y���V��NPc*���
��kϪ׈J��W��(0bV�^�J%Q3�4��XM,["�%�-��!�`{�5Ȱ~HQ��v��چ��?+�m&�Ud'u��A��wh4�� I�)IďEn�۪����/&�L�w���Rȱ�L�M��@=/��I>`H�?A�Q�h��fO�^�NyKd8�2<�C�K���T�M�[�
�f֏���1�$��+����z�j�
�}e0=��P�ц�X��6��d2�4��_M�~�o��>װ}�`p	�ƣ�i(X�2rO����p�h�ʶ�B�H�2�w�>��$,m��G�1��j@�2AK9�E�mL�cxQ�JB^��dS0�:%�a��iHS�RH(��Km}�MR�vmʄ�|)��E��x�V���Y�PA�����	Lp� )�M��'(h����Q�$���Jg� �uSSӃC�5\�i�l����z������������VK#>�yB薚�V��C}��OE=aI�Lm�lm���Q�d%BF3�e�t�`�^�	�\{��o��eq����o�u&�;K�J�mY�>r�:��0�KiYu"���jP/��r(m�����:��]�P��Z��p��4]R��dm@㸠�z�^���
���pc�Z�9'�2p��2NRN¹�V�����'B�I���>8wY�'s��i�4v�`�P6�\41��i�4?�A�T����t�������%��C���������U�IGh�Q�=[��*S�Hh���|��Rm>� �w]?ݷ�T�v��5Ս�P�rk_$�HX�x�Wc(�T2�O��?�g�s߶���0Y�~fQ9Lv��0iK��(#�Q�z��P���Yq��ڒ��`���r�2�R$5��-Q���+�o�����F��5�ZZ���@L�>!bM���k�"&��S�3Ի_j��W�/h��8�$��F�XBBԤ��d��ͅv��U�t�-�s:�q��P���V���s��ʱ׶"<���*������Y���&�4h^�ȹq�����_�p	aл�h,��*��)ص
~�B��0�fGZo�C�k��G2�ϫ����I-@j�U&|�5�S{���A҉�ݍ\_�$�a�1��Zu�΄Oǯ��/�WG`9��G����n@A��)Q��HQ��]�*�u��A��3n,獾���E�9ﻍ�1�C����j�&�pU�=��/APF]M�6��O�	���v��8�V2�~�z�2@ob|�be�WLM4����H�/������(�I���{��������b&d����m���8����p&��P@HI�� ��R3Y�Y��>!���v�kN}��1�St� ��v�c���Z����ۀdd��"���W�$6���މ��|��Wۤ�V"j�a��F���P'q��:Y_�!i�'��9�]�)ө�
�X��Z�~y���w=Q�G-Ճ�I��GhV�����)W9kV����
ԯ<}��E?���=�-�fY��!����i������
\��Q�I��n�)��^T���J��A���ٚ�(�O��k��w�CVY__W�X��s�@�h�6@p�K�0_w�uG��"#�
}S��Y{ͺ�#G��κ.�k�^����H�/tM�9�:N@�ms��s
u�ۻ���Y�>�CE����ORJ�J�o�$�S�,��K�g~o����1%��g�ufQ�$����=���eK���脝_�/��zB@o]�=ӆ&�B�!��wT
W0�N.��"x4s��-�.��4�yj�RO�$����#-)o�L�>���{Gm_�@5k_N�Pt����I_Ի��W�Q{��02����`a��l:ބJ�FP.Qa��ӹ��Ih�< p�Ɨ�h[��v�o���j8:��<Ԙ�c}@YJ���-�w�e���}�Ȕ���aGf�u�WS���s=����א��	-m>mkOX�B��R��T�z|� ���8�v#�(�IΡ'`���=���@�6�%��:��Y����4�Q{���R��%��l�OO���ua�!�E���
�t����,OA��
��b܇:�����K'O���R���p}K��e	��� #�-�B_v�2"���_,�%�0�ӈ�?1a�K>�J�og���I�ǽ�e�l���I����u�ZjHbn�U� �k��?�䶎���U��a����=T�SC|>ݓm�0r� U�AEW&�,n�;&8��z�wW�}�����[�s�+��aQ���6�j��k+N��L4��)�%2,�SbqFIy�<��]���DG��60��u�J�����6��<T���> ����sb(�l����$��+�i�Φ.�^Y�"n��%b��t�b�,�`aX�tq���J�ʡ8��H@j'_�C5�׻��	�VY�2�:XH%�Q���bU���<"����ͪ��vx���)o�uZ�QGU3���+�H���E�L���U%���V����j�ȅ\�'�b��j�+[�6�{�>�@��O�e���6�T�_z[��	T[}}�*�X�-:a���2�|�;��ħ:�k��Iɞ�{>�5�m7ͽC�Hr�f����U�w�"bﭷ�.��!%O�u��Y�Q}�TtC�H0f�P&��wW��XSA2����E/E�"��;rn��Vu��q(�1�J��x�y���)J�9��>tϭH�u�P�:6�v0�vx.t~�)�Kkf�>i0ΑX��\�pb��v��)5-#�ڦR��4�̋�D�����
f˽CrV�BpuS��W𛕃:��yac�*B�+:_��I�Y";���|<av����0Rm�-�g�d�F�AphRa��,���g�X`�F�і��;��D@"��Cѫ��W�x����où�/�tx3�Q��̅=����֝�W���^,��V�}g>1�k2m��G��!5#y���?S��fm�׏k��L���m�� �Tt�|��+�cuB���+9�U��U�	�H�C��^�勐�g�t���*��F=��s�G�%�:
ಛ2��?Nj��|$�.j&�nQ;a�ӏk�ӎ�/���I��{.6�?+E>�µcI��:��&��(w�:z�Uxۅ�驅����9�먌��Y��Xm�iCCPICC profile(�}�=H�@�_SE)U���d�NDE�
E�j�VL.��&
I����Zp�c���⬫�� ~�89:)�H��K
-b=8�ǻ{��w�P-2�j4�6���J���`��E��,cN��h9�����]�g�>���R3|"�,3L�x�xz�68��X^V�ω�L� �#���8�\xf�L&�C�b���&fyS#�"��N�B�c��g�Xf�{�3��2�i!�E,A�eP���:)�m�t��r�ȱ�4Ȯ�~wke''��`hq���c�U��qj'�����R��$����G@�6pq�Д=�rx2dSv%?M!�����@�-X�z����HRW����Q�z�ww6���z?R�r����.�PLTE������UUU333OOO���fffZZZRRRXXXKKK���������uuu???���������<<<���������|||ppp��ﵵ�GGG��輼�]]]yyynnnCCC��ש�������ԁ����������Ų��������:::����������ۿ��+++������aB�tRNS@��fbKGD�H	pHYs��tIME�
��C�IDATH����8�#��Ebg�,$�����fSv��==�$�i��� �r\�Ŗ��
���c���%Z��Ә(%(%B�("�ɻ��z���%R��e��V	�-#yÆ'(��A#2� �d$e��[ԍ�za��l6�Zi,`�^A$�\�	��m�l�9�+aK��}�+�>���3:�ø��Ƃ�G���s\=4��Ll9�Z�e�iӋ��t�[�ff=㒳!{ɶ���",Z���rtF�$pޕ�!f,4�Q0BH�	����ApM 3ƙ<�@�W�e�R�AŃ��'�k߯O�������$\�W�{uQ��K�c�ѓ�8�P�
�P4fI���.t"����<����Ta���t$ф��V����ld@�N3�u,l�0418��5bXc<�:�ȴ�&���� ;3*4݄(Cp�"κ�y�wVVњ�%��BPtW�!�K�/i��:��4(v����X�H=zԖ*�Ƙ��~/�o�(g0�߫��R�D7|�i�R7�!8�ݳ�{'A����ј�b7��HU;���s��������6��8�5B�<���C�FD������z\�)���T�ِxj���W`$qI��~���_܂��Q^��v����+w�>�Z�?kk�y7�@6�[��A��Z6;�߉��dqZ�V"��
_fL7�rV����ȁX�~�e�Ӈ,�9xV69��
�{���a��H��ޛ�q�]�?V�$��~^D �x^�$� Z)�H���W�ǥ�i�{S 'l�Bwp��"*����y��WU��G$���v�pUVx9�w�=���Rk�IaA��c�"�:�=����\��^QU;bK�����|�w�v0Wߧ���KM�ξ0IEND�B`�dist/images/c4wp.jpg000064400000041106150755130600010337 0ustar00���JFIFHH��38http://ns.adobe.com/xap/1.0/<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.3-c011 66.145661, 2012/02/06-14:56:27        ">
   <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
      <rdf:Description rdf:about=""
            xmlns:exif="http://ns.adobe.com/exif/1.0/">
         <exif:ColorSpace>1</exif:ColorSpace>
         <exif:PixelXDimension>210</exif:PixelXDimension>
         <exif:PixelYDimension>90</exif:PixelYDimension>
      </rdf:Description>
      <rdf:Description rdf:about=""
            xmlns:xmp="http://ns.adobe.com/xap/1.0/">
         <xmp:CreatorTool>Adobe Fireworks CS6 (Windows)</xmp:CreatorTool>
         <xmp:CreateDate>2022-04-21T12:56:57Z</xmp:CreateDate>
         <xmp:ModifyDate>2022-04-21T12:56:57Z</xmp:ModifyDate>
      </rdf:Description>
      <rdf:Description rdf:about=""
            xmlns:dc="http://purl.org/dc/elements/1.1/">
         <dc:format>image/jpeg</dc:format>
      </rdf:Description>
   </rdf:RDF>
</x:xmpmeta>
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                            
<?xpacket end="w"?>��C









��C

��Z���	��G	!"1AQ2a#BR$3q����%67CTUb���s�����������&!12AQ"a3q���?�Av��k��`�����{2�p��(���4��W�.���u��7�C+�NI��_�$����$�^I7Fi��E��/^6�m�UbN���G2�lhm���u
�Y�A��H(��~aVkG��g;���1~i�?	F%�Rgrlu�H�����-
h��qM+²%��B�R��:�m��u\��w��f��/Ȓ_��z�Dnuk6�C�5WSl�*��9��$SSuP���kۡ�������,\#���a�q�uGg�O��{�	O�W�n��*�pu��ή5�V�����Y
>)K3!���u0�ɐ�B�"".���D]L�	��b6�G�f�-���aw31���vω(ؽԒJО�����t~7�{�Y�=�a���:������ٰ����2>�'����{�k��Rbt���-�r�u��d��^����I��=�$��J�<{|�^ئ��f���Xu�H,�'R˵vٗiQ�B8yk$p�33=�S$c���{Y�6�\w"���$NZZ�"rRl�|)�|*A�����L╧��vk�.ו�Ք�𻔢�a���
��Z��MI���La��c���C�v����MM�?c���v�E��.�L�h/2Inj�����"ش�����.�rd���������%dI�ƂC���(��+�7�&�Rbt�Uq��º�K�	�p�u���&�W�Yx���A�w�#��<�]M��x!gr�=6)�b�-(s�"/�^�+]Δ�9N�,C8���X9��Umd�k��Q(�i�.#R��BA�Mu:E��ҹ�����e�bp�%VJ6~�V����Jz��<[|�^��K���xɈ�u��ml��SN$д���]�qOhN��+��`�*Γ��e�r$�/5-�Q�N4^��'�^]8�K�o�r���$��c��<�睟�(ֵ��vESա�B�a.:U)�����~^~gӣ��ϗWM|X�s=�'�r՟�?�#��GW�b�t.���n
�YR�1S�wz���onN6���jV�d8���Ӹpuy+{n�ls�Tl8�4��\KThvѤY�ո\A)[}�uh�7�Ͼ��^��њv_����_X�.�d����	;H�`�n�U���>-��#%-���8�:9[Y��a���w8≠I�m�A6[pT�h2-�2�N:~��Îyw�sl�ؘ�`y)��S�⤟�'�c�����w�^��_cb��{tk{����E���inY���7:
g�`�֥>�������?��ye�5�����%l�
N#MT�����[yI7Uij�pȑ�BS��&�+jc�Ӵ߶�yzaF��S|�u�E�]E0�g�{��+i��龍J�Lz�YЏ3��M���O�G�-ž���}��J�4r���UOWGZoa�q�]7�J�8�+��0���N���Y䌴�%eڷ!��};��0��?���m�(��!��ݣkd�jO�L�i�&&v�LZv�j��`v��g�3��cF|��������Bi5�d�9v���i��5�c
ī�s��8���N��=����-��1]���L�*Y��1`Y&'�[�^b�Jn&�c�������q���y�b�k{k��/ьv�OE]��/���+r�������}�q�i��jtو@�u��L��fr�q2#��ԩ�L?5)�"m�U�?=��חN,��+�4i0�_�1�#ˎ��>é48�9�RT�lddb�}��{��͓@���>@�?4ɇx[Tw�~��~����-Ƕ��v�U`�Lh�c�cMȈ���:�[kB�e%I=���̌<�h_fX�s5���k�S4
�8�3t��	F�%{��/}:#.M$E��e�TVGT/¼X���}�JBc�Uw��H�Q�+�f�3W��+��W���S
@�������VPR��Y&���L�w�ߩS{(��G���V��
S�w�'ҍ� c1��?FB�u��`�{)n���5n�%�����Zҽr^|'�:��u�����bXFW񞐄8�m��#��b�%��o�AA�Z�@�)3#��C��)Ut�ɾHZ��V��Y�113T�ڒ�V��UR���GS" �ÿX]ݴr�
�ߋ�[�b�ReSY�U�{;؝+�
�q�,I.�]����;��fў���ivAAY�I�(�t�8���r9�ֵ ��R�K���Dd�c-�v��5�c�w�#d�(um+�m��L��"-�ڵ���F��,q�5W�u�Y�X�C�][M";�y�ղ��f���O�+z����-�賊	x�I�TM$�%�k%�Ҵ�i2RK�
��J�bw��R`4M�x�HEC-����|ΪJᙒw뷹�L쵹N�jl�ZZg��O%�#�)�Z�&f^i#��&|4�+[���P����}+-���f�d���z��R�%l�Ȉ�fdD"Ԙ�Z�UF�*�b�#\~���aچ������,�>�oؗ�����k!hq	q��Qn�$�##�#�B�_R*�
�������{�r&,�����jQ�$�Z��-1Ӕ��Z{�8�d�f��^{�ʐ��b�g�\�v%<�jߖn��}��tڻ�w����)�{j�cYp]o��3�Se.dU���p�ݥ��=78�9�;�a\q��e[j��ޢf���Q:N�b��F���5�6b���������4ҟ�E~�5�D�h]q�ڨfO���E��A|	t�S&!_�C?�R#�e�G�IF._ӈ���5M�n-�e���~1�9}�F�vZ9zq��y�e^�h��t���i�0�z'��
9��%�n}d�l���=���/���]�9�@
�U#}et��'?Ѳ����.ޒ|�]_*�cPa#��Ob/B/S?b!�ΝV�V7+����צ#'U�����!�{򗙓$�P|���Ȟ��wU6�I���䝏�U~�z�"���I���˞�^��T���U�gwaQ~�z\s�q$IA�w��m�v�׈���8���b�/�M���R�o��I��v���Q��L4���^�vDƯ۬N�"4��ة�#��prȷZ���_�k\�m��LyL5#��&Q�Pt��R&�bfW-�k͡j5s��ԥ���~~b�ɩڴ˫nX�g���dZqg1�v9xT���BV�c��$�K����M�e���a�g�ZCqm{qg�tȨ�㶴r���ws_�4���2d䜹�����]�>B�]���)�#��J�$��Ѻx|��tNo�K�����?t���]}͘�!�o�d�"����CQ`;:��Cf�JGS�ϙ�6�fCl3�tt�ՙX�,�=��f�}{�t��~E��+�'/��l���؀<2#����dist/images/wp-password-img.jpg000064400000042400150755130600012520 0ustar00����ExifMM*bj(1r2��i��
��'
��'Adobe Photoshop CC (Macintosh)2021:12:02 08:52:12��ҠZ"*(2
ZHH���Adobe_CM��Adobed����			



��E�"��
��?	
	
3!1AQa"q�2���B#$R�b34r��C%�S���cs5���&D�TdE£t6�U�e���u��F'������������Vfv�������7GWgw�������5!1AQaq"2����B#�R��3$b�r��CScs4�%���&5��D�T�dEU6te����u��F������������Vfv�������'7GWgw������?�T�I%)$�IJI$�R�I$���I%)$�IJI$�R�I$���I%)$�IO��T�@���Ļ3%�(�a��y4JJq��^vVp����du��X���N>&-��9Yoe����=5��8t����q���ȧ5�.����c�Χ���_��b]W��Z�x�9�~'H�άY�b�u��wӱ��]�I�;����5�P�k�i�ޣ����z���t����N��^��w�z��K��ci�DZ�����[uGe�'��۩������n��O�ΧGK�0����m��ժ�Ѳ���c�ۭ��*����]��6�_K�klii-;\�p�.U�Q�6�>(���IQ�Y���d���>���MW���_z��m��x?W��}o���en-}x�寺,�Ͳ��]��V����6Q1$�BQu�T擄z��g}`����s�G������Y��\?�VO�lf��q�H�.�8�9�۬�7����rj�?�e�g��*�����g����{Ԗ՞�n�#/��1��u�3�<�����}fs��c?�[N���rc[@n�ip���;RD��ȣ&�ݏc.��F���{^�j�>nV��dU�b�M�~���NĔ�%���mNV�Ǵ��k���¯�VdT�c��hg;�'g�II�C9
�q���m�ap�w}
��>����X�L6b�]�k�����as��IIR\�RϮ���Km��Y5d
ln��^�J�M�w5�߱�IO��U��7tL|Rb���l|���?s���,��� �����ò��w��R�n��{v�@ԁ=��\K��Z.��eی��)��+Ĭ���l��G�-X��`�r6t{,�[k���N�{����fu��fK��:�-F/����ȭ��w=�{�F�^wX���W����\J�!)k�E
�LJ����r��L���>../U����[z~.>W������K	�Ua��X�-���Ъs�KysA?0����g3��;�g��d���w2��;�}��E������_O�@�W���/����>��+"O
�xv�)p�?X]oJ���QqƧ�n�X'o�lw�3`�������e,\^����?�k�tn��b;sO�1+.����7����g���{mu5_S鹍��ZYeo�s\6��k��\�;pp��86�[�g���F��
��e���k�o�YU�y�jϵX��Lz}?�����ط!b}X���{��Ffgc���T��5�ۑ{u��YO�~��б��j�V�ji$V��\M������=%<��
0r:�]:��+�6�7�M6��V�^���Z����z?���T1*����q2sq���.n����U�^���
����j��c�~��k���+�Y]��teYL�O���[?�lk��~��bwO8�����o�w;�w�c�Y�IO!��*����&��+�ec�dtǾ�Q���ב�CX߳��{sjf?�+l��Q�����.�/�ŵ��&kc�i����F�]�=ޮE~��������\,j���$�S�~�,`
�(`�~�Ӝ��*1o󎦶V]�c[Z���uN��y�_V_vT
kK�}�Z�3���0R�=���7�5��Ұi���:�u[�WK?e��
/�����譽��}[��_b�N	��=f���dװm��m��66��7��M�Ӱ:�?ٳ��ʢAZƽ�����Ԕ�a�g��c���Ǭ�Ն��פ��~�V7��k��G���zKS�7A��]#���I�s��F]x�Ҭ�X���c�ϴ��+��u�.��`�fb}���TZk�c}6���W齻؏΅%<k�b}j��z%x�[sr�.��������vzw;��"��^��p�b`c��כ��c����J����J��T�I%8�k�K�׷2l��,�خ���A��s�}ꃾ�uKǥ���:�c��f���md�uI'	�uZa�4�OF�b��E\�5s���{��u$�I�J�+@��I%)$�IJI$�R�I$���I%)$�IJI$�S��T�ʩ$�ꤗʩ$�ꤗʩ$�ꤗʩ$�ꤗʩ$�ꤗʩ$�ꤗʩ$�ꤗʩ$�ꤗʩ$�ꤗʩ$�����Photoshop 3.08BIM%8BIM:%printOutput	hardProofboolPstSboolInteenumInteClrmprintSixteenBitboolprinterNameTEXTBrother HL-1210W seriesprintProofSetupObjcProof Setup
proofSetupBltnenumbuiltinProof	proofCMYK8BIM;-printOutputOptionsCptnboolClbrboolRgsMboolCrnCboolCntCboolLblsboolNgtvboolEmlDboolIntrboolBckgObjcRGBCRd  doub@o�Grn doub@o�Bl  doub@o�BrdTUntF#RltBld UntF#RltRsltUntF#Pxl@R
vectorDataboolPgPsenumPgPsPgPCLeftUntF#RltTop UntF#RltScl UntF#Prc@YcropWhenPrintingboolcropRectBottomlongcropRectLeftlong
cropRectRightlongcropRectToplong8BIM�HH8BIM&?�8BIM
8BIM8BIM�	8BIM'
8BIM�H/fflff/ff���2Z5-8BIM�p��������������������������������������������������������������������������������������������8BIM8BIM8BIM08BIM-8BIM@@8BIM8BIMgZ�Wpassword_color_1500x1500�ZnullboundsObjcRct1Top longLeftlongBtomlongZRghtlong�slicesVlLsObjcslicesliceIDlonggroupIDlongoriginenumESliceOrigin
autoGeneratedTypeenum
ESliceTypeImg boundsObjcRct1Top longLeftlongBtomlongZRghtlong�urlTEXTnullTEXTMsgeTEXTaltTagTEXTcellTextIsHTMLboolcellTextTEXT	horzAlignenumESliceHorzAligndefault	vertAlignenumESliceVertAligndefaultbgColorTypeenumESliceBGColorTypeNone	topOutsetlong
leftOutsetlongbottomOutsetlongrightOutsetlong8BIM(?�8BIM8BIM
v�E��`
Z���Adobe_CM��Adobed����			



��E�"��
��?	
	
3!1AQa"q�2���B#$R�b34r��C%�S���cs5���&D�TdE£t6�U�e���u��F'������������Vfv�������7GWgw�������5!1AQaq"2����B#�R��3$b�r��CScs4�%���&5��D�T�dEU6te����u��F������������Vfv�������'7GWgw������?�T�I%)$�IJI$�R�I$���I%)$�IJI$�R�I$���I%)$�IO��T�@���Ļ3%�(�a��y4JJq��^vVp����du��X���N>&-��9Yoe����=5��8t����q���ȧ5�.����c�Χ���_��b]W��Z�x�9�~'H�άY�b�u��wӱ��]�I�;����5�P�k�i�ޣ����z���t����N��^��w�z��K��ci�DZ�����[uGe�'��۩������n��O�ΧGK�0����m��ժ�Ѳ���c�ۭ��*����]��6�_K�klii-;\�p�.U�Q�6�>(���IQ�Y���d���>���MW���_z��m��x?W��}o���en-}x�寺,�Ͳ��]��V����6Q1$�BQu�T擄z��g}`����s�G������Y��\?�VO�lf��q�H�.�8�9�۬�7����rj�?�e�g��*�����g����{Ԗ՞�n�#/��1��u�3�<�����}fs��c?�[N���rc[@n�ip���;RD��ȣ&�ݏc.��F���{^�j�>nV��dU�b�M�~���NĔ�%���mNV�Ǵ��k���¯�VdT�c��hg;�'g�II�C9
�q���m�ap�w}
��>����X�L6b�]�k�����as��IIR\�RϮ���Km��Y5d
ln��^�J�M�w5�߱�IO��U��7tL|Rb���l|���?s���,��� �����ò��w��R�n��{v�@ԁ=��\K��Z.��eی��)��+Ĭ���l��G�-X��`�r6t{,�[k���N�{����fu��fK��:�-F/����ȭ��w=�{�F�^wX���W����\J�!)k�E
�LJ����r��L���>../U����[z~.>W������K	�Ua��X�-���Ъs�KysA?0����g3��;�g��d���w2��;�}��E������_O�@�W���/����>��+"O
�xv�)p�?X]oJ���QqƧ�n�X'o�lw�3`�������e,\^����?�k�tn��b;sO�1+.����7����g���{mu5_S鹍��ZYeo�s\6��k��\�;pp��86�[�g���F��
��e���k�o�YU�y�jϵX��Lz}?�����ط!b}X���{��Ffgc���T��5�ۑ{u��YO�~��б��j�V�ji$V��\M������=%<��
0r:�]:��+�6�7�M6��V�^���Z����z?���T1*����q2sq���.n����U�^���
����j��c�~��k���+�Y]��teYL�O���[?�lk��~��bwO8�����o�w;�w�c�Y�IO!��*����&��+�ec�dtǾ�Q���ב�CX߳��{sjf?�+l��Q�����.�/�ŵ��&kc�i����F�]�=ޮE~��������\,j���$�S�~�,`
�(`�~�Ӝ��*1o󎦶V]�c[Z���uN��y�_V_vT
kK�}�Z�3���0R�=���7�5��Ұi���:�u[�WK?e��
/�����譽��}[��_b�N	��=f���dװm��m��66��7��M�Ӱ:�?ٳ��ʢAZƽ�����Ԕ�a�g��c���Ǭ�Ն��פ��~�V7��k��G���zKS�7A��]#���I�s��F]x�Ҭ�X���c�ϴ��+��u�.��`�fb}���TZk�c}6���W齻؏΅%<k�b}j��z%x�[sr�.��������vzw;��"��^��p�b`c��כ��c����J����J��T�I%8�k�K�׷2l��,�خ���A��s�}ꃾ�uKǥ���:�c��f���md�uI'	�uZa�4�OF�b��E\�5s���{��u$�I�J�+@��I%)$�IJI$�R�I$���I%)$�IJI$�S��T�ʩ$�ꤗʩ$�ꤗʩ$�ꤗʩ$�ꤗʩ$�ꤗʩ$�ꤗʩ$�ꤗʩ$�ꤗʩ$�ꤗʩ$���8BIM!SAdobe PhotoshopAdobe Photoshop CC8BIM��Nhttp://ns.adobe.com/xap/1.0/<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c140 79.160451, 2017/05/06-01:08:21        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmp:CreatorTool="Adobe Photoshop CC (Macintosh)" xmp:CreateDate="2021-12-02T08:39:01+02:00" xmp:ModifyDate="2021-12-02T08:52:12+02:00" xmp:MetadataDate="2021-12-02T08:52:12+02:00" dc:format="image/jpeg" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1" xmpMM:InstanceID="xmp.iid:b3bc9497-011d-4239-a2ba-f031a8498e0a" xmpMM:DocumentID="adobe:docid:photoshop:3e679cfa-9073-234f-9097-d797b380e679" xmpMM:OriginalDocumentID="xmp.did:1a11d4b0-93cb-481f-88a3-46b030a092c8"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:1a11d4b0-93cb-481f-88a3-46b030a092c8" stEvt:when="2021-12-02T08:39:01+02:00" stEvt:softwareAgent="Adobe Photoshop CC (Macintosh)"/> <rdf:li stEvt:action="converted" stEvt:parameters="from image/png to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:b3bc9497-011d-4239-a2ba-f031a8498e0a" stEvt:when="2021-12-02T08:52:12+02:00" stEvt:softwareAgent="Adobe Photoshop CC (Macintosh)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> </rdf:Description> </rdf:RDF> </x:xmpmeta>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 <?xpacket end="w"?>��XICC_PROFILEHLinomntrRGB XYZ �	1acspMSFTIEC sRGB���-HP  cprtP3desc�lwtpt�bkptrXYZgXYZ,bXYZ@dmndTpdmdd��vuedL�view�$lumi�meas$tech0rTRC<gTRC<bTRC<textCopyright (c) 1998 Hewlett-Packard CompanydescsRGB IEC61966-2.1sRGB IEC61966-2.1XYZ �Q�XYZ XYZ o�8��XYZ b����XYZ $����descIEC http://www.iec.chIEC http://www.iec.chdesc.IEC 61966-2.1 Default RGB colour space - sRGB.IEC 61966-2.1 Default RGB colour space - sRGBdesc,Reference Viewing Condition in IEC61966-2.1,Reference Viewing Condition in IEC61966-2.1view��_.���\�XYZ L	VPW�meas�sig CRT curv
#(-27;@EJOTY^chmrw|�������������������������
%+28>ELRY`gnu|����������������&/8AKT]gqz������������!-8COZfr~���������� -;HUcq~���������
+:IXgw��������'7HYj{�������+=Oat�������2FZn�������		%	:	O	d	y	�	�	�	�	�	�

'
=
T
j
�
�
�
�
�
�"9Qi������*C\u�����


&
@
Z
t
�
�
�
�
�.Id����	%A^z����	&Ca~����1Om����&Ed����#Cc����'Ij����4Vx���&Il����Ae����@e���� Ek���*Qw���;c���*R{���Gp���@j���>i���  A l � � �!!H!u!�!�!�"'"U"�"�"�#
#8#f#�#�#�$$M$|$�$�%	%8%h%�%�%�&'&W&�&�&�''I'z'�'�(
(?(q(�(�))8)k)�)�**5*h*�*�++6+i+�+�,,9,n,�,�--A-v-�-�..L.�.�.�/$/Z/�/�/�050l0�0�11J1�1�1�2*2c2�2�3
3F33�3�4+4e4�4�55M5�5�5�676r6�6�7$7`7�7�88P8�8�99B99�9�:6:t:�:�;-;k;�;�<'<e<�<�="=a=�=�> >`>�>�?!?a?�?�@#@d@�@�A)AjA�A�B0BrB�B�C:C}C�DDGD�D�EEUE�E�F"FgF�F�G5G{G�HHKH�H�IIcI�I�J7J}J�KKSK�K�L*LrL�MMJM�M�N%NnN�OOIO�O�P'PqP�QQPQ�Q�R1R|R�SS_S�S�TBT�T�U(UuU�VV\V�V�WDW�W�X/X}X�YYiY�ZZVZ�Z�[E[�[�\5\�\�]']x]�^^l^�__a_�``W`�`�aOa�a�bIb�b�cCc�c�d@d�d�e=e�e�f=f�f�g=g�g�h?h�h�iCi�i�jHj�j�kOk�k�lWl�mm`m�nnkn�ooxo�p+p�p�q:q�q�rKr�ss]s�ttpt�u(u�u�v>v�v�wVw�xxnx�y*y�y�zFz�{{c{�|!|�|�}A}�~~b~�#��G���
�k�͂0����W�������G����r�ׇ;����i�Ή3�����d�ʋ0�����c�ʍ1�����f�Ώ6����n�֑?����z��M��� ����_�ɖ4���
�u��L���$�����h�՛B��������d�Ҟ@��������i�ءG���&����v��V�ǥ8��������n��R�ĩ7�������u��\�ЭD���-�������u��`�ֲK�³8���%�������y��h��Y�ѹJ�º;���.���!������
�����z���p��g���_���X���Q���K���F���Aǿ�=ȼ�:ɹ�8ʷ�6˶�5̵�5͵�6ζ�7ϸ�9к�<Ѿ�?��D���I���N���U���\���d���l���v�ۀ�܊�ݖ�ޢ�)߯�6��D���S���c���s���
����2��F���[���p�����(��@���X���r�����4��P��m��������8��W��w����)���K��m����!Adobed����





		
			
��Z�"���0@ P`!
!1"2BAQRbr# @aq���0P`����3c�$�!01Qa@Aq��"Rb������9׹Y��_Az	^o��h�b�Uf���]G/g�Ȍ�pw=�8z�}>���tyޚ��:�/�/R��u϶˜�`8��O"�;7:�>�L����Ϛ�Wdv��Ȫ�J�Fe�q�r#}kFup��JU�m[��B��獞J�ǒ�q�������z�]Ҫ}p@���l1�+�]��9��^�����Od���nY^�[Qt��F�Ft��|c��#痨Z�g��#a������d�q�*7�y�(g��]�v�*�^>v�'�C��5�ˏ��A��9# δ���B��RCR�߮.���P�y�MG!�J��h����|'��=�K75���1T��Sg��ٻ�f4~�-"ǫ>زׄV����84���KY��]��p�@>	��%8��ͤڌ康�zL��t&�f��C���V��$����lz�ɬ�HS�1T��:8"��X��1��$��Ӝ�L�+@큑/D�J)֑ c#����o2���{wdEiC��+1����z���W�d�U�=<��/�x�;�n\/o���wA"z���?����7��?׾‰Y���˸`Zcvcx��+�bOF���S�D���q>��]V%L�@�&1�Per�.�ZD��j���ԕ1PZ�k��?׵ɠ�}D~j��\��bA�+�j/�L�pVT�+��q�*��h��cc��d���E������Kg��4Q�k�V�0\L�(��U�I$�}H`�R�'����?��k�[)V����m�Ы�c�t�f�Z��#�=y߻��|t�בe�A�$BX} ��nu>r�֯bJX8�1D�����V#��:5���,����/(_�2�j�[Q�q�+��u��:����#߅�"����k����}�U��v=��Go�M���Y���8�ܤ̇��l�)�,M���}&��0�͓�G��H�r����SƤ��B猩#�N8�IP��Ο�j�&�9FΟq�F�+�++���pv�E�!��W�q�1Y��/a������+Ս���У�ծ���[��m�;�#�^>=�����[��hԟ�e$�Τm�^_#k�Le�S�"kinT��%f��o��sY���e�E�1INԧ��f^K��x�����#�f�4{v���V}�>��GR&�1&5~߫ �M%���,��{�g$���u$g'8G)Wn�Kv�j2�}��i#�]��u����^$[u�=����|9	ח��
PȰQ�ʱ��}��X;|���Q��3j��m{2Y�b>!F^@��������͈����6௓ُ���2��"��*�d���`���ީvmb�԰A	����r<*�
��`�^�\�,�~ܢl�v�	�7��I�5ls#��$�Ñk��
�{����|0�RAԹ,�#���Y�H�C���4k#�eչ1�(��Ue8{}��6�%���2�T���Y
�LΦ�7/��M��HV5��}�9}]C���k4�F�DH���:����mcgƤ�l��ס4H�H���<�'��*�%�l��v#"��=���*�,�l�_N����[��DI5+��46"gD��3����j�Q��w�jH�Z(��R��8�����b<n����͛�c �S��T�ܣ�bn�b9,)���j�7��ģT��
2�l�#�=²�OUb�9�L�I���4ʻ���uR�adW�G���ݾڷ9i��C��c�W�j��aICs��X�m���98z��!*��`b���$�KX���6P�?n�u�H�%��R�k-{�W}Ï��=����4���~=4���pؑ(��S�N��o�/$^_Y
5�3؂H�v�H�chZX�^8�
w�@#}c-�b��ػKhа�c���uw�-`r9J4���I)�Z^r'8�n�eF�y5jll5󘻜JQ�"���
�V*VH�����V��Ǐ��lMq
Q��!c�0����[��Ь��ϛ�^s0���V@�H����Xt��#�9[�rپZ@�ws��ˬoQPT�_���	����@���p�g�լ�LC�2H��:y=���8���C/�κ�e�-f�K�ℓQ���!��q�����kFVP�0ܻ7a��:��R+3����N�ЙP��.��}�Z��]Ex���U����8��/?[��_ɩ�#g��ei���$
܏�;vӶ�������36�`���~_W�?��� ^�f*��;k�/��d������:��+�^n'�����x�*HCݜ��e��} �T���e�չ��5�F'�+k��۲B{�,v���vî8��e#g��)[�������dist/images/login-security.jpeg000064400000010636150755130600012610 0ustar00���JFIFHH��C		



          ��C

                                                 ��Z�����?!"1A2Qaq#$B��6t��4CRUrs��������2	!12A"Qq��3BCa�����4R��?�3P�P(
�@�P(
�@�P(
�@�P(
�@�P(
�@�P(
�@�P(
�@�P(
�@�P(
�@�P(
�@�PF_�[�0�v���V�a^V�>�	R��U�SZ��jp�<7��8ӌ����o+��N�%yW�ǁ������?�C�x?E㮴���h]5�ιaV�ӞT�o�T�'���`+��-6Ӊ����*��6�c�}!f���Jo8Ϭxd��̗{
�yq*�	O�T��lW*�~����ڭ�����F����9�/}h������:фeW�Y�����(���ix m]���::4��K&�K�^�E�b��1�(��F���S�[QRw���t�HlRU~��X��#͔��DWʈ
�h^��B��ju-˒�^k��8�)���S�\dEi߆e�t��Z���Ϛ��B"�}[�DŽ\1��A�\�j	3�v�!�qI�N�$���w��d��FtݤEWvR�$rRR��#���{�ޓ�=�7���Cռ2�m
���G��9
S)
k�JG%�RR=~|UM��p��O~�z��Lf䘌���W�����7�:P�Vw�ݒ��z����m����)OpD��ijH�JtT��oz���Y4�.w��1&��&B_�0�[);�x���?*���n��\�c�z�p¹l��$��B~֓�>�޴|{���Yy�Y�r����u��AP�)�ӊH�J}����T��I���V��@�P(����/�5�v�����O�?O�cu�ž�r���c��Y�žs������N����>�a���<u�1����O�׳;`�+��g�~{��մ������ɋ��鬵-�:v��l*�I~;J��b0ܫ��b�Jo앣���W\u�:i+�Y�Z��zn�QCO7���$������Sx��2���
�\e$pHH�b�9h*O���R�kk!��2�J�s�
U$$)+O%���8��Q_�v2�}�H���=���n���#���	PBJ�䝏p}�I��m�f�+�b��֜ev�� �sb��+�tx_���H��*��4㒓�-+��!H��hT@�����9s����ʢ<�>I��v�AǞ	�!qҤ�Z<���_ִ��-V�6���z��1�D�7��|����~jT���d�6<�ξڅ�5Ŧ���dBP�%�+m�P�%�������窽X�t}Yc��P�LVf�Iza�kpB�P�M��֝z	�}�H�Ԟ��/A��׷p����Q��u��璟f�:�P�#ƿTt�*��/�,��o.~�ܒ�	�=�O'87����񿟊�����3꺱
�9�nX����Ȭ7�i�������z������"�C�@�P(
o��`���;W���z'����=q��bw,~�p�.R_���JV�HR�	��~��.NՉ���~�k��ڑ�fc�h�\|9�]�m�0�u<T���r<���g^+��ҊWlt|���W[���Ѽ2�g�ZAv|;�l�$)
(S\�)�����3�)�y���
gE�pl}I�-pg��<`�IG��U��c�4�o��^m�Y��q��1J�C�Y-1˭�YCu���x�N�����U�N�ō^�O�mbA��9o[�$��t����Cڷ̽��ׯM����� ��@
+�5�2�a8��Je]l�&IO�δ��ȫ܏βke�㶻5����o����e�|{�<H^�\8�^�#�_���/X���#<���l6�%���$�i)Ju���zR~�#�k��~� ���[�bw���37��iJ�@KE��K�����m^\�s��6�Xe�Qg�&���q
�R��H磱�{R��Ͷ�5���7�?p�G��?�Wm*-��O��*�.v�e��\��2#�m�������&��,<͚�����l%JqW���l˚��"�8ϝc�"b�%��PT���ǫ��m�t��+-��N�Ę	�LW[J�F������3.{%��{��l�N�ql�����T��l��4Ɏ3�˽N�P��X�G��vN��7�uYi�q9�|f��s�<l���r4iIWszIRO}����mZj�P(
s��`���;W���z'����1��Y��^F_a�n�u+�(����~i�.�a��~��,���d�ڭΖEط�2A=��aC������Ļ�F����hF_�5���ښ�7��/|_���b�Jo썣���W\u�:i#o��&�m�>�ݜ�A�	�K������}G�JTtHH }��33��b2��z��a�Q�^�
�&�	�v�O�K�4�yJ$��ү ���Y��Mr���K�
~�Ӗ>�e2m�*([m���(%�'���}�dg�g�>�f,��5{����&�����š�+{ |�&ӵ������ur�|�f�z�Ęͥ%EN4�0[uŶ��y����fwa<��:E~�-�L�a�����qiy�)jl��%K%@)+���g8-��:��e���+3&Z������J���y�(oD'���3�4��f�[Z�̋���Z �z[6��y7)��v��NΕ�D{{R���B��P2K��U�Fer���C��a���x��o��?�F�jՓ�s0�c�����w{���-������E%g_ʪo<���ź����G~켓���$�sN4��%խͥ@{?}\E�$�����&At���ܮRf�/�S��ߡ���N��N��m�C���'�W���3�za�J��;��d�+{�Hު7N�c��zk�6��J��;�~���Oá�RU˖�������U9�Y�>�3x���׹�W����t�]�)��QQڜ�7��S~�����˺}~�e��N�[�b�\Xm—��ʖ<��髶k�G4��A�u�d�Mc�$�DV�RYZ�:RA����[�ٝ*�+��@���K��Ƅ�2fD������7�]����άF<��bv�xY�虋c��"��d6{wXNG�]�&�
Ӵ��SB��C�n;G^=�D�f��qgt��[a�}��W����
���?e����y�}!���7�_��Ӷ�j{�v+�HrE���'�Omŷ���z�Oζ!�ַY�n�͞���������v4��̂���mI��+�W=J�t��_H�edV��3�;l�C��8���m'���=$Y4��ػ��$�N�9>}93���HjP���ˈ����	W������"�rM��Ոqm�U�5��1�Ɲ�imMK��C}��Z�'�F���u_�Dŋ��e�z<�LL�#r��ۊ⒞)W�&�)��m�h�:e|g�j�U"9�R�d��!�?�?i;�^��g��n�Ճ���Wn����U�rKL���9�k`�'�>�ا��m���aٽ�t[�-�{�$!�d��C{�$(�K�
N�{j���2�
�'��׷+�_.3�.1݌b[��h��-G����	��S~�M��oK�ϏBDz��^p��$>�#珡jA:��U�-
�	��7K��c^V�Qd��^�nT�稧�O��}�U���d]��zg�����3��&?1!��o`
�TU�y4�Ns$�<�V��g�K��5�!j7u$I)R�դqR
B�QO ���9n�rD�.^��L�&&?tjsk��o��*l�xo��S���+�_�]�=*���y���	�:���(�9脕h����p��<�yWEo�~��Vv&���J��]�;��q��5驝>M��x�w�:HͿ4�E�,I����g�i�z��Nh���>�:�t�1���3�)��2����ҙg`+g���iG#R[vs(
��e���ͥ֗�m�$���mm19�R�[`�b"S+|�l��J*:�f����#��u��m֜˦�̠P(

|��t�]���̝�<y��}����ڿ�j6B����-2�e	m��Km��)��V���@�P(
�A~��[��$^���z/���|��b?:��KbR��-!�P��l��JR���c�@�P(
�@�P(
�@�P(
�@�P(
�@�P(
�@�P(
�@�P(
�@�P(
�@�P(
�@�P(
�@���dist/images/lastpass-logo.png000064400000021516150755130600012261 0ustar00�PNG


IHDRN�2y�zTXtRaw profile type exifxڭ�Y�9vE��
-3��L;��u."�"���j�f3������_�_��=g�K��j��ɖ-~��������}/����F����g����?>��a�����wb��(����d��$����!odߡV��ס����7���sX���o���(�ƒR�'���w��w�u����B���%�f����-�?~s�~��8�W�?Ų~c�/�F(z=�|~�������o��/���w�{�gv#W"Z���~܆�XN�c�������}_��H���O�V���u!�F�἟+,��㉍�1���k=�hq�O��
7�di�N�V<�4��%���{�
�'�1p��G����w�ܽj��2_�WT]3eN߹�����[y��M����(U2X^�;~~n1K�����s���O����g�5�j�-�q�$h0�r�d �7��9�]�=��|��wm,�F�6���jj��� Y9��N
��J.���Jw�ʨ��Zj��
�FK-��jk�7k���{鵷޻�a�X�Z�nfcD7x��^����8�̳�:����X��ʫ�������q�
L����8����SN=��cg\j�o���ۯ��3k߬�����Z�f-�L�3k��Z�q� 8)��9��P�Q9�=��9��[�)Jd�E�q;(c�0��
?s�G�Q�\�(o�˜S��s��y���m��z�t�b��w����0��s0M��Қ�Ӹxq��lڱ��Y�)u�	����?��q\-=��M�r_-��[Z���k.~�����C�n-�g��ܧQ#=�E��#�k��R϶�d���ҁ�M�s��{�uZs��� �N�+#��0�@�����;�ݚl�H�He*��"I�z�yYq#N#յP��g��+���
ө�H�3~ X?�g�t�ꍟ?gm+���D~�i��[����AS�4��3�z?;�S��FDb">���g���<��c�feeʀ�7�4���=�+�� s���-S��=���!�Qv�>�0~�3z[#X�*�QS�+٤O�Z�Z����$�Z�(�:w��2*��ym�0�8%QoPrm�7�s,�o���bȡ��2]�%���]I,�,�
ڜ�i6�T^'Ͳ�k�S���1�E�j�~�~�c���5ܟJ�<�~/|�����1rc<��Ź�ޱƩ��Zu�bn@E��a��{�b���������a��ژA�L��p1'�u]�P�Z"<��Tzkq�����Sm��y��;�>�s\k2�\����g�9Ξ{��v8���so������;0�*��g���2�f�(Ԍ1#�:h~�p��/��.m:j+�
�4��k��kHCeD�k<��~�@H&��=A*���7w�62�˹�F?�����NC���Gz=ok'#�F=�ILW�ɂ����[�&�w^7B g:���s͟��X����	����nb��O�k}��XE�8�W�s��=���MM��B|m���B*`n8����<��H��r�	az:�Ҧ;6�=	lF���$���ג���0�Hu@r#�^cY�~�%�r
w�d�"Gwɴ歝���g��ƚ:�.��B~�\��>�ޭ�N=����,DT_A��e%�G�k�m]�èT-Mup�'���� ��G����qs���(8�
�H)8�Y�f��:�>¤TF<�T��8/L�6�( @��<{�	�S�%���泇M�ڦ�&��W�;°�bU����
1�[78qh!� "=��5"�K�@�T�΃h��T����n,~���%�M9Q�3r�Iy��op~/��{*(�Y)ڴ(��^rΉ�����R"���^*�gwә����-��$3�<��`P�@a�[��ʼ�Cg��0�1����{���ܐ�R�mQ��	z
���|��!p��$FR7W�(ډ���!"�S_Y�*`�mA;��TF�[�L��)�_N� (,�
zҜ
���݃8}���C	b�(ޑZ�З�Ŵ7���}�Dx����(qT�n�Ϋ�3C!�ϲ�*Kr���&�6��K��\1�K����Ç�k
s…$
�SS�Ӳtbc�̋��~]F��B�aE� 9�	�BOg�
;̆F=hc�'zrϞ�ы3�s/�]w���:�	�6���mY’���9!��z�[%�2%�G.k!�>k�Δ�@�AVy��L)��	�⇲n��E�� ��v�F
O�Cz��CK�ӷ{U�J;��,�@zz8uҲ@����=A� �\�C�Oqt�S���s�"o�B�#�f�v�X2
��K��	��m">2U�d$6�����Mj�2������� �l��JYS��F]�/0f��z��+*Ϥ�3�z��i�� i��C@1���@b�}Q�ڂ�"�b@qd���J3p�vM��Cn��b�J��MD9���6I�	J��[�b��&M9���f��cqin�O�Ѕ�)a��J���\i���d�߾E&�ND��7#ںK
sЌ�AH:R
����~"t�K>��h_5¨o���*��
˼P)H�\b
)!S�5���C~�H\�Ux!��K���������UW�~Z���i�Q������
DfW���wGh=cR�\�M�C���b��E[���x�,�H+��ҰiwYB��@�v2��$�1�(/��8�F�����|ܶ�2l^���X=��`�ne�M�
z4t'��X/O�^$^d��#�	��M+�?�<�Ja�:�5	:H�jx5��)D�3E�ia�Zv|��#.���?Bs*�P��<ދ�A�h9d�����"e@3ÛK�#Ǹs�I���.�d�@ �u���CҔ���is�B�0��£INCץ��M���܁�|�F
0�F(3Mہ=�3��r�EL�U�`�A��j1���Pg�E��z�I������v)iwP7�j
�`٣̪4h[,�
�.Lk�4�Gp�q*�Ρ��B$(�����B�R5���X�z�dtv@�by�[�؁g��DI5"
�@05Iݻ��<�cc�$���܀YxU�s͞�p�RظNث�-�Ѩ���%��"���|�P��l�5�C�=�[vc�o���hF���)��)*f0��n�صx	
�)�Q��<��?��&S}z�$�>��u
��V�<"�(/׊8K�'�I�D���Ǻd	"��Z�$^4,Hyd
��CJ�4K�s]fc�FZG=!Z�4e5�4B��+I�IҨ�K�4��%ۏ�l.$�%���`���1QwI��I�4ʧ��7��=����A-wS�N
=sP����4\!�
��ek��*B'��,�P;��$ҏ�RZ;�Q�X�ϴl� ��A����6L�Q�zc�������(�;��P<"��a�֩���k��AkvJ�i�aJN`E�QU�D���AKj�~I���P�8g�1�
�C1�Ba�Yxڇ�q$���R��y)D�B%��f]�$1k�#/�Ʋ����x����ɲ\�Rc�C��C�Zo���P���HQp�r-�Q.����A�=�2
āJ�o2}�t0��������qW���G 8��``z-��:S+J�4�GŅ����IPA��A5Dj����pg��DLQ%l.
[�X��{@��y�_>}��P<n���Q�T^Ҭ=)�Қp���xF�����6"���!�o0���A��$�q�C����M�"�,R�P�j�0Kwb��^��Ё����R՜��Ϙ�?0�E����Q/5�)�b�\�Qk�$O�c{��aW�=,��7`�71��P<@���y>hvF���X"G?¸��<^�k�j!�Í��2�(�����EA���8
����k@�V_w)Nh�"�!�1nڼ��~
�6�ۘxƄ&�{��j��ሌ�����h򁤌KEO2z�*�!،��5�qAADV��!��%�{�r��h������'2�8��Z�T�>��wAm��=��]8T�IG���
�Em6�#&�՛/��Ci*ۼ����p�A.W��A;�n&YC\�g�[.��L���ɽh��m�z:c�JG8⠿P
{�v] -�&
mµ���}b��>�X�
r����M�`��q����r$�[��P�9s�!J�ޠ��8�⽩�]���r�1.��p�4��%��l4����g�[�[{���Z�5�b��>+��"��e����0_�]NKb�p9E 
,��W��<p������6|'��g"�P��0)E�`S�V�-��C��膦,T�yCr4�#2�	��8��ߊD=�|bC��'�A�.`R���q�,��As2ˋ�ߑ��JkhRD�浥[v-O��ix0�!���N/#�F������X���D�.*jBM0�V�ݛ����P�j-h��"����S�f#�3
�[� 
Y�����p�*�V�6^�*!R�	�)�Z�1:DB�?�^c&��:�X���"K�Cdm!�H�|�dF�I��O����85�V(1� $(G�x$�3�d�c��jkr�@)�)-p������y������[��$R����hC�x��O*pKƓ�C���(���\�_[l#G�hx�nh4-�Ь��[�B| U��[r	��8�!�Ə�̸h΢�8�}h	o�"$��*F`��n��|��	�H�-à��E����u�
G����վc�[��%��E{T�{}AWl���]%=(�T��k������P�S�Q8\��2f	W�����W�R�̰ji^I���$��
�n���W״CnDžJC �{>�_�0w
�3������g�1zd�c�M��
Y���Pþ��D`��yު��c����'��<$��fE X�#��v��-��"qa��*�#2Em�Hz����u���Rjw}�)�J��jO�]���"��%�"{�y[�Id�]�K�-N��������ע�-!�V��ϥ��U�}��TH\��9����A�⦏6X�}0\�\�M�0(c��	-�2t`o�}��u�_juj�o_�k[�%`����ty.�BH��M�Q�4��T/ 
����%[�K.s��N�D�Q�Ih6f�j	ذҠf����~�2T��
pfş��a0��s�����.��9�|exΈ�ы<5�ɦ�c�3���"��iób
�#����H�1T��zJ�;��~�J�%�?�zU6h@����caa��4�5�<��[���Fw�i"�^�F�un���P�|�gëEdzV��N���M9B�ɳ�����<|�PsDx�z��W��-N���؇��C[�T�vb�x�.^R����`h٢Qg\�}F�1�/./�Ō�|3�+x[s�B�@��鵡��Z�B��
	0P��Y]B�q"(m4z;iw~
��Ҫ�A11H��6m�R�pX*Z?@Q	���FZ��o-�f���	� �V1D�5�"I?x|;V(}G����w�p�JԨҋ�3瞒3�<j�0��jZ��
�p���^���~�N���j��1	&}�e.�f��|�$fEiZ����N*�8�pv����n�J��`�WxKM�Q�DBړ��Q�X`���y
��щ)c�*�%��45��A�ˑ����㇮_�"�[�/�)Cڍ�y��&��w��֒��C�R�W���/-4UF�)�Cr�!�>�}����ց��HH/�tu$i�,zS�f�R
kY�|n��&I�0Fv�(Vm6�A�d��ݝђ�ij�a�
���R|Cѹ�[�o�6�	�V��&	[?;���y����L��l�8��M��N�(�����3������<��5M���Z���~�H+H�*���Lچ�Gk8��Z���uo2��!o�jT�)���^�b6����:" �KF�5u-{ صm��*�R��*&��Ĕ��֋����E��A�|�N*O���&�X#�
1����"ؖ�΢!��@���
x���7T���l���x�	0E���JH"�;�,�����A�AZ����p$$�[�߬�Cm�x��::9
e͢�z�A��`;o��u�c>�΅�A+8��E/��'�	굳/s~�R#���I���-�s�?a�B��n�.�'�smܘ��L-�0�ᵎE��(}��Z�sg*N�|�ͻ��~�����vD4�ez��VB��yf���`�d�;#jX�0���B���6�W80#�O�҇��Σ�u*�k�o ��~Tz��Q�5@��CF䶶����T�4�"�!!]>!�U����j���F���ӄOH��t%bf^�@�pGP�-� L�|i7��
��mY��_:�H�v���³�3���-��F��_�#2_f*h#p�k��I*S�ј��q�T��F���>#�$�~�ȍ��?�P�:N���Z�l�h���A���l|�Ŷ�II�_��ᜑ7����N�E�@6M�uZt��%F!;�5@�Gd�ߢ��tq�F���>���'�[�f�.�G��hߎ��٨���\�(/H�����Ol��紬umW��m��L`1�rw�:�×�@�pO#jM��n!.68R?��U��ց�o%�Z1h:q#�����/	PĔ�_�c2��<QD��С�>��ک���:����r�i�:a��B�$5�'���?��0�_;���x��,!"e���MZ��P�G�W�'A֠ݦ���Q�/�{F�c��;��GV����}�xWB�Ǧ�G*�ńx��t�Pm1�"k��5�Ո����/كqD9ݝ�-�A�d�[DP#\RU���	�*��Zf���ӻo���9�A�}P�6��	�Y�CG������N����M�^�#�1��V�R�@,���;:dͳ=��}�?�����:0J��/�U_�~��Iv�6���70��S.��2��XU���
�2ɯ$H�\����cPt�Bfƃm���K�IG��qQ���p(}�T�<<9h�ө'�ߩ��VҠ&�-v{^�]׮66��r�I�;�j���]�-��P�%�H�i\<��ؑm!6� ��>����C+�:M$��*2M��wh7'Bx�XQ�H�$�E�FMP���ɧ�"|���?/N?>�O/̟ג���u�V���o�����ѳ�fb��b��{��vC�'��6���t.�iCCPICC profile(�}�=H�@�_SE)U���d�NDE�
E�j�VL.��&
I����Zp�c���⬫�� ~�89:)�H��K
-b=8�ǻ{��w�P-2�j4�6���J���`��E��,cN��h9�����]�g�>���R3|"�,3L�x�xz�68��X^V�ω�L� �#���8�\xf�L&�C�b���&fyS#�"��N�B�c��g�Xf�{�3��2�i!�E,A�eP���:)�m�t��r�ȱ�4Ȯ�~wke''��`hq���c�U��qj'�����R��$����G@�6pq�Д=�rx2dSv%?M!�����@�-X�z����HRW����Q�z�ww6���z?R�r����.�PLTE�-(����"�,%�������� ����%����:5�HC������(!�)#��������zv���ꜙ�����@;�����間�OJ��SN�������ﮫ獋�ie�XSﴲ蒏�uq�ea�a]�]Y�qm�nj���롟�}��E?扆僀�<6���允����bKGD�H	pHYs��tIME�
�[0��IDATHǵ����(������,Y�9�I�`��;��jk�f)ӈ>�֏���h��pZ��$.�/��i��"������	�i��x���p)�BT(��r~
��a��!���b�H9
���7�
�/f��Y����Z��9�`��҃��v7��=�;�5;����'�X'�M�'���6A2�K�so|�"��)J��,r|�b���ΐ�qL��X.��`�1Sљ��Z��m@:2�x�X��)ԓ����2�g�� �T�<d	�kj[Q�TX.%��s��Z߼�fb���;.�Vh+��#��|����8�!F�ۘb��*8Kf����6�>F���X�u�'^�kl:lj���{�uc��u�T�}��|۾�9�Q��kn˲��$]y؅Uj�pq\���2�y�q��u9�X���)b�LS%H)�&�������3
���Y�Վ��K���tm
��1��Ȏ��W��f����8�$�
��L��L.u�4�b.|�}��h��2��M�7��W����*��0��[C]��nP
c�:�eoN�椖��d4�<4öt���V�](t7��{B$����O�-�	9a��%��Ɔ�c��V���P��1pX���gm�x��/�r�i��YIQbUlHf�ə5���Y�����|�a��
Q6���X�873�æ"�z�h�����[
t7F蠽�rӯ�]�h����n��Xv#ě�D������Y����,&
7q	w�>�¢�����n�j�{)�'�'[�h�-1s:����7b�i����^�Pb���z����[�j�7'p�n3�{=�8Ͱ^q�A'a�O�d�{UBX����j�zW�m��4r�LRZ�?�;�;��@�k��+��W^IEND�B`�dist/images/authy-logo.png000064400000013442150755130600011560 0ustar00�PNG


IHDR[Nw�#zTXtRaw profile type exifxڭ�i�#9�F��)�$��8A��
��RfeVU�t�M�"�p��`��9��?��?E�<���fk��2˔ŋ�>?�Sy��?վ��߯?�甄Kʳ~���{��z��?����g|ߑ�(���ј9^����|���h�ϋ6G�u��ho|��-?��y����.t�䕉T�h��_��
���:U㾬�ע���	K���`�5@��ǫ�������ޡ�e�ƈ�F���?�_'֟+����3ݿl�{��{�gw�4"ھ�;��)������o�u��H+mR�i'���B��K�����}�y��"��O"�E�kC�L���|��T�Aζ��4��k���o�̞�U2��H�z<�����ܻ#D9�I��'�u�2"s�HH�߼�7�?��_
�R%��
�`�+�g�����7��}��O�wB�ܕ�d%�e����Ez��q����E�ȵ��H)�M�.Cbn>��{�Ti��&Q�i'7S�*�R?�jhU����j�㩳�����Z�-@nu���z�Ͼ��2�h��1�XS���u���s�%�b��X��WLL�X�f݆M[���e��v�cϽ\\���݇O_'?�8���N?�̳.�v��[o���;��oV��/���Y�7Sq_��5�>��"������x�P�9K#�"���Y�BSTa�5r�x����r�ԛ���Gy{��&�W�H�G�R�׼�M�<xn��ta�4)���ֳ��������|Yw��w�u�}�y;��Yv��K��+t#��}�6�w��:��e��O�� Ƿ.�`��ܿ	ti�w�|�ɤIF9�H">����.��b��$��/j\\��A̎��g�g���1׹4��l��(/cۇx�/�e�e���t���ֳW;g<�;��n���DH��:L��ԓZ�u��-G�Bǻ;ɱc,�[�}ԇU�]��K�) ��P4|A���mBq��v_�Glz�u�;��#��w�0�ڵ�~SSj��[ך�_�qF����[��h�I+�&��g�R.�D�K�#e�6�'���Vu��<ԤIKH����G�̛��1O�\<k̵����:�n���J�1X;���j�s�����<���>r���Q�/���6��2 ����mƭ�l���YwϏ�N�I�KGY^j�
W�a��*-6���������d[�#�6��+����e��|/6|�R|�9��(N���m�l�I�fyN�x!"+�Dr�ۡ�I�����w�m�d���.	fUmů���������ه-w"�@�{s��l��k
�0�2�	��i����[���-��
}�����H	�C��БݚR�;bs�|{Xy�];+�m�
9��J����\퓙�6#������<t?(mU��F��zt0s� z��Y-Hm�����N�UN����|?h�s�έث�y��!�'uYR�'72�.��U�yP���?�L쇎��yj=�n>gʘZ;>M���K<{�05��BOڴ�Qk��I�&��
�U�"}� ��R��@o�R�D�%��QVIuͫ0wc�����h����fŷ���5�4�
�����Ghg��FC�48�ꐤ�/����Y��Y��^ow��̹�#w����U�� 3ɠ+�a���w���e�"q�B>�V�:�y���ʀ �/�~PeNu��*�U,�V�р�MW��[�HAzzZYfT%��'�!�D�R;�˝!�E��
0��f�
m�jpWi�J��2�p>�si����+���&���*�@��`i�9��J�ؤ��
3TaXH��A%>괡*X|$��J�n%kPi���0�<rz�O��a�CN���C�.��d�6fEG
�:.;��b��5�VG�)
-���S�� ��yvOf��\��i�M��%���K�MVT�
4�`�j����p]�d����*��-L߄6�b�:n#<���H:z'�ý��H*-��H*J�O���	�ÓdHz �@�P�[�mZD,u���|�=�YQfN���vZ��$�F�'�
�������#2�A��/хw�H����-��$��Fs �6��7�bd�А+�j�H�Q� -�����?��)Y����%Z�&����f�~)V����U�3j�MP�P
�C[��"e����ߩ��D�k�R�^%�T�L����!�~�%�i��.��V��� ț s��1��YB�
z��]yA_�__��	��+z}�WʚD��A񇅘��
��gi:}:#;���,X�+�EJ��
����~���<�f�̙=C��>	��$�T�T�H�^��4h(6	�$�AZ`F�A8�|��o����Jw���i�]j�e�3���A.����0N�n�n�pv����26B6$�p �F�����|�!��!�UR�CFa�=Odo�S�C�(K\A�c�}��戳	�
*�V��Q����r�-x��!S)]ȇZh	�c<�Pp�"���v��ጄ��'�o�
j���
��/�P���t�	V����D��FAن�9�.�ξ����t�R�t:���� �4QgLʞƅ(�B�8p]�0�yB)�y���|Ѯ�]߹����N����T�y+w�
�Ïf����ᅛ�q���-J�mp�[�>!*�A*�SFPe%�b�<C��_�p�	(^
v�4AE�>�!�{ ���PA�!:h$I���`�q��:�g(�o�!܃pb6l&酂�<+"�
���b	�x��)X�(ڥ�$4�X��Q��H�lū�03��Aqt�;,ԑ�=B�%EA;\�%b>�k���7�����'��:�Z�ڽ��,�6�]��52��K
�D���NNJᖢ[�r����PKh0BF��Lk��e���L4?UἻ�M!�,�I((�ѣ<i�|���vP�x��ۯ?�3�`�l8�k�u�7Lsַ`�xv�Ռ{�N�P��}��s�	#C.4��Ũ�~�C�70di�	��M�~Y?��m��`�4�4��#���bA�>�;�GP�c����
��3��@Hs�:\��FI�zZ��������P6N�a!wL't��ua�3�T&��o�{#��:Ӊ�����k���l��ʤwcIJ��q�~��@�$���XX��M�t�tnh(D���"O����5Һ:��qz�L����}:PK[&���3��E�T^��\��x,$6����b!��� fAG�l�oZ�����,�a���z���l
	R(D�
ܖ�:3���3��#d��E+S)ֆ+0�$��2AD4t�H�E���5���#�'��$ �r�?�O,C�ú�4�R�h?f��}x��$ix�]CciY��􍛹�E軣8�S����}34��]|h�i4^�
C[�N�F��k��4�Ja����: FǮ��o��s�m�V�v|�F	`P�;v���Q0Q�z�j�滎���'���4f��(��y�hE`
o�\���!]Y���
S����'v��dm��P�5NlZ��W���Р'�mQ"��S�]�x�P��,����G��nC�~��&F�Ӊ
]���F��85yn%�\|�/0+\`�6^� �%_ p��s��5�P\�]�7>���(�4�p�T�� eQd8ދ�_a]й�&�/	=$j�Ԩ�	r9���;�g
Q�0q��n�T{��!��"P�ۥX!u��@��*��qd^&�Tz�7\R�M��8�l���#�;:$1S$�B��9Ea/��FX@1�$AO��8��<3��)���P�$@��`��O�8�R�5;��-�Fx	���L_��g}PKD�FD��
3��ٔ�ė;5��,)�8.���@o>����a��L�p��<	�|齂Dq���Uj3��Y%jC�B4hP�8�z�6Mrڅ|��q�N�,:�����Z
<*��ڡ�Dt���9�!�Y��q&z�n��:P
�0��^����F �3QG���q��tރ�̹�b��r���h��濞{!_ ���
��7�hE|��A vH��G�U�J0��-��% pC"(����g��
{�\47(��H��َ���{���Vq��f`�U��~
�O!1���J�G6b��RȔ��S�
�V�=�{����!O�9�v���Q�]��M����^-N<Z�
,��a���q�)��hD�Qj�}1`apt)��qd}4TO|?@�(lQ�G|w_����/�.!�Ǜ}Еj�K��J�����)kL_��ʲ�!������N��R"�8]dj�c�wKA��Q�G�x���jc'�:C�c�|_}
�5�����aa�`{�oE���#(��!��Fz�i�a��jKo�/��
�5�B�[��1'�5|��H�W�%@" ��Y�9�=�O!X�h}�e`�iCCPICC profile(�}�=H�@�_SE)U���d�NDE�
E�j�VL.��&
I����Zp�c���⬫�� ~�89:)�H��K
-b=8�ǻ{��w�P-2�j4�6���J���`��E��,cN��h9�����]�g�>���R3|"�,3L�x�xz�68��X^V�ω�L� �#���8�\xf�L&�C�b���&fyS#�"��N�B�c��g�Xf�{�3��2�i!�E,A�eP���:)�m�t��r�ȱ�4Ȯ�~wke''��`hq���c�U��qj'�����R��$����G@�6pq�Д=�rx2dSv%?M!�����@�-X�z����HRW����Q�z�ww6���z?R�r����.�PLTE!����\bf=DI���dimMTY�$%���~��7>D�33�
�������������������sx}puzJQU+29�������������������hnr^ei�ffW]aFMRBHN08=������̶��������{��msvQX\�UW�����������������y�����y}�w{��tt�HJ�CD��������ۯ������������}~�z|jqu�LM�:<����ɟ�����<BI$��QtRNS@��fbKGD�H	pHYs��tIME�
�vcr�IDAT8˵�g{�0�(�f		4(e�.��zݽ��?�9r}n���d���P�;
p�������V�uY����򗽢��0F�i���Y���L%w|N����_4]���M��ZU�Sď�哌�D�۬,V�k$���Er����J��,�	At���]�f��n9m�E�"�{�;����\�(]=�H"A:f����W��aVc"�<�F�%b3��n=��?٢���Kؠj����!2��F�"��I��ww��=�[���ӥnnTn|G����ytB��`V�f~�m�G)���W��np�G��ۈ5dݧ�ϱ�n�V�jRD��]���3�[��_!~�pj�2�G�|}A�Z��\�L���2c��!I�?xp��Z{h�G�����R\�iډ�5'egk��7�'�7s�n�/����~s�����ȚB�p�������2zy�O���!%��w��7�+�#k/IEND�B`�dist/images/website-file-changes-monitor.jpg000064400000011411150755130600015130 0ustar00���JFIFHH��LExifMM*�i��ҠZ��8Photoshop 3.08BIM8BIM%��ُ��	��B~��Z�"��	
���}!1AQa"q2���#B��R��$3br�	
%&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz������������������������������������������������������������������������	
���w!1AQaq"2�B����	#3R�br�
$4�%�&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz�����������������������������������������������������������������������C







��C

����?����+��P��(��(��(��(��(��(��(��(��(��(��(��(��(�����+��P��(�$�?����%����m�!ǙY�y�V�9�#����B�O��
��x�����n�6����<��j���í�$ҵ�m�5�1��
�8�~wސd瞜W�_;�aRmo}�=O�ɸgT�\AVT�S�U��Y����]֋_�����������O�/��
�s�
���7Wl��-���u۽W$p9�5��i��W�9��s� �Ğ[BF6�� ��s_{_�ũ�x"�������'���v'B�?u�ׯ�,6a��v��-�����Y���X�ө���O��2J�JV��|�鳷��W��>?������N���A���Քvq�K��2ԕ�ѭ�S�?,�r�FY��I}�ti�O�
(��<�:�(����sK@Q@��8�JZ(��(��(��(�������+��P��~=�{��o��g��7�(�"#�
�f�y����W��s�y��3�u/��ڒ(�
Α��������c�����*�x�S �7'/bJնI�g�NGlW���)TXZnͫ�����>ۇ��=N ���F��c�{:����g������� �f�G�fK�z�[��[+���]����Ǔ���߉~:�v|?�?�Wk'��^4S�iw"�D�:,L۾�W���Rx�◎�,|4����x�]Z�
���Etxo b�PQN� ����K���;o�/U�Wq�_[x�
�[Ic<f�A81�,��#0"mc���x�F�zʚ�o4���<�3�2��������E���-�Mϼ�|n��w�3�˧x�L3@�p�Ԏ3�ud�K�q�e|i�O����|��Z�K�G&��i���>�crS�+����7=_�7�<g�?	��-6];T��4�"�b��Pp3��|��pp&��N_W��g����9�a1�휭����X7��x>���nO����`_�>A�x�MH�C����ta���t<tOٟ����9��v2�Ɩ��ȣ�s)�1±�̽3���>(x�㏉��G�C�u=Mr#������H����?T|.�]Ꮔ���~�'��v̞Lr��������L>��ٿ/N筙?�dp��>�vӥ�Ny�i}�=V�t���������q*]��S��Mr�JEzspA�
�v��&�����4	��K4��ҼՑ�H,�a�Pą�"�����o������Lխ�u�HJ[#;3ym�h�@��ʵ4};�Eߌ[Z�� �������xU|��AP0W~H$��X
��R�����mK�2�b�U�ܹSoK�N_d����/�1��Oxc]�u�#K�
��	���y`v��3"��y�nxks��o���|%���G���W�1��������J���ޅ��k�t?�Т���K��l���nn�Q��k�?pc�R=z�
~Ο
�1�&�n�W^L[A���	*�����9 �S�MԜ�����F#9�]:�)Q|�T���i4��-�-��O��?j�����2���5�\A2��2�Wʜ�v�z�m~%�@��ōq�u��?�h�xm�W%`T- a̮@V���������;Q�n���u+x��.I#��ĉl~�Q�=+���E��C�Q���9~�-
��$")�*Sl��|8�aDp��iJZ�y�U�2H��(�|��kk�{{�6�%�k�tx��/��^<�e���i�$�����q���p�c4j�J�d$g�����T��%ү�SZ�th�[m6f����ca�8��_Lx+௃������{<�D�[%����LrRnq�}�&��w�> ���W�a�k1��ler_�0E\cv�9>�V�R��W���g�nS��q��qKU�7�;�,����|E}3K�<[�i6��[X�E�i�y�����Pɝ����5��4��u��Z���i���h�	m<l'73�@��cCm��'��z'�_|�^�[��{��I䵳vyYD�[��	)��]�>x�~!��Wf�v�%ݼ��G��4k����֡�k�ɥ�/�Gd8�'�*q�i�vZ�q��ھ��K��ۯb�U��A�`^y�)�*/���	�B�_�tkx[Q����Y!�NS̒"U�U± ���g�z����Q@Q@Q@�����+��P���1��'�<H~8��I���1��.ʃ�=]?�K����#��T�c%Ϋeu��8�iE�G�k�����x���"x���g�4�9đH9�r�1�r�����C�y�F�_�>Z���jE҃�d`���g+��r9��x�Ꮓ�������K�n,�n'�L��ۗY��3�������,�7�~����񭑛Nմi�0���v��v�
`�p8�N�\q�W���nm�i/џQ[=�k'��rxv��ڼ�����/���\���?����g[-gN�5�ٱ�n��{�w�m��؆:�%�$��>6�8���|�k�]Cw�B�}J�[:�]#�0,	��2T��Y����ǟ�7�����e�O�x�k�������ȩؑ�_�%���	����ρa�W���=�ہ�]M�$#�eQŽr}J�R�����`�S�Z��r4ӵ�Z�����~|.���o���
ǒp��y�Iݘ�!��ETB�T ��ӊ�U�U�#')Iݷ�g�
�]����������4�r�S70\j%c�f5,RI�ʀ0*^�y��
�����K�C�ZE��$���%���l%�E+�
����辿�������K�Լ����sl?���h�H�b�-�4p>��0�2���V���oG�������:���
i���o`�R�V�ym��������k�����1�Y��Z�^>���'M���.����]{e�«5�I/�	
�>u\��b�H��	�{�m��4kc�v�{x�bw;��³I$�Wm�+�8�+M6�%x~�!E��)�zrx�̻�%�G�g��9��W���<[�9�5���5-h�٩��[y�1ލ�~��az�&�o|q��~����K�ҵY���nf��Ye��'���L�CX�1���������k���z��a�ڥ�텲��4(�0 �}��%XŌ���|7��f�q�Z\ţE6^|)+B�(U�	r(�D��ݟ�� Y�fҧ��$��Y�{sg��n����M
͔ʳ�%v%f������zG��e������S��栰�	&K'x�#�ȪM}�s�?
�kqx��L��R�m���F���F�ӹ�t�˘//-�kV-�hن�P��$pH���=�i�Os���/�<E�@xz��"�[H�
�f�<n6��no"K��pBF���Z�gZ���gռv<e,%����"^\���bX�|�g���Ǧ;~���<6��Y��la����bM�6A�c��y��QI�
K���e�-SA�n�(g���q֟1>�[���?�~+��i�.|W{4R��<t�#k�).Z,p���+8ָ}K]�f����.�]֖��j��:�"�"�r#��M��@�'���I5��<7�Յm���F���/���w�1�g��s�j<%�Yt�ѥ�-�$�V
��-�&ݡ�I�3��9��7����/[K�x�|c,&�<E��g�"�n.%��W�v�2_���I�}��x��^#��Ν�뺆�i���CFc`��C�Pv��ϭ}#/�|)>��)�L�}N1�n�3��`I�ÏzԲ�t�7�u�v�|�4�Z�#}�l�8'�I��I�v˴QEI�QE�����+��P��(����+�	���u'��g-Y<1>��[�<0���dA9�-"8V<�s��ERmlEJq����	����ρa�_���=�ہ�]M��vU(�w'�h��wܨ�EYQHaEPEPEPEPEPEPEPEPEP�����+��P��(��(��(��(��(��(��(��(��(��(��(��(��(��license.txt000064400000107046150755130600006741 0ustar00                    GNU GENERAL PUBLIC LICENSE
                       Version 3, 29 June 2007

 Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The GNU General Public License is a free, copyleft license for
software and other kinds of works.

  The licenses for most software and other practical works are designed
to take away your freedom to share and change the works.  By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.  We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors.  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.

  To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights.  Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received.  You must make sure that they, too, receive
or can get the source code.  And you must show them these terms so they
know their rights.

  Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.

  For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software.  For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.

  Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so.  This is fundamentally incompatible with the aim of
protecting users' freedom to change the software.  The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable.  Therefore, we
have designed this version of the GPL to prohibit the practice for those
products.  If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.

  Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary.  To prevent this, the GPL assures that
patents cannot be used to render the program non-free.

  The precise terms and conditions for copying, distribution and
modification follow.

                       TERMS AND CONDITIONS

  0. Definitions.

  "This License" refers to version 3 of the GNU General Public License.

  "Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.

  "The Program" refers to any copyrightable work licensed under this
License.  Each licensee is addressed as "you".  "Licensees" and
"recipients" may be individuals or organizations.

  To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy.  The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.

  A "covered work" means either the unmodified Program or a work based
on the Program.

  To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy.  Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.

  To "convey" a work means any kind of propagation that enables other
parties to make or receive copies.  Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.

  An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License.  If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.

  1. Source Code.

  The "source code" for a work means the preferred form of the work
for making modifications to it.  "Object code" means any non-source
form of a work.

  A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.

  The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form.  A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.

  The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities.  However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work.  For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.

  The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.

  The Corresponding Source for a work in source code form is that
same work.

  2. Basic Permissions.

  All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met.  This License explicitly affirms your unlimited
permission to run the unmodified Program.  The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work.  This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.

  You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force.  You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright.  Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.

  Conveying under any other circumstances is permitted solely under
the conditions stated below.  Sublicensing is not allowed; section 10
makes it unnecessary.

  3. Protecting Users' Legal Rights From Anti-Circumvention Law.

  No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.

  When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.

  4. Conveying Verbatim Copies.

  You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.

  You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.

  5. Conveying Modified Source Versions.

  You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:

    a) The work must carry prominent notices stating that you modified
    it, and giving a relevant date.

    b) The work must carry prominent notices stating that it is
    released under this License and any conditions added under section
    7.  This requirement modifies the requirement in section 4 to
    "keep intact all notices".

    c) You must license the entire work, as a whole, under this
    License to anyone who comes into possession of a copy.  This
    License will therefore apply, along with any applicable section 7
    additional terms, to the whole of the work, and all its parts,
    regardless of how they are packaged.  This License gives no
    permission to license the work in any other way, but it does not
    invalidate such permission if you have separately received it.

    d) If the work has interactive user interfaces, each must display
    Appropriate Legal Notices; however, if the Program has interactive
    interfaces that do not display Appropriate Legal Notices, your
    work need not make them do so.

  A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit.  Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.

  6. Conveying Non-Source Forms.

  You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:

    a) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by the
    Corresponding Source fixed on a durable physical medium
    customarily used for software interchange.

    b) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by a
    written offer, valid for at least three years and valid for as
    long as you offer spare parts or customer support for that product
    model, to give anyone who possesses the object code either (1) a
    copy of the Corresponding Source for all the software in the
    product that is covered by this License, on a durable physical
    medium customarily used for software interchange, for a price no
    more than your reasonable cost of physically performing this
    conveying of source, or (2) access to copy the
    Corresponding Source from a network server at no charge.

    c) Convey individual copies of the object code with a copy of the
    written offer to provide the Corresponding Source.  This
    alternative is allowed only occasionally and noncommercially, and
    only if you received the object code with such an offer, in accord
    with subsection 6b.

    d) Convey the object code by offering access from a designated
    place (gratis or for a charge), and offer equivalent access to the
    Corresponding Source in the same way through the same place at no
    further charge.  You need not require recipients to copy the
    Corresponding Source along with the object code.  If the place to
    copy the object code is a network server, the Corresponding Source
    may be on a different server (operated by you or a third party)
    that supports equivalent copying facilities, provided you maintain
    clear directions next to the object code saying where to find the
    Corresponding Source.  Regardless of what server hosts the
    Corresponding Source, you remain obligated to ensure that it is
    available for as long as needed to satisfy these requirements.

    e) Convey the object code using peer-to-peer transmission, provided
    you inform other peers where the object code and Corresponding
    Source of the work are being offered to the general public at no
    charge under subsection 6d.

  A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.

  A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling.  In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage.  For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product.  A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.

  "Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source.  The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.

  If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information.  But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).

  The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed.  Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.

  Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.

  7. Additional Terms.

  "Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law.  If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.

  When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it.  (Additional permissions may be written to require their own
removal in certain cases when you modify the work.)  You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.

  Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:

    a) Disclaiming warranty or limiting liability differently from the
    terms of sections 15 and 16 of this License; or

    b) Requiring preservation of specified reasonable legal notices or
    author attributions in that material or in the Appropriate Legal
    Notices displayed by works containing it; or

    c) Prohibiting misrepresentation of the origin of that material, or
    requiring that modified versions of such material be marked in
    reasonable ways as different from the original version; or

    d) Limiting the use for publicity purposes of names of licensors or
    authors of the material; or

    e) Declining to grant rights under trademark law for use of some
    trade names, trademarks, or service marks; or

    f) Requiring indemnification of licensors and authors of that
    material by anyone who conveys the material (or modified versions of
    it) with contractual assumptions of liability to the recipient, for
    any liability that these contractual assumptions directly impose on
    those licensors and authors.

  All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10.  If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term.  If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.

  If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.

  Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.

  8. Termination.

  You may not propagate or modify a covered work except as expressly
provided under this License.  Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).

  However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.

  Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.

  Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License.  If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.

  9. Acceptance Not Required for Having Copies.

  You are not required to accept this License in order to receive or
run a copy of the Program.  Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance.  However,
nothing other than this License grants you permission to propagate or
modify any covered work.  These actions infringe copyright if you do
not accept this License.  Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.

  10. Automatic Licensing of Downstream Recipients.

  Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License.  You are not responsible
for enforcing compliance by third parties with this License.

  An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations.  If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.

  You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License.  For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.

  11. Patents.

  A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based.  The
work thus licensed is called the contributor's "contributor version".

  A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version.  For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.

  Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.

  In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement).  To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.

  If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients.  "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.

  If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.

  A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License.  You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.

  Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.

  12. No Surrender of Others' Freedom.

  If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all.  For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.

  13. Use with the GNU Affero General Public License.

  Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work.  The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.

  14. Revised Versions of this License.

  The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

  Each version is given a distinguishing version number.  If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation.  If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.

  If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.

  Later license versions may give you additional or different
permissions.  However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.

  15. Disclaimer of Warranty.

  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. Limitation of Liability.

  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.

  17. Interpretation of Sections 15 and 16.

  If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.

  18. Additional terms in accordance with Section 7.

  In accordance with Section 7 of this License, the following additional
terms apply: 

    a) You are prohibited from misrepresenting the the origin of the 
    covered work. Modified versions of the covered work must be clearly 
    marked as being different from the original version of the covered 
    work;

    b) Unless expressly agreed otherwise, you are not allowed to use the 
    names of the author(s) or any of its licensors for publicity or 
    marketing purposes;

    c) Unless expressly agreed otherwise, you do not receive any rights 
    with respect to trademark law, trade name law or comparable laws 
    whatsoever. Unless expressly agreed otherwise, you are not entitled to
    use any trade name, trademark, service mark or similar belonging to the
    author(s) or its licensors for any purpose.   

    d) In the event you choose to convey the covered work or modified 
    versions thereof, you are required to indemnify and hold harmless the 
    author(s) and its licensors against any costs resulting from any 
    contractual assumptions of liability of the author(s) or its licensors 
    with regard to the covered work agreed on or implied by you.


                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.

Also add information on how to contact you by electronic and paper mail.

  If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:

    <program>  Copyright (C) <year>  <name of author>
    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".

  You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.

  The GNU General Public License does not permit incorporating your program
into proprietary programs.  If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library.  If this is what you want to do, use the GNU Lesser General
Public License instead of this License.  But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.
vendor/arcturial/clickatell/src/Rest.php000064400000011336150755130600014361 0ustar00<?php

namespace WP2FA_Vendor\Clickatell;

class Rest
{
    /**
     * API base URL
     * @var string
     */
    const API_URL = 'https://platform.clickatell.com';
    /**
     * @var string
     */
    const HTTP_GET = 'GET';
    /**
     * @var string
     */
    const HTTP_POST = 'POST';
    /**
     * The CURL agent identifier
     * @var string
     */
    const AGENT = 'ClickatellV2/1.0';
    /**
     * Excepted HTTP statuses
     * @var array
     */
    const ACCEPTED_CODES = [200, 201, 202];
    /**
     * @var string
     */
    private $apiToken = '';
    /**
     * Create a new API connection
     *
     * @param string $apiToken The token found on your integration
     */
    public function __construct($apiToken)
    {
        $this->apiToken = $apiToken;
    }
    /**
     * Handle CURL response from Clickatell APIs
     *
     * @param string $result   The API response
     * @param int    $httpCode The HTTP status code
     *
     * @throws Exception
     * @return array
     */
    protected function handle($result, $httpCode)
    {
        // Check for non-OK statuses
        if (!\in_array($httpCode, static::ACCEPTED_CODES)) {
            // Decode JSON if possible, if this can't be decoded...something fatal went wrong
            // and we will just return the entire body as an exception.
            if ($error = \json_decode($result, \true)) {
                $error = $error['error'];
            } else {
                $error = $result;
            }
            throw new \WP2FA_Vendor\Clickatell\ClickatellException($error);
        } else {
            return \json_decode($result, \true);
        }
    }
    /**
     * Abstract CURL usage.
     *
     * @param string $uri     The endpoint
     * @param string $data    Array of parameters
     *
     * @return Decoder
     */
    protected function curl($uri, $data)
    {
        // Force data object to array
        $data = $data ? (array) $data : $data;
        $headers = ['Content-Type: application/json', 'Accept: application/json', 'Authorization: ' . $this->apiToken];
        // This is the clickatell endpoint. It doesn't really change so
        // it's safe for us to "hardcode" it here.
        $endpoint = static::API_URL . "/" . $uri;
        $curlInfo = \curl_version();
        $ch = \curl_init();
        \curl_setopt($ch, \CURLOPT_URL, $endpoint);
        \curl_setopt($ch, \CURLOPT_HEADER, 0);
        \curl_setopt($ch, \CURLOPT_RETURNTRANSFER, \true);
        \curl_setopt($ch, \CURLOPT_HTTPHEADER, $headers);
        \curl_setopt($ch, \CURLOPT_USERAGENT, static::AGENT . ' curl/' . $curlInfo['version'] . ' PHP/' . \phpversion());
        // Specify the raw post data
        if ($data) {
            \curl_setopt($ch, \CURLOPT_POST, 1);
            \curl_setopt($ch, \CURLOPT_POSTFIELDS, \json_encode($data));
        }
        $result = \curl_exec($ch);
        $httpCode = \curl_getinfo($ch, \CURLINFO_HTTP_CODE);
        return $this->handle($result, $httpCode);
    }
    /**
     * @see https://www.clickatell.com/developers/api-documentation/rest-api-send-message/
     *
     * @param array $message The message parameters
     *
     * @return array
     */
    public function sendMessage(array $message)
    {
        $response = $this->curl('messages', $message);
        return $response['messages'];
    }
    /**
     * @see https://www.clickatell.com/developers/api-documentation/rest-api-status-callback/
     *
     * @param callable $callback The function to trigger with desired parameters
     * @param string   $file     The stream or file name, default to standard input
     *
     * @return void
     */
    public static function parseStatusCallback($callback, $file = \STDIN)
    {
        $body = \file_get_contents($file);
        $body = \json_decode($body, \true);
        $keys = ['apiKey', 'messageId', 'requestId', 'clientMessageId', 'to', 'from', 'status', 'statusDescription', 'timestamp'];
        if (!\array_diff($keys, \array_keys($body))) {
            $callback($body);
        }
        return;
    }
    /**
     * @see https://www.clickatell.com/developers/api-documentation/rest-api-reply-callback/
     *
     * @param callable $callback The function to trigger with desired parameters
     * @param string   $file     The stream or file name, default to standard input
     *
     * @return void
     */
    public static function parseReplyCallback($callback, $file = \STDIN)
    {
        $body = \file_get_contents($file);
        $body = \json_decode($body, \true);
        $keys = ['integrationId', 'messageId', 'replyMessageId', 'apiKey', 'fromNumber', 'toNumber', 'timestamp', 'text', 'charset', 'udh', 'network', 'keyword'];
        if (!\array_diff($keys, \array_keys($body))) {
            $callback($body);
        }
        return;
    }
}
vendor/arcturial/clickatell/src/ClickatellException.php000064400000000242150755130600017364 0ustar00<?php

namespace WP2FA_Vendor\Clickatell;

class ClickatellException extends \Exception
{
    // Custom Clickatell Exception
    // ---------------------------
}
vendor/firebase/php-jwt/src/BeforeValidException.php000064400000000156150755130600016557 0ustar00<?php

namespace WP2FA_Vendor\Firebase\JWT;

class BeforeValidException extends \UnexpectedValueException
{
}
vendor/firebase/php-jwt/src/Key.php000064400000002606150755130600013250 0ustar00<?php

namespace WP2FA_Vendor\Firebase\JWT;

use InvalidArgumentException;
use OpenSSLAsymmetricKey;
class Key
{
    /** @var string $algorithm */
    private $algorithm;
    /** @var string|resource|OpenSSLAsymmetricKey $keyMaterial */
    private $keyMaterial;
    /**
     * @param string|resource|OpenSSLAsymmetricKey $keyMaterial
     * @param string $algorithm
     */
    public function __construct($keyMaterial, $algorithm)
    {
        if (!\is_string($keyMaterial) && !\is_resource($keyMaterial) && !$keyMaterial instanceof OpenSSLAsymmetricKey) {
            throw new InvalidArgumentException('Type error: $keyMaterial must be a string, resource, or OpenSSLAsymmetricKey');
        }
        if (empty($keyMaterial)) {
            throw new InvalidArgumentException('Type error: $keyMaterial must not be empty');
        }
        if (!\is_string($algorithm) || empty($keyMaterial)) {
            throw new InvalidArgumentException('Type error: $algorithm must be a string');
        }
        $this->keyMaterial = $keyMaterial;
        $this->algorithm = $algorithm;
    }
    /**
     * Return the algorithm valid for this key
     *
     * @return string
     */
    public function getAlgorithm()
    {
        return $this->algorithm;
    }
    /**
     * @return string|resource|OpenSSLAsymmetricKey
     */
    public function getKeyMaterial()
    {
        return $this->keyMaterial;
    }
}
vendor/firebase/php-jwt/src/SignatureInvalidException.php000064400000000163150755130600017643 0ustar00<?php

namespace WP2FA_Vendor\Firebase\JWT;

class SignatureInvalidException extends \UnexpectedValueException
{
}
vendor/firebase/php-jwt/src/JWT.php000064400000053106150755130600013165 0ustar00<?php

namespace WP2FA_Vendor\Firebase\JWT;

use ArrayAccess;
use DomainException;
use Exception;
use InvalidArgumentException;
use OpenSSLAsymmetricKey;
use UnexpectedValueException;
use DateTime;
/**
 * JSON Web Token implementation, based on this spec:
 * https://tools.ietf.org/html/rfc7519
 *
 * PHP version 5
 *
 * @category Authentication
 * @package  Authentication_JWT
 * @author   Neuman Vong <neuman@twilio.com>
 * @author   Anant Narayanan <anant@php.net>
 * @license  http://opensource.org/licenses/BSD-3-Clause 3-clause BSD
 * @link     https://github.com/firebase/php-jwt
 */
class JWT
{
    const ASN1_INTEGER = 0x2;
    const ASN1_SEQUENCE = 0x10;
    const ASN1_BIT_STRING = 0x3;
    /**
     * When checking nbf, iat or expiration times,
     * we want to provide some extra leeway time to
     * account for clock skew.
     */
    public static $leeway = 0;
    /**
     * Allow the current timestamp to be specified.
     * Useful for fixing a value within unit testing.
     *
     * Will default to PHP time() value if null.
     */
    public static $timestamp = null;
    public static $supported_algs = array('ES384' => array('openssl', 'SHA384'), 'ES256' => array('openssl', 'SHA256'), 'HS256' => array('hash_hmac', 'SHA256'), 'HS384' => array('hash_hmac', 'SHA384'), 'HS512' => array('hash_hmac', 'SHA512'), 'RS256' => array('openssl', 'SHA256'), 'RS384' => array('openssl', 'SHA384'), 'RS512' => array('openssl', 'SHA512'), 'EdDSA' => array('sodium_crypto', 'EdDSA'));
    /**
     * Decodes a JWT string into a PHP object.
     *
     * @param string                    $jwt            The JWT
     * @param Key|array<Key>|mixed      $keyOrKeyArray  The Key or array of Key objects.
     *                                                  If the algorithm used is asymmetric, this is the public key
     *                                                  Each Key object contains an algorithm and matching key.
     *                                                  Supported algorithms are 'ES384','ES256', 'HS256', 'HS384',
     *                                                  'HS512', 'RS256', 'RS384', and 'RS512'
     * @param array                     $allowed_algs   [DEPRECATED] List of supported verification algorithms. Only
     *                                                  should be used for backwards  compatibility.
     *
     * @return object The JWT's payload as a PHP object
     *
     * @throws InvalidArgumentException     Provided JWT was empty
     * @throws UnexpectedValueException     Provided JWT was invalid
     * @throws SignatureInvalidException    Provided JWT was invalid because the signature verification failed
     * @throws BeforeValidException         Provided JWT is trying to be used before it's eligible as defined by 'nbf'
     * @throws BeforeValidException         Provided JWT is trying to be used before it's been created as defined by 'iat'
     * @throws ExpiredException             Provided JWT has since expired, as defined by the 'exp' claim
     *
     * @uses jsonDecode
     * @uses urlsafeB64Decode
     */
    public static function decode($jwt, $keyOrKeyArray, array $allowed_algs = array())
    {
        $timestamp = \is_null(static::$timestamp) ? \time() : static::$timestamp;
        if (empty($keyOrKeyArray)) {
            throw new InvalidArgumentException('Key may not be empty');
        }
        $tks = \explode('.', $jwt);
        if (\count($tks) != 3) {
            throw new UnexpectedValueException('Wrong number of segments');
        }
        list($headb64, $bodyb64, $cryptob64) = $tks;
        if (null === ($header = static::jsonDecode(static::urlsafeB64Decode($headb64)))) {
            throw new UnexpectedValueException('Invalid header encoding');
        }
        if (null === ($payload = static::jsonDecode(static::urlsafeB64Decode($bodyb64)))) {
            throw new UnexpectedValueException('Invalid claims encoding');
        }
        if (\false === ($sig = static::urlsafeB64Decode($cryptob64))) {
            throw new UnexpectedValueException('Invalid signature encoding');
        }
        if (empty($header->alg)) {
            throw new UnexpectedValueException('Empty algorithm');
        }
        if (empty(static::$supported_algs[$header->alg])) {
            throw new UnexpectedValueException('Algorithm not supported');
        }
        list($keyMaterial, $algorithm) = self::getKeyMaterialAndAlgorithm($keyOrKeyArray, empty($header->kid) ? null : $header->kid);
        if (empty($algorithm)) {
            // Use deprecated "allowed_algs" to determine if the algorithm is supported.
            // This opens up the possibility of an attack in some implementations.
            // @see https://github.com/firebase/php-jwt/issues/351
            if (!\in_array($header->alg, $allowed_algs)) {
                throw new UnexpectedValueException('Algorithm not allowed');
            }
        } else {
            // Check the algorithm
            if (!self::constantTimeEquals($algorithm, $header->alg)) {
                // See issue #351
                throw new UnexpectedValueException('Incorrect key for this algorithm');
            }
        }
        if ($header->alg === 'ES256' || $header->alg === 'ES384') {
            // OpenSSL expects an ASN.1 DER sequence for ES256/ES384 signatures
            $sig = self::signatureToDER($sig);
        }
        if (!static::verify("{$headb64}.{$bodyb64}", $sig, $keyMaterial, $header->alg)) {
            throw new SignatureInvalidException('Signature verification failed');
        }
        // Check the nbf if it is defined. This is the time that the
        // token can actually be used. If it's not yet that time, abort.
        if (isset($payload->nbf) && $payload->nbf > $timestamp + static::$leeway) {
            throw new BeforeValidException('Cannot handle token prior to ' . \date(DateTime::ISO8601, $payload->nbf));
        }
        // Check that this token has been created before 'now'. This prevents
        // using tokens that have been created for later use (and haven't
        // correctly used the nbf claim).
        if (isset($payload->iat) && $payload->iat > $timestamp + static::$leeway) {
            throw new BeforeValidException('Cannot handle token prior to ' . \date(DateTime::ISO8601, $payload->iat));
        }
        // Check if this token has expired.
        if (isset($payload->exp) && $timestamp - static::$leeway >= $payload->exp) {
            throw new ExpiredException('Expired token');
        }
        return $payload;
    }
    /**
     * Converts and signs a PHP object or array into a JWT string.
     *
     * @param object|array      $payload    PHP object or array
     * @param string|resource   $key        The secret key.
     *                                      If the algorithm used is asymmetric, this is the private key
     * @param string            $alg        The signing algorithm.
     *                                      Supported algorithms are 'ES384','ES256', 'HS256', 'HS384',
     *                                      'HS512', 'RS256', 'RS384', and 'RS512'
     * @param mixed             $keyId
     * @param array             $head       An array with header elements to attach
     *
     * @return string A signed JWT
     *
     * @uses jsonEncode
     * @uses urlsafeB64Encode
     */
    public static function encode($payload, $key, $alg = 'HS256', $keyId = null, $head = null)
    {
        $header = array('typ' => 'JWT', 'alg' => $alg);
        if ($keyId !== null) {
            $header['kid'] = $keyId;
        }
        if (isset($head) && \is_array($head)) {
            $header = \array_merge($head, $header);
        }
        $segments = array();
        $segments[] = static::urlsafeB64Encode(static::jsonEncode($header));
        $segments[] = static::urlsafeB64Encode(static::jsonEncode($payload));
        $signing_input = \implode('.', $segments);
        $signature = static::sign($signing_input, $key, $alg);
        $segments[] = static::urlsafeB64Encode($signature);
        return \implode('.', $segments);
    }
    /**
     * Sign a string with a given key and algorithm.
     *
     * @param string            $msg    The message to sign
     * @param string|resource   $key    The secret key
     * @param string            $alg    The signing algorithm.
     *                                  Supported algorithms are 'ES384','ES256', 'HS256', 'HS384',
     *                                  'HS512', 'RS256', 'RS384', and 'RS512'
     *
     * @return string An encrypted message
     *
     * @throws DomainException Unsupported algorithm or bad key was specified
     */
    public static function sign($msg, $key, $alg = 'HS256')
    {
        if (empty(static::$supported_algs[$alg])) {
            throw new DomainException('Algorithm not supported');
        }
        list($function, $algorithm) = static::$supported_algs[$alg];
        switch ($function) {
            case 'hash_hmac':
                return \hash_hmac($algorithm, $msg, $key, \true);
            case 'openssl':
                $signature = '';
                $success = \openssl_sign($msg, $signature, $key, $algorithm);
                if (!$success) {
                    throw new DomainException("OpenSSL unable to sign data");
                }
                if ($alg === 'ES256') {
                    $signature = self::signatureFromDER($signature, 256);
                } elseif ($alg === 'ES384') {
                    $signature = self::signatureFromDER($signature, 384);
                }
                return $signature;
            case 'sodium_crypto':
                if (!\function_exists('sodium_crypto_sign_detached')) {
                    throw new DomainException('libsodium is not available');
                }
                try {
                    // The last non-empty line is used as the key.
                    $lines = \array_filter(\explode("\n", $key));
                    $key = \base64_decode(\end($lines));
                    return \sodium_crypto_sign_detached($msg, $key);
                } catch (Exception $e) {
                    throw new DomainException($e->getMessage(), 0, $e);
                }
        }
    }
    /**
     * Verify a signature with the message, key and method. Not all methods
     * are symmetric, so we must have a separate verify and sign method.
     *
     * @param string            $msg        The original message (header and body)
     * @param string            $signature  The original signature
     * @param string|resource   $key        For HS*, a string key works. for RS*, must be a resource of an openssl public key
     * @param string            $alg        The algorithm
     *
     * @return bool
     *
     * @throws DomainException Invalid Algorithm, bad key, or OpenSSL failure
     */
    private static function verify($msg, $signature, $key, $alg)
    {
        if (empty(static::$supported_algs[$alg])) {
            throw new DomainException('Algorithm not supported');
        }
        list($function, $algorithm) = static::$supported_algs[$alg];
        switch ($function) {
            case 'openssl':
                $success = \openssl_verify($msg, $signature, $key, $algorithm);
                if ($success === 1) {
                    return \true;
                } elseif ($success === 0) {
                    return \false;
                }
                // returns 1 on success, 0 on failure, -1 on error.
                throw new DomainException('OpenSSL error: ' . \openssl_error_string());
            case 'sodium_crypto':
                if (!\function_exists('sodium_crypto_sign_verify_detached')) {
                    throw new DomainException('libsodium is not available');
                }
                try {
                    // The last non-empty line is used as the key.
                    $lines = \array_filter(\explode("\n", $key));
                    $key = \base64_decode(\end($lines));
                    return \sodium_crypto_sign_verify_detached($signature, $msg, $key);
                } catch (Exception $e) {
                    throw new DomainException($e->getMessage(), 0, $e);
                }
            case 'hash_hmac':
            default:
                $hash = \hash_hmac($algorithm, $msg, $key, \true);
                return self::constantTimeEquals($signature, $hash);
        }
    }
    /**
     * Decode a JSON string into a PHP object.
     *
     * @param string $input JSON string
     *
     * @return object Object representation of JSON string
     *
     * @throws DomainException Provided string was invalid JSON
     */
    public static function jsonDecode($input)
    {
        if (\version_compare(\PHP_VERSION, '5.4.0', '>=') && !(\defined('WP2FA_Vendor\\JSON_C_VERSION') && \PHP_INT_SIZE > 4)) {
            /** In PHP >=5.4.0, json_decode() accepts an options parameter, that allows you
             * to specify that large ints (like Steam Transaction IDs) should be treated as
             * strings, rather than the PHP default behaviour of converting them to floats.
             */
            $obj = \json_decode($input, \false, 512, \JSON_BIGINT_AS_STRING);
        } else {
            /** Not all servers will support that, however, so for older versions we must
             * manually detect large ints in the JSON string and quote them (thus converting
             *them to strings) before decoding, hence the preg_replace() call.
             */
            $max_int_length = \strlen((string) \PHP_INT_MAX) - 1;
            $json_without_bigints = \preg_replace('/:\\s*(-?\\d{' . $max_int_length . ',})/', ': "$1"', $input);
            $obj = \json_decode($json_without_bigints);
        }
        if ($errno = \json_last_error()) {
            static::handleJsonError($errno);
        } elseif ($obj === null && $input !== 'null') {
            throw new DomainException('Null result with non-null input');
        }
        return $obj;
    }
    /**
     * Encode a PHP object into a JSON string.
     *
     * @param object|array $input A PHP object or array
     *
     * @return string JSON representation of the PHP object or array
     *
     * @throws DomainException Provided object could not be encoded to valid JSON
     */
    public static function jsonEncode($input)
    {
        $json = \json_encode($input);
        if ($errno = \json_last_error()) {
            static::handleJsonError($errno);
        } elseif ($json === 'null' && $input !== null) {
            throw new DomainException('Null result with non-null input');
        }
        return $json;
    }
    /**
     * Decode a string with URL-safe Base64.
     *
     * @param string $input A Base64 encoded string
     *
     * @return string A decoded string
     */
    public static function urlsafeB64Decode($input)
    {
        $remainder = \strlen($input) % 4;
        if ($remainder) {
            $padlen = 4 - $remainder;
            $input .= \str_repeat('=', $padlen);
        }
        return \base64_decode(\strtr($input, '-_', '+/'));
    }
    /**
     * Encode a string with URL-safe Base64.
     *
     * @param string $input The string you want encoded
     *
     * @return string The base64 encode of what you passed in
     */
    public static function urlsafeB64Encode($input)
    {
        return \str_replace('=', '', \strtr(\base64_encode($input), '+/', '-_'));
    }
    /**
     * Determine if an algorithm has been provided for each Key
     *
     * @param Key|array<Key>|mixed $keyOrKeyArray
     * @param string|null $kid
     *
     * @throws UnexpectedValueException
     *
     * @return array containing the keyMaterial and algorithm
     */
    private static function getKeyMaterialAndAlgorithm($keyOrKeyArray, $kid = null)
    {
        if (\is_string($keyOrKeyArray) || \is_resource($keyOrKeyArray) || $keyOrKeyArray instanceof OpenSSLAsymmetricKey) {
            return array($keyOrKeyArray, null);
        }
        if ($keyOrKeyArray instanceof Key) {
            return array($keyOrKeyArray->getKeyMaterial(), $keyOrKeyArray->getAlgorithm());
        }
        if (\is_array($keyOrKeyArray) || $keyOrKeyArray instanceof ArrayAccess) {
            if (!isset($kid)) {
                throw new UnexpectedValueException('"kid" empty, unable to lookup correct key');
            }
            if (!isset($keyOrKeyArray[$kid])) {
                throw new UnexpectedValueException('"kid" invalid, unable to lookup correct key');
            }
            $key = $keyOrKeyArray[$kid];
            if ($key instanceof Key) {
                return array($key->getKeyMaterial(), $key->getAlgorithm());
            }
            return array($key, null);
        }
        throw new UnexpectedValueException('$keyOrKeyArray must be a string|resource key, an array of string|resource keys, ' . 'an instance of Firebase\\JWT\\Key key or an array of Firebase\\JWT\\Key keys');
    }
    /**
     * @param string $left
     * @param string $right
     * @return bool
     */
    public static function constantTimeEquals($left, $right)
    {
        if (\function_exists('hash_equals')) {
            return \hash_equals($left, $right);
        }
        $len = \min(static::safeStrlen($left), static::safeStrlen($right));
        $status = 0;
        for ($i = 0; $i < $len; $i++) {
            $status |= \ord($left[$i]) ^ \ord($right[$i]);
        }
        $status |= static::safeStrlen($left) ^ static::safeStrlen($right);
        return $status === 0;
    }
    /**
     * Helper method to create a JSON error.
     *
     * @param int $errno An error number from json_last_error()
     *
     * @return void
     */
    private static function handleJsonError($errno)
    {
        $messages = array(\JSON_ERROR_DEPTH => 'Maximum stack depth exceeded', \JSON_ERROR_STATE_MISMATCH => 'Invalid or malformed JSON', \JSON_ERROR_CTRL_CHAR => 'Unexpected control character found', \JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON', \JSON_ERROR_UTF8 => 'Malformed UTF-8 characters');
        throw new DomainException(isset($messages[$errno]) ? $messages[$errno] : 'Unknown JSON error: ' . $errno);
    }
    /**
     * Get the number of bytes in cryptographic strings.
     *
     * @param string $str
     *
     * @return int
     */
    private static function safeStrlen($str)
    {
        if (\function_exists('mb_strlen')) {
            return \mb_strlen($str, '8bit');
        }
        return \strlen($str);
    }
    /**
     * Convert an ECDSA signature to an ASN.1 DER sequence
     *
     * @param   string $sig The ECDSA signature to convert
     * @return  string The encoded DER object
     */
    private static function signatureToDER($sig)
    {
        // Separate the signature into r-value and s-value
        list($r, $s) = \str_split($sig, (int) (\strlen($sig) / 2));
        // Trim leading zeros
        $r = \ltrim($r, "\x00");
        $s = \ltrim($s, "\x00");
        // Convert r-value and s-value from unsigned big-endian integers to
        // signed two's complement
        if (\ord($r[0]) > 0x7f) {
            $r = "\x00" . $r;
        }
        if (\ord($s[0]) > 0x7f) {
            $s = "\x00" . $s;
        }
        return self::encodeDER(self::ASN1_SEQUENCE, self::encodeDER(self::ASN1_INTEGER, $r) . self::encodeDER(self::ASN1_INTEGER, $s));
    }
    /**
     * Encodes a value into a DER object.
     *
     * @param   int     $type DER tag
     * @param   string  $value the value to encode
     * @return  string  the encoded object
     */
    private static function encodeDER($type, $value)
    {
        $tag_header = 0;
        if ($type === self::ASN1_SEQUENCE) {
            $tag_header |= 0x20;
        }
        // Type
        $der = \chr($tag_header | $type);
        // Length
        $der .= \chr(\strlen($value));
        return $der . $value;
    }
    /**
     * Encodes signature from a DER object.
     *
     * @param   string  $der binary signature in DER format
     * @param   int     $keySize the number of bits in the key
     * @return  string  the signature
     */
    private static function signatureFromDER($der, $keySize)
    {
        // OpenSSL returns the ECDSA signatures as a binary ASN.1 DER SEQUENCE
        list($offset, $_) = self::readDER($der);
        list($offset, $r) = self::readDER($der, $offset);
        list($offset, $s) = self::readDER($der, $offset);
        // Convert r-value and s-value from signed two's compliment to unsigned
        // big-endian integers
        $r = \ltrim($r, "\x00");
        $s = \ltrim($s, "\x00");
        // Pad out r and s so that they are $keySize bits long
        $r = \str_pad($r, $keySize / 8, "\x00", \STR_PAD_LEFT);
        $s = \str_pad($s, $keySize / 8, "\x00", \STR_PAD_LEFT);
        return $r . $s;
    }
    /**
     * Reads binary DER-encoded data and decodes into a single object
     *
     * @param string $der the binary data in DER format
     * @param int $offset the offset of the data stream containing the object
     * to decode
     * @return array [$offset, $data] the new offset and the decoded object
     */
    private static function readDER($der, $offset = 0)
    {
        $pos = $offset;
        $size = \strlen($der);
        $constructed = \ord($der[$pos]) >> 5 & 0x1;
        $type = \ord($der[$pos++]) & 0x1f;
        // Length
        $len = \ord($der[$pos++]);
        if ($len & 0x80) {
            $n = $len & 0x1f;
            $len = 0;
            while ($n-- && $pos < $size) {
                $len = $len << 8 | \ord($der[$pos++]);
            }
        }
        // Value
        if ($type == self::ASN1_BIT_STRING) {
            $pos++;
            // Skip the first contents octet (padding indicator)
            $data = \substr($der, $pos, $len - 1);
            $pos += $len - 1;
        } elseif (!$constructed) {
            $data = \substr($der, $pos, $len);
            $pos += $len;
        } else {
            $data = null;
        }
        return array($pos, $data);
    }
}
vendor/firebase/php-jwt/src/JWK.php000064400000012400150755130600013144 0ustar00<?php

namespace WP2FA_Vendor\Firebase\JWT;

use DomainException;
use InvalidArgumentException;
use UnexpectedValueException;
/**
 * JSON Web Key implementation, based on this spec:
 * https://tools.ietf.org/html/draft-ietf-jose-json-web-key-41
 *
 * PHP version 5
 *
 * @category Authentication
 * @package  Authentication_JWT
 * @author   Bui Sy Nguyen <nguyenbs@gmail.com>
 * @license  http://opensource.org/licenses/BSD-3-Clause 3-clause BSD
 * @link     https://github.com/firebase/php-jwt
 */
class JWK
{
    /**
     * Parse a set of JWK keys
     *
     * @param array $jwks The JSON Web Key Set as an associative array
     *
     * @return array An associative array that represents the set of keys
     *
     * @throws InvalidArgumentException     Provided JWK Set is empty
     * @throws UnexpectedValueException     Provided JWK Set was invalid
     * @throws DomainException              OpenSSL failure
     *
     * @uses parseKey
     */
    public static function parseKeySet(array $jwks)
    {
        $keys = array();
        if (!isset($jwks['keys'])) {
            throw new UnexpectedValueException('"keys" member must exist in the JWK Set');
        }
        if (empty($jwks['keys'])) {
            throw new InvalidArgumentException('JWK Set did not contain any keys');
        }
        foreach ($jwks['keys'] as $k => $v) {
            $kid = isset($v['kid']) ? $v['kid'] : $k;
            if ($key = self::parseKey($v)) {
                $keys[$kid] = $key;
            }
        }
        if (0 === \count($keys)) {
            throw new UnexpectedValueException('No supported algorithms found in JWK Set');
        }
        return $keys;
    }
    /**
     * Parse a JWK key
     *
     * @param array $jwk An individual JWK
     *
     * @return resource|array An associative array that represents the key
     *
     * @throws InvalidArgumentException     Provided JWK is empty
     * @throws UnexpectedValueException     Provided JWK was invalid
     * @throws DomainException              OpenSSL failure
     *
     * @uses createPemFromModulusAndExponent
     */
    public static function parseKey(array $jwk)
    {
        if (empty($jwk)) {
            throw new InvalidArgumentException('JWK must not be empty');
        }
        if (!isset($jwk['kty'])) {
            throw new UnexpectedValueException('JWK must contain a "kty" parameter');
        }
        switch ($jwk['kty']) {
            case 'RSA':
                if (!empty($jwk['d'])) {
                    throw new UnexpectedValueException('RSA private keys are not supported');
                }
                if (!isset($jwk['n']) || !isset($jwk['e'])) {
                    throw new UnexpectedValueException('RSA keys must contain values for both "n" and "e"');
                }
                $pem = self::createPemFromModulusAndExponent($jwk['n'], $jwk['e']);
                $publicKey = \openssl_pkey_get_public($pem);
                if (\false === $publicKey) {
                    throw new DomainException('OpenSSL error: ' . \openssl_error_string());
                }
                return $publicKey;
            default:
                // Currently only RSA is supported
                break;
        }
    }
    /**
     * Create a public key represented in PEM format from RSA modulus and exponent information
     *
     * @param string $n The RSA modulus encoded in Base64
     * @param string $e The RSA exponent encoded in Base64
     *
     * @return string The RSA public key represented in PEM format
     *
     * @uses encodeLength
     */
    private static function createPemFromModulusAndExponent($n, $e)
    {
        $modulus = JWT::urlsafeB64Decode($n);
        $publicExponent = JWT::urlsafeB64Decode($e);
        $components = array('modulus' => \pack('Ca*a*', 2, self::encodeLength(\strlen($modulus)), $modulus), 'publicExponent' => \pack('Ca*a*', 2, self::encodeLength(\strlen($publicExponent)), $publicExponent));
        $rsaPublicKey = \pack('Ca*a*a*', 48, self::encodeLength(\strlen($components['modulus']) + \strlen($components['publicExponent'])), $components['modulus'], $components['publicExponent']);
        // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
        $rsaOID = \pack('H*', '300d06092a864886f70d0101010500');
        // hex version of MA0GCSqGSIb3DQEBAQUA
        $rsaPublicKey = \chr(0) . $rsaPublicKey;
        $rsaPublicKey = \chr(3) . self::encodeLength(\strlen($rsaPublicKey)) . $rsaPublicKey;
        $rsaPublicKey = \pack('Ca*a*', 48, self::encodeLength(\strlen($rsaOID . $rsaPublicKey)), $rsaOID . $rsaPublicKey);
        $rsaPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" . \chunk_split(\base64_encode($rsaPublicKey), 64) . '-----END PUBLIC KEY-----';
        return $rsaPublicKey;
    }
    /**
     * DER-encode the length
     *
     * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4.  See
     * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
     *
     * @param int $length
     * @return string
     */
    private static function encodeLength($length)
    {
        if ($length <= 0x7f) {
            return \chr($length);
        }
        $temp = \ltrim(\pack('N', $length), \chr(0));
        return \pack('Ca*', 0x80 | \strlen($temp), $temp);
    }
}
vendor/firebase/php-jwt/src/ExpiredException.php000064400000000152150755130600015771 0ustar00<?php

namespace WP2FA_Vendor\Firebase\JWT;

class ExpiredException extends \UnexpectedValueException
{
}
vendor/khanamiryan/qrcode-detector-decoder/ecs.php000064400000001773150755130600016327 0ustar00<?php

namespace WP2FA_Vendor;

use WP2FA_Vendor\Rector\Set\ValueObject\LevelSetList;
use WP2FA_Vendor\PhpCsFixer\Fixer\Operator\ConcatSpaceFixer;
use WP2FA_Vendor\Symplify\EasyCodingStandard\Config\ECSConfig;
use WP2FA_Vendor\Symplify\EasyCodingStandard\ValueObject\Option;
use WP2FA_Vendor\PhpCsFixer\Fixer\ArrayNotation\ArraySyntaxFixer;
use WP2FA_Vendor\Symplify\EasyCodingStandard\ValueObject\Set\SetList;
use WP2FA_Vendor\Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ECSConfig $configurator) : void {
    // alternative to CLI arguments, easier to maintain and extend
    $configurator->paths([__DIR__ . '/lib', __DIR__ . '/tests']);
    // choose
    $configurator->sets([SetList::CLEAN_CODE, SetList::PSR_12]);
    $configurator->ruleWithConfiguration(ConcatSpaceFixer::class, ['spacing' => 'one']);
    // indent and tabs/spaces
    // [default: spaces]. BUT: tabs are superiour due to accessibility reasons
    $configurator->indentation('tab');
};
vendor/khanamiryan/qrcode-detector-decoder/lib/FormatException.php000064400000002602150755130600021422 0ustar00<?php

/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace WP2FA_Vendor\Zxing;

/**
 * Thrown when a barcode was successfully detected, but some aspect of
 * the content did not conform to the barcode's format rules. This could have
 * been due to a mis-detection.
 *
 * @author Sean Owen
 */
final class FormatException extends ReaderException
{
    private static ?\WP2FA_Vendor\Zxing\FormatException $instance = null;
    public function __construct($cause = null)
    {
        if ($cause) {
            parent::__construct($cause);
        }
    }
    public static function getFormatInstance($cause = null)
    {
        if (!self::$instance) {
            self::$instance = new FormatException();
        }
        if (self::$isStackTrace) {
            return new FormatException($cause);
        } else {
            return self::$instance;
        }
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/Result.php000064400000007210150755130600017571 0ustar00<?php

/*
 * Copyright 2007 ZXing authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
namespace WP2FA_Vendor\Zxing;

/**
 * <p>Encapsulates the result of decoding a barcode within an image.</p>
 *
 * @author Sean Owen
 */
final class Result
{
    /**
     * @var mixed[]|mixed
     */
    private $resultMetadata = null;
    private $timestamp;
    public function __construct(private $text, private $rawBytes, private $resultPoints, private $format, $timestamp = '')
    {
        $this->timestamp = $timestamp ?: \time();
    }
    /**
     * @return raw text encoded by the barcode
     */
    public function getText()
    {
        return $this->text;
    }
    /**
     * @return raw bytes encoded by the barcode, if applicable, otherwise {@code null}
     */
    public function getRawBytes()
    {
        return $this->rawBytes;
    }
    /**
     * @return points related to the barcode in the image. These are typically points
     *         identifying finder patterns or the corners of the barcode. The exact meaning is
     *         specific to the type of barcode that was decoded.
     */
    public function getResultPoints()
    {
        return $this->resultPoints;
    }
    /**
     * @return {@link BarcodeFormat} representing the format of the barcode that was decoded
     */
    public function getBarcodeFormat()
    {
        return $this->format;
    }
    /**
     * @return {@link Map} mapping {@link ResultMetadataType} keys to values. May be
     *   {@code null}. This contains optional metadata about what was detected about the barcode,
     *   like orientation.
     */
    public function getResultMetadata()
    {
        return $this->resultMetadata;
    }
    public function putMetadata($type, $value) : void
    {
        $resultMetadata = [];
        if ($this->resultMetadata === null) {
            $this->resultMetadata = [];
        }
        $resultMetadata[$type] = $value;
    }
    public function putAllMetadata($metadata) : void
    {
        if ($metadata !== null) {
            if ($this->resultMetadata === null) {
                $this->resultMetadata = $metadata;
            } else {
                $this->resultMetadata = \array_merge($this->resultMetadata, $metadata);
            }
        }
    }
    public function addResultPoints($newPoints) : void
    {
        $oldPoints = $this->resultPoints;
        if ($oldPoints === null) {
            $this->resultPoints = $newPoints;
        } elseif ($newPoints !== null && (\is_countable($newPoints) ? \count($newPoints) : 0) > 0) {
            $allPoints = fill_array(0, (\is_countable($oldPoints) ? \count($oldPoints) : 0) + (\is_countable($newPoints) ? \count($newPoints) : 0), 0);
            $allPoints = arraycopy($oldPoints, 0, $allPoints, 0, \is_countable($oldPoints) ? \count($oldPoints) : 0);
            $allPoints = arraycopy($newPoints, 0, $allPoints, \is_countable($oldPoints) ? \count($oldPoints) : 0, \is_countable($newPoints) ? \count($newPoints) : 0);
            $this->resultPoints = $allPoints;
        }
    }
    public function getTimestamp()
    {
        return $this->timestamp;
    }
    public function toString()
    {
        return $this->text;
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/Reader.php000064400000000211150755130600017507 0ustar00<?php

namespace WP2FA_Vendor\Zxing;

interface Reader
{
    public function decode(BinaryBitmap $image);
    public function reset();
}
vendor/khanamiryan/qrcode-detector-decoder/lib/RGBLuminanceSource.php000064400000024034150755130600021745 0ustar00<?php

/*
* Copyright 2009 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace WP2FA_Vendor\Zxing;

/**
 * This class is used to help decode images from files which arrive as RGB data from
 * an ARGB pixel array. It does not support rotation.
 *
 * @author dswitkin@google.com (Daniel Switkin)
 * @author Betaminos
 */
final class RGBLuminanceSource extends LuminanceSource
{
    public $luminances;
    private $dataWidth;
    private $dataHeight;
    /**
     * @var mixed|int
     */
    private $left;
    /**
     * @var mixed|int
     */
    private $top;
    /**
     * @var mixed|null
     */
    private $pixels;
    public function __construct($pixels, $dataWidth, $dataHeight, $left = null, $top = null, $width = null, $height = null)
    {
        if (!$left && !$top && !$width && !$height) {
            $this->RGBLuminanceSource_($pixels, $dataWidth, $dataHeight);
            return;
        }
        parent::__construct($width, $height);
        if ($left + $width > $dataWidth || $top + $height > $dataHeight) {
            throw new \InvalidArgumentException("Crop rectangle does not fit within image data.");
        }
        $this->luminances = $pixels;
        $this->dataWidth = $dataWidth;
        $this->dataHeight = $dataHeight;
        $this->left = $left;
        $this->top = $top;
    }
    public function RGBLuminanceSource_($width, $height, $pixels) : void
    {
        parent::__construct($width, $height);
        $this->dataWidth = $width;
        $this->dataHeight = $height;
        $this->left = 0;
        $this->top = 0;
        $this->pixels = $pixels;
        // In order to measure pure decoding speed, we convert the entire image to a greyscale array
        // up front, which is the same as the Y channel of the YUVLuminanceSource in the real app.
        $this->luminances = [];
        //$this->luminances = $this->grayScaleToBitmap($this->grayscale());
        foreach ($pixels as $key => $pixel) {
            $r = $pixel['red'];
            $g = $pixel['green'];
            $b = $pixel['blue'];
            /* if (($pixel & 0xFF000000) == 0) {
            				 $pixel = 0xFFFFFFFF; // = white
            			 }
            
            			 // .229R + 0.587G + 0.114B (YUV/YIQ for PAL and NTSC)
            
            			 $this->luminances[$key] =
            				 (306 * (($pixel >> 16) & 0xFF) +
            					 601 * (($pixel >> 8) & 0xFF) +
            					 117 * ($pixel & 0xFF) +
            					 0x200) >> 10;
            
            			*/
            //$r = ($pixel >> 16) & 0xff;
            //$g = ($pixel >> 8) & 0xff;
            //$b = $pixel & 0xff;
            if ($r == $g && $g == $b) {
                // Image is already greyscale, so pick any channel.
                $this->luminances[$key] = $r;
                //(($r + 128) % 256) - 128;
            } else {
                // Calculate luminance cheaply, favoring green.
                $this->luminances[$key] = ($r + 2 * $g + $b) / 4;
                //(((($r + 2 * $g + $b) / 4) + 128) % 256) - 128;
            }
        }
        /*
        
        for ($y = 0; $y < $height; $y++) {
        	$offset = $y * $width;
        	for ($x = 0; $x < $width; $x++) {
        		$pixel = $pixels[$offset + $x];
        		$r = ($pixel >> 16) & 0xff;
        		$g = ($pixel >> 8) & 0xff;
        		$b = $pixel & 0xff;
        		if ($r == $g && $g == $b) {
        // Image is already greyscale, so pick any channel.
        
        			$this->luminances[(int)($offset + $x)] = (($r+128) % 256) - 128;
        		} else {
        // Calculate luminance cheaply, favoring green.
        			$this->luminances[(int)($offset + $x)] =  (((($r + 2 * $g + $b) / 4)+128)%256) - 128;
        		}
        
        
        
        	}
        */
        //}
        //   $this->luminances = $this->grayScaleToBitmap($this->luminances);
    }
    public function grayscale()
    {
        $width = $this->dataWidth;
        $height = $this->dataHeight;
        $ret = fill_array(0, $width * $height, 0);
        for ($y = 0; $y < $height; $y++) {
            for ($x = 0; $x < $width; $x++) {
                $gray = $this->getPixel($x, $y, $width, $height);
                $ret[$x + $y * $width] = $gray;
            }
        }
        return $ret;
    }
    public function getPixel($x, $y, $width, $height)
    {
        $image = $this->pixels;
        if ($width < $x) {
            die('error');
        }
        if ($height < $y) {
            die('error');
        }
        $point = $x + $y * $width;
        $r = $image[$point]['red'];
        //($image[$point] >> 16) & 0xff;
        $g = $image[$point]['green'];
        //($image[$point] >> 8) & 0xff;
        $b = $image[$point]['blue'];
        //$image[$point] & 0xff;
        $p = (int) (($r * 33 + $g * 34 + $b * 33) / 100);
        return $p;
    }
    public function grayScaleToBitmap($grayScale)
    {
        $middle = $this->getMiddleBrightnessPerArea($grayScale);
        $sqrtNumArea = \is_countable($middle) ? \count($middle) : 0;
        $areaWidth = \floor($this->dataWidth / $sqrtNumArea);
        $areaHeight = \floor($this->dataHeight / $sqrtNumArea);
        $bitmap = fill_array(0, $this->dataWidth * $this->dataHeight, 0);
        for ($ay = 0; $ay < $sqrtNumArea; $ay++) {
            for ($ax = 0; $ax < $sqrtNumArea; $ax++) {
                for ($dy = 0; $dy < $areaHeight; $dy++) {
                    for ($dx = 0; $dx < $areaWidth; $dx++) {
                        $bitmap[(int) ($areaWidth * $ax + $dx + ($areaHeight * $ay + $dy) * $this->dataWidth)] = $grayScale[(int) ($areaWidth * $ax + $dx + ($areaHeight * $ay + $dy) * $this->dataWidth)] < $middle[$ax][$ay] ? 0 : 255;
                    }
                }
            }
        }
        return $bitmap;
    }
    public function getMiddleBrightnessPerArea($image)
    {
        $numSqrtArea = 4;
        //obtain middle brightness((min + max) / 2) per area
        $areaWidth = \floor($this->dataWidth / $numSqrtArea);
        $areaHeight = \floor($this->dataHeight / $numSqrtArea);
        $minmax = fill_array(0, $numSqrtArea, 0);
        for ($i = 0; $i < $numSqrtArea; $i++) {
            $minmax[$i] = fill_array(0, $numSqrtArea, 0);
            for ($i2 = 0; $i2 < $numSqrtArea; $i2++) {
                $minmax[$i][$i2] = [0, 0];
            }
        }
        for ($ay = 0; $ay < $numSqrtArea; $ay++) {
            for ($ax = 0; $ax < $numSqrtArea; $ax++) {
                $minmax[$ax][$ay][0] = 0xff;
                for ($dy = 0; $dy < $areaHeight; $dy++) {
                    for ($dx = 0; $dx < $areaWidth; $dx++) {
                        $target = $image[(int) ($areaWidth * $ax + $dx + ($areaHeight * $ay + $dy) * $this->dataWidth)];
                        if ($target < $minmax[$ax][$ay][0]) {
                            $minmax[$ax][$ay][0] = $target;
                        }
                        if ($target > $minmax[$ax][$ay][1]) {
                            $minmax[$ax][$ay][1] = $target;
                        }
                    }
                }
                //minmax[ax][ay][0] = (minmax[ax][ay][0] + minmax[ax][ay][1]) / 2;
            }
        }
        $middle = [];
        for ($i3 = 0; $i3 < $numSqrtArea; $i3++) {
            $middle[$i3] = [];
        }
        for ($ay = 0; $ay < $numSqrtArea; $ay++) {
            for ($ax = 0; $ax < $numSqrtArea; $ax++) {
                $middle[$ax][$ay] = \floor(($minmax[$ax][$ay][0] + $minmax[$ax][$ay][1]) / 2);
                //Console.out.print(middle[ax][ay] + ",");
            }
            //Console.out.println("");
        }
        //Console.out.println("");
        return $middle;
    }
    //@Override
    public function getRow($y, $row = null)
    {
        if ($y < 0 || $y >= $this->getHeight()) {
            throw new \InvalidArgumentException("Requested row is outside the image: " + \WP2FA_Vendor\Y);
        }
        $width = $this->getWidth();
        if ($row == null || (\is_countable($row) ? \count($row) : 0) < $width) {
            $row = [];
        }
        $offset = ($y + $this->top) * $this->dataWidth + $this->left;
        $row = arraycopy($this->luminances, $offset, $row, 0, $width);
        return $row;
    }
    //@Override
    public function getMatrix()
    {
        $width = $this->getWidth();
        $height = $this->getHeight();
        // If the caller asks for the entire underlying image, save the copy and give them the
        // original data. The docs specifically warn that result.length must be ignored.
        if ($width == $this->dataWidth && $height == $this->dataHeight) {
            return $this->luminances;
        }
        $area = $width * $height;
        $matrix = [];
        $inputOffset = $this->top * $this->dataWidth + $this->left;
        // If the width matches the full width of the underlying data, perform a single copy.
        if ($width == $this->dataWidth) {
            $matrix = arraycopy($this->luminances, $inputOffset, $matrix, 0, $area);
            return $matrix;
        }
        // Otherwise copy one cropped row at a time.
        $rgb = $this->luminances;
        for ($y = 0; $y < $height; $y++) {
            $outputOffset = $y * $width;
            $matrix = arraycopy($rgb, $inputOffset, $matrix, $outputOffset, $width);
            $inputOffset += $this->dataWidth;
        }
        return $matrix;
    }
    //@Override
    public function isCropSupported()
    {
        return \true;
    }
    //@Override
    public function crop($left, $top, $width, $height) : \WP2FA_Vendor\Zxing\RGBLuminanceSource
    {
        return new RGBLuminanceSource($this->luminances, $this->dataWidth, $this->dataHeight, $this->left + $left, $this->top + $top, $width, $height);
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/BinaryBitmap.php000064400000013423150755130600020677 0ustar00<?php

/*
* Copyright 2009 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace WP2FA_Vendor\Zxing;

use WP2FA_Vendor\Zxing\Common\BitMatrix;
/**
 * This class is the core bitmap class used by ZXing to represent 1 bit data. Reader objects
 * accept a BinaryBitmap and attempt to decode it.
 *
 * @author dswitkin@google.com (Daniel Switkin)
 */
final class BinaryBitmap
{
    private readonly \WP2FA_Vendor\Zxing\Binarizer $binarizer;
    private ?\WP2FA_Vendor\Zxing\Common\BitMatrix $matrix = null;
    public function __construct(Binarizer $binarizer)
    {
        if ($binarizer === null) {
            throw new \InvalidArgumentException("Binarizer must be non-null.");
        }
        $this->binarizer = $binarizer;
    }
    /**
     * @return int The width of the bitmap.
     */
    public function getWidth()
    {
        return $this->binarizer->getWidth();
    }
    /**
     * @return int The height of the bitmap.
     */
    public function getHeight()
    {
        return $this->binarizer->getHeight();
    }
    /**
     * Converts one row of luminance data to 1 bit data. May actually do the conversion, or return
     * cached data. Callers should assume this method is expensive and call it as seldom as possible.
     * This method is intended for decoding 1D barcodes and may choose to apply sharpening.
     *
     * @param $y   The row to fetch, which must be in [0, bitmap height)
     * @param An $row optional preallocated array. If null or too small, it will be ignored.
     *            If used, the Binarizer will call BitArray.clear(). Always use the returned object.
     *
     * @return array The array of bits for this row (true means black).
     * @throws NotFoundException if row can't be binarized
     */
    public function getBlackRow($y, $row)
    {
        return $this->binarizer->getBlackRow($y, $row);
    }
    /**
     * @return bool Whether this bitmap can be cropped.
     */
    public function isCropSupported()
    {
        return $this->binarizer->getLuminanceSource()->isCropSupported();
    }
    /**
     * Returns a new object with cropped image data. Implementations may keep a reference to the
     * original data rather than a copy. Only callable if isCropSupported() is true.
     *
     * @param $left   The left coordinate, which must be in [0,getWidth())
     * @param $top    The top coordinate, which must be in [0,getHeight())
     * @param $width  The width of the rectangle to crop.
     * @param $height The height of the rectangle to crop.
     *
     * @return BinaryBitmap A cropped version of this object.
     */
    public function crop($left, $top, $width, $height) : \WP2FA_Vendor\Zxing\BinaryBitmap
    {
        $newSource = $this->binarizer->getLuminanceSource()->crop($left, $top, $width, $height);
        return new BinaryBitmap($this->binarizer->createBinarizer($newSource));
    }
    /**
     * @return Whether this bitmap supports counter-clockwise rotation.
     */
    public function isRotateSupported()
    {
        return $this->binarizer->getLuminanceSource()->isRotateSupported();
    }
    /**
     * Returns a new object with rotated image data by 90 degrees counterclockwise.
     * Only callable if {@link #isRotateSupported()} is true.
     *
     * @return BinaryBitmap A rotated version of this object.
     */
    public function rotateCounterClockwise() : \WP2FA_Vendor\Zxing\BinaryBitmap
    {
        $newSource = $this->binarizer->getLuminanceSource()->rotateCounterClockwise();
        return new BinaryBitmap($this->binarizer->createBinarizer($newSource));
    }
    /**
     * Returns a new object with rotated image data by 45 degrees counterclockwise.
     * Only callable if {@link #isRotateSupported()} is true.
     *
     * @return BinaryBitmap A rotated version of this object.
     */
    public function rotateCounterClockwise45() : \WP2FA_Vendor\Zxing\BinaryBitmap
    {
        $newSource = $this->binarizer->getLuminanceSource()->rotateCounterClockwise45();
        return new BinaryBitmap($this->binarizer->createBinarizer($newSource));
    }
    public function toString()
    {
        try {
            return $this->getBlackMatrix()->toString();
        } catch (NotFoundException) {
        }
        return '';
    }
    /**
     * Converts a 2D array of luminance data to 1 bit. As above, assume this method is expensive
     * and do not call it repeatedly. This method is intended for decoding 2D barcodes and may or
     * may not apply sharpening. Therefore, a row from this matrix may not be identical to one
     * fetched using getBlackRow(), so don't mix and match between them.
     *
     * @return BitMatrix The 2D array of bits for the image (true means black).
     * @throws NotFoundException if image can't be binarized to make a matrix
     */
    public function getBlackMatrix()
    {
        // The matrix is created on demand the first time it is requested, then cached. There are two
        // reasons for this:
        // 1. This work will never be done if the caller only installs 1D Reader objects, or if a
        //    1D Reader finds a barcode before the 2D Readers run.
        // 2. This work will only be done once even if the caller installs multiple 2D Readers.
        if ($this->matrix === null) {
            $this->matrix = $this->binarizer->getBlackMatrix();
        }
        return $this->matrix;
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/IMagickLuminanceSource.php000064400000011110150755130600022626 0ustar00<?php

namespace WP2FA_Vendor\Zxing;

/**
 * This class is used to help decode images from files which arrive as GD Resource
 * It does not support rotation.
 */
final class IMagickLuminanceSource extends LuminanceSource
{
    public $luminances;
    private $dataWidth;
    private $dataHeight;
    /**
     * @var mixed|int
     */
    private $left;
    /**
     * @var mixed|int
     */
    private $top;
    private ?\Imagick $image = null;
    public function __construct(\Imagick $image, $dataWidth, $dataHeight, $left = null, $top = null, $width = null, $height = null)
    {
        if (!$left && !$top && !$width && !$height) {
            $this->_IMagickLuminanceSource($image, $dataWidth, $dataHeight);
            return;
        }
        parent::__construct($width, $height);
        if ($left + $width > $dataWidth || $top + $height > $dataHeight) {
            throw new \InvalidArgumentException("Crop rectangle does not fit within image data.");
        }
        $this->luminances = $image;
        $this->dataWidth = $dataWidth;
        $this->dataHeight = $dataHeight;
        $this->left = $left;
        $this->top = $top;
    }
    public function _IMagickLuminanceSource(\Imagick $image, $width, $height) : void
    {
        parent::__construct($width, $height);
        $this->dataWidth = $width;
        $this->dataHeight = $height;
        $this->left = 0;
        $this->top = 0;
        $this->image = $image;
        // In order to measure pure decoding speed, we convert the entire image to a greyscale array
        // up front, which is the same as the Y channel of the YUVLuminanceSource in the real app.
        $this->luminances = [];
        $image->setImageColorspace(\Imagick::COLORSPACE_GRAY);
        // $image->newPseudoImage(0, 0, "magick:rose");
        $pixels = $image->exportImagePixels(1, 1, $width, $height, "RGB", \Imagick::PIXEL_CHAR);
        $array = [];
        $rgb = [];
        $countPixels = \count($pixels);
        for ($i = 0; $i < $countPixels; $i += 3) {
            $r = $pixels[$i] & 0xff;
            $g = $pixels[$i + 1] & 0xff;
            $b = $pixels[$i + 2] & 0xff;
            if ($r == $g && $g == $b) {
                // Image is already greyscale, so pick any channel.
                $this->luminances[] = $r;
                //(($r + 128) % 256) - 128;
            } else {
                // Calculate luminance cheaply, favoring green.
                $this->luminances[] = ($r + 2 * $g + $b) / 4;
                //(((($r + 2 * $g + $b) / 4) + 128) % 256) - 128;
            }
        }
    }
    //@Override
    public function getRow($y, $row = null)
    {
        if ($y < 0 || $y >= $this->getHeight()) {
            throw new \InvalidArgumentException('Requested row is outside the image: ' . $y);
        }
        $width = $this->getWidth();
        if ($row == null || (\is_countable($row) ? \count($row) : 0) < $width) {
            $row = [];
        }
        $offset = ($y + $this->top) * $this->dataWidth + $this->left;
        $row = arraycopy($this->luminances, $offset, $row, 0, $width);
        return $row;
    }
    //@Override
    public function getMatrix()
    {
        $width = $this->getWidth();
        $height = $this->getHeight();
        // If the caller asks for the entire underlying image, save the copy and give them the
        // original data. The docs specifically warn that result.length must be ignored.
        if ($width == $this->dataWidth && $height == $this->dataHeight) {
            return $this->luminances;
        }
        $area = $width * $height;
        $matrix = [];
        $inputOffset = $this->top * $this->dataWidth + $this->left;
        // If the width matches the full width of the underlying data, perform a single copy.
        if ($width == $this->dataWidth) {
            $matrix = arraycopy($this->luminances, $inputOffset, $matrix, 0, $area);
            return $matrix;
        }
        // Otherwise copy one cropped row at a time.
        $rgb = $this->luminances;
        for ($y = 0; $y < $height; $y++) {
            $outputOffset = $y * $width;
            $matrix = arraycopy($rgb, $inputOffset, $matrix, $outputOffset, $width);
            $inputOffset += $this->dataWidth;
        }
        return $matrix;
    }
    //@Override
    public function isCropSupported() : bool
    {
        return \true;
    }
    //@Override
    public function crop($left, $top, $width, $height)
    {
        return $this->luminances->cropImage($width, $height, $left, $top);
        return new GDLuminanceSource($this->luminances, $this->dataWidth, $this->dataHeight, $this->left + $left, $this->top + $top, $width, $height);
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/LuminanceSource.php000064400000012676150755130600021423 0ustar00<?php

/*
* Copyright 2009 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace WP2FA_Vendor\Zxing;

/**
 * The purpose of this class hierarchy is to abstract different bitmap implementations across
 * platforms into a standard interface for requesting greyscale luminance values. The interface
 * only provides immutable methods; therefore crop and rotation create copies. This is to ensure
 * that one Reader does not modify the original luminance source and leave it in an unknown state
 * for other Readers in the chain.
 *
 * @author dswitkin@google.com (Daniel Switkin)
 */
abstract class LuminanceSource
{
    public function __construct(private $width, private $height)
    {
    }
    /**
     * Fetches luminance data for the underlying bitmap. Values should be fetched using:
     * {@code int luminance = array[y * width + x] & 0xff}
     *
     * @return A row-major 2D array of luminance values. Do not use result.length as it may be
     *         larger than width * height bytes on some platforms. Do not modify the contents
     *         of the result.
     */
    public abstract function getMatrix();
    /**
     * @return float The width of the bitmap.
     */
    public final function getWidth() : float
    {
        return $this->width;
    }
    /**
     * @return float The height of the bitmap.
     */
    public final function getHeight() : float
    {
        return $this->height;
    }
    /**
     * @return bool Whether this subclass supports cropping.
     */
    public function isCropSupported() : bool
    {
        return \false;
    }
    /**
     * Returns a new object with cropped image data. Implementations may keep a reference to the
     * original data rather than a copy. Only callable if isCropSupported() is true.
     *
     * @param $left   The left coordinate, which must be in [0,getWidth())
     * @param $top    The top coordinate, which must be in [0,getHeight())
     * @param $width  The width of the rectangle to crop.
     * @param $height The height of the rectangle to crop.
     *
     * @return mixed A cropped version of this object.
     */
    public function crop($left, $top, $width, $height)
    {
        throw new \Exception("This luminance source does not support cropping.");
    }
    /**
     * @return bool Whether this subclass supports counter-clockwise rotation.
     */
    public function isRotateSupported() : bool
    {
        return \false;
    }
    /**
     * @return a wrapper of this {@code LuminanceSource} which inverts the luminances it returns -- black becomes
     *  white and vice versa, and each value becomes (255-value).
     */
    // public function invert()
    // {
    // 	return new InvertedLuminanceSource($this);
    // }
    /**
     * Returns a new object with rotated image data by 90 degrees counterclockwise.
     * Only callable if {@link #isRotateSupported()} is true.
     *
     * @return mixed A rotated version of this object.
     */
    public function rotateCounterClockwise()
    {
        throw new \Exception("This luminance source does not support rotation by 90 degrees.");
    }
    /**
     * Returns a new object with rotated image data by 45 degrees counterclockwise.
     * Only callable if {@link #isRotateSupported()} is true.
     *
     * @return mixed A rotated version of this object.
     */
    public function rotateCounterClockwise45()
    {
        throw new \Exception("This luminance source does not support rotation by 45 degrees.");
    }
    public final function toString()
    {
        $row = [];
        $result = '';
        for ($y = 0; $y < $this->height; $y++) {
            $row = $this->getRow($y, $row);
            for ($x = 0; $x < $this->width; $x++) {
                $luminance = $row[$x] & 0xff;
                $c = '';
                if ($luminance < 0x40) {
                    $c = '#';
                } elseif ($luminance < 0x80) {
                    $c = '+';
                } elseif ($luminance < 0xc0) {
                    $c = '.';
                } else {
                    $c = ' ';
                }
                $result .= $c;
            }
            $result .= '\\n';
        }
        return $result;
    }
    /**
     * Fetches one row of luminance data from the underlying platform's bitmap. Values range from
     * 0 (black) to 255 (white). Because Java does not have an unsigned byte type, callers will have
     * to bitwise and with 0xff for each value. It is preferable for implementations of this method
     * to only fetch this row rather than the whole image, since no 2D Readers may be installed and
     * getMatrix() may never be called.
     *
     * @param $y   ; The row to fetch, which must be in [0,getHeight())
     * @param $row ; An optional preallocated array. If null or too small, it will be ignored.
     *             Always use the returned object, and ignore the .length of the array.
     *
     * @return array
     * An array containing the luminance data.
     */
    public abstract function getRow($y, $row);
}
vendor/khanamiryan/qrcode-detector-decoder/lib/GDLuminanceSource.php000064400000012527150755130600021631 0ustar00<?php

namespace WP2FA_Vendor\Zxing;

/**
 * This class is used to help decode images from files which arrive as GD Resource
 * It does not support rotation.
 *
 *
 *
 */
final class GDLuminanceSource extends LuminanceSource
{
    public $luminances;
    private $dataWidth;
    private $dataHeight;
    /**
     * @var mixed|int
     */
    private $left;
    /**
     * @var mixed|int
     */
    private $top;
    /**
     * @var mixed|null
     */
    private $gdImage;
    public function __construct($gdImage, $dataWidth, $dataHeight, $left = null, $top = null, $width = null, $height = null)
    {
        if (!$left && !$top && !$width && !$height) {
            $this->GDLuminanceSource($gdImage, $dataWidth, $dataHeight);
            return;
        }
        parent::__construct($width, $height);
        if ($left + $width > $dataWidth || $top + $height > $dataHeight) {
            throw new \InvalidArgumentException("Crop rectangle does not fit within image data.");
        }
        $this->luminances = $gdImage;
        $this->dataWidth = $dataWidth;
        $this->dataHeight = $dataHeight;
        $this->left = $left;
        $this->top = $top;
    }
    public function GDLuminanceSource($gdImage, $width, $height) : void
    {
        parent::__construct($width, $height);
        $this->dataWidth = $width;
        $this->dataHeight = $height;
        $this->left = 0;
        $this->top = 0;
        $this->gdImage = $gdImage;
        // In order to measure pure decoding speed, we convert the entire image to a greyscale array
        // up front, which is the same as the Y channel of the YUVLuminanceSource in the real app.
        $this->luminances = [];
        //$this->luminances = $this->grayScaleToBitmap($this->grayscale());
        $array = [];
        $rgb = [];
        for ($j = 0; $j < $height; $j++) {
            for ($i = 0; $i < $width; $i++) {
                $argb = \imagecolorat($this->gdImage, $i, $j);
                $pixel = \imagecolorsforindex($this->gdImage, $argb);
                $r = $pixel['red'];
                $g = $pixel['green'];
                $b = $pixel['blue'];
                if ($r == $g && $g == $b) {
                    // Image is already greyscale, so pick any channel.
                    $this->luminances[] = $r;
                    //(($r + 128) % 256) - 128;
                } else {
                    // Calculate luminance cheaply, favoring green.
                    $this->luminances[] = ($r + 2 * $g + $b) / 4;
                    //(((($r + 2 * $g + $b) / 4) + 128) % 256) - 128;
                }
            }
        }
        /*
        for ($y = 0; $y < $height; $y++) {
        	$offset = $y * $width;
        	for ($x = 0; $x < $width; $x++) {
        		$pixel = $pixels[$offset + $x];
        		$r = ($pixel >> 16) & 0xff;
        		$g = ($pixel >> 8) & 0xff;
        		$b = $pixel & 0xff;
        		if ($r == $g && $g == $b) {
        // Image is already greyscale, so pick any channel.
        
        			$this->luminances[(int)($offset + $x)] = (($r+128) % 256) - 128;
        		} else {
        // Calculate luminance cheaply, favoring green.
        			$this->luminances[(int)($offset + $x)] =  (((($r + 2 * $g + $b) / 4)+128)%256) - 128;
        		}
        
        
        
        	}
        */
        //}
        //   $this->luminances = $this->grayScaleToBitmap($this->luminances);
    }
    //@Override
    public function getRow($y, $row = null)
    {
        if ($y < 0 || $y >= $this->getHeight()) {
            throw new \InvalidArgumentException('Requested row is outside the image: ' . $y);
        }
        $width = $this->getWidth();
        if ($row == null || (\is_countable($row) ? \count($row) : 0) < $width) {
            $row = [];
        }
        $offset = ($y + $this->top) * $this->dataWidth + $this->left;
        $row = arraycopy($this->luminances, $offset, $row, 0, $width);
        return $row;
    }
    //@Override
    public function getMatrix()
    {
        $width = $this->getWidth();
        $height = $this->getHeight();
        // If the caller asks for the entire underlying image, save the copy and give them the
        // original data. The docs specifically warn that result.length must be ignored.
        if ($width == $this->dataWidth && $height == $this->dataHeight) {
            return $this->luminances;
        }
        $area = $width * $height;
        $matrix = [];
        $inputOffset = $this->top * $this->dataWidth + $this->left;
        // If the width matches the full width of the underlying data, perform a single copy.
        if ($width == $this->dataWidth) {
            $matrix = arraycopy($this->luminances, $inputOffset, $matrix, 0, $area);
            return $matrix;
        }
        // Otherwise copy one cropped row at a time.
        $rgb = $this->luminances;
        for ($y = 0; $y < $height; $y++) {
            $outputOffset = $y * $width;
            $matrix = arraycopy($rgb, $inputOffset, $matrix, $outputOffset, $width);
            $inputOffset += $this->dataWidth;
        }
        return $matrix;
    }
    //@Override
    public function isCropSupported()
    {
        return \true;
    }
    //@Override
    public function crop($left, $top, $width, $height) : \WP2FA_Vendor\Zxing\GDLuminanceSource
    {
        return new GDLuminanceSource($this->luminances, $this->dataWidth, $this->dataHeight, $this->left + $left, $this->top + $top, $width, $height);
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/ResultPoint.php000064400000010415150755130600020604 0ustar00<?php

/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace WP2FA_Vendor\Zxing;

use WP2FA_Vendor\Zxing\Common\Detector\MathUtils;
/**
 * <p>Encapsulates a point of interest in an image containing a barcode. Typically, this
 * would be the location of a finder pattern or the corner of the barcode, for example.</p>
 *
 * @author Sean Owen
 */
class ResultPoint
{
    private float $x;
    private float $y;
    public function __construct($x, $y)
    {
        $this->x = (float) $x;
        $this->y = (float) $y;
    }
    /**
     * Orders an array of three ResultPoints in an order [A,B,C] such that AB is less than AC
     * and BC is less than AC, and the angle between BC and BA is less than 180 degrees.
     *
     * @param array $patterns of three {@code ResultPoint} to order
     */
    public static function orderBestPatterns($patterns)
    {
        // Find distances between pattern centers
        $zeroOneDistance = self::distance($patterns[0], $patterns[1]);
        $oneTwoDistance = self::distance($patterns[1], $patterns[2]);
        $zeroTwoDistance = self::distance($patterns[0], $patterns[2]);
        $pointA = '';
        $pointB = '';
        $pointC = '';
        // Assume one closest to other two is B; A and C will just be guesses at first
        if ($oneTwoDistance >= $zeroOneDistance && $oneTwoDistance >= $zeroTwoDistance) {
            $pointB = $patterns[0];
            $pointA = $patterns[1];
            $pointC = $patterns[2];
        } elseif ($zeroTwoDistance >= $oneTwoDistance && $zeroTwoDistance >= $zeroOneDistance) {
            $pointB = $patterns[1];
            $pointA = $patterns[0];
            $pointC = $patterns[2];
        } else {
            $pointB = $patterns[2];
            $pointA = $patterns[0];
            $pointC = $patterns[1];
        }
        // Use cross product to figure out whether A and C are correct or flipped.
        // This asks whether BC x BA has a positive z component, which is the arrangement
        // we want for A, B, C. If it's negative, then we've got it flipped around and
        // should swap A and C.
        if (self::crossProductZ($pointA, $pointB, $pointC) < 0.0) {
            $temp = $pointA;
            $pointA = $pointC;
            $pointC = $temp;
        }
        $patterns[0] = $pointA;
        $patterns[1] = $pointB;
        $patterns[2] = $pointC;
        return $patterns;
    }
    /**
     * @param first $pattern1 pattern
     * @param second $pattern2 pattern
     *
     * @return distance between two points
     */
    public static function distance($pattern1, $pattern2)
    {
        return MathUtils::distance($pattern1->x, $pattern1->y, $pattern2->x, $pattern2->y);
    }
    //@Override
    /**
     * Returns the z component of the cross product between vectors BC and BA.
     */
    private static function crossProductZ($pointA, $pointB, $pointC)
    {
        $bX = $pointB->x;
        $bY = $pointB->y;
        return ($pointC->x - $bX) * ($pointA->y - $bY) - ($pointC->y - $bY) * ($pointA->x - $bX);
    }
    //@Override
    public final function getX()
    {
        return (float) $this->x;
    }
    //@Override
    public final function getY()
    {
        return (float) $this->y;
    }
    public final function equals($other)
    {
        if ($other instanceof ResultPoint) {
            $otherPoint = $other;
            return $this->x == $otherPoint->x && $this->y == $otherPoint->y;
        }
        return \false;
    }
    public final function hashCode()
    {
        return 31 * floatToIntBits($this->x) + floatToIntBits($this->y);
    }
    public final function toString()
    {
        $result = '';
        $result .= '(';
        $result .= $this->x;
        $result .= ',';
        $result .= $this->y;
        $result .= ')';
        return $result;
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/PlanarYUVLuminanceSource.php000064400000012777150755130600023167 0ustar00<?php

/*
 * Copyright 2009 ZXing authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
namespace WP2FA_Vendor\Zxing;

/**
 * This object extends LuminanceSource around an array of YUV data returned from the camera driver,
 * with the option to crop to a rectangle within the full data. This can be used to exclude
 * superfluous pixels around the perimeter and speed up decoding.
 *
 * It works for any pixel format where the Y channel is planar and appears first, including
 * YCbCr_420_SP and YCbCr_422_SP.
 *
 * @author dswitkin@google.com (Daniel Switkin)
 */
final class PlanarYUVLuminanceSource extends LuminanceSource
{
    private static int $THUMBNAIL_SCALE_FACTOR = 2;
    private $dataWidth;
    private $dataHeight;
    private $left;
    private $top;
    public function __construct(private $yuvData, $dataWidth, $dataHeight, $left, $top, $width, $height, $reverseHorizontal)
    {
        parent::__construct($width, $height);
        if ($left + $width > $dataWidth || $top + $height > $dataHeight) {
            throw new \InvalidArgumentException("Crop rectangle does not fit within image data.");
        }
        $this->dataWidth = $dataWidth;
        $this->dataHeight = $dataHeight;
        $this->left = $left;
        $this->top = $top;
        if ($reverseHorizontal) {
            $this->reverseHorizontal($width, $height);
        }
    }
    //@Override
    public function getRow($y, $row = null)
    {
        if ($y < 0 || $y >= $this->getHeight()) {
            throw new \InvalidArgumentException("Requested row is outside the image: " + \WP2FA_Vendor\Y);
        }
        $width = $this->getWidth();
        if ($row == null || (\is_countable($row) ? \count($row) : 0) < $width) {
            $row = [];
            //new byte[width];
        }
        $offset = ($y + $this->top) * $this->dataWidth + $this->left;
        $row = arraycopy($this->yuvData, $offset, $row, 0, $width);
        return $row;
    }
    //@Override
    public function getMatrix()
    {
        $width = $this->getWidth();
        $height = $this->getHeight();
        // If the caller asks for the entire underlying image, save the copy and give them the
        // original data. The docs specifically warn that result.length must be ignored.
        if ($width == $this->dataWidth && $height == $this->dataHeight) {
            return $this->yuvData;
        }
        $area = $width * $height;
        $matrix = [];
        //new byte[area];
        $inputOffset = $this->top * $this->dataWidth + $this->left;
        // If the width matches the full width of the underlying data, perform a single copy.
        if ($width == $this->dataWidth) {
            $matrix = arraycopy($this->yuvData, $inputOffset, $matrix, 0, $area);
            return $matrix;
        }
        // Otherwise copy one cropped row at a time.
        $yuv = $this->yuvData;
        for ($y = 0; $y < $height; $y++) {
            $outputOffset = $y * $width;
            $matrix = arraycopy($this->yuvData, $inputOffset, $matrix, $outputOffset, $width);
            $inputOffset += $this->dataWidth;
        }
        return $matrix;
    }
    // @Override
    public function isCropSupported()
    {
        return \true;
    }
    // @Override
    public function crop($left, $top, $width, $height) : \WP2FA_Vendor\Zxing\PlanarYUVLuminanceSource
    {
        return new PlanarYUVLuminanceSource($this->yuvData, $this->dataWidth, $this->dataHeight, $this->left + $left, $this->top + $top, $width, $height, \false);
    }
    public function renderThumbnail()
    {
        $width = (int) ($this->getWidth() / self::$THUMBNAIL_SCALE_FACTOR);
        $height = (int) ($this->getHeight() / self::$THUMBNAIL_SCALE_FACTOR);
        $pixels = [];
        //new int[width * height];
        $yuv = $this->yuvData;
        $inputOffset = $this->top * $this->dataWidth + $this->left;
        for ($y = 0; $y < $height; $y++) {
            $outputOffset = $y * $width;
            for ($x = 0; $x < $width; $x++) {
                $grey = $yuv[$inputOffset + $x * self::$THUMBNAIL_SCALE_FACTOR] & 0xff;
                $pixels[$outputOffset + $x] = 0xff000000 | $grey * 0x10101;
            }
            $inputOffset += $this->dataWidth * self::$THUMBNAIL_SCALE_FACTOR;
        }
        return $pixels;
    }
    /**
     * @return width of image from {@link #renderThumbnail()}
     */
    /*
      public int getThumbnailWidth() {
    	return getWidth() / THUMBNAIL_SCALE_FACTOR;
      }*/
    /**
     * @return height of image from {@link #renderThumbnail()}
     */
    /*
      public int getThumbnailHeight() {
    	return getHeight() / THUMBNAIL_SCALE_FACTOR;
      }
    
      private void reverseHorizontal(int width, int height) {
    	byte[] yuvData = this.yuvData;
    	for (int y = 0, rowStart = top * dataWidth + left; y < height; y++, rowStart += dataWidth) {
    		int middle = rowStart + width / 2;
    	  for (int x1 = rowStart, x2 = rowStart + width - 1; x1 < middle; x1++, x2--) {
    			byte temp = yuvData[x1];
    		yuvData[x1] = yuvData[x2];
    		yuvData[x2] = temp;
    	  }
    	}
      }
    */
}
vendor/khanamiryan/qrcode-detector-decoder/lib/NotFoundException.php000064400000002114150755130600021724 0ustar00<?php

/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace WP2FA_Vendor\Zxing;

/**
 * Thrown when a barcode was not found in the image. It might have been
 * partially detected but could not be confirmed.
 *
 * @author Sean Owen
 */
final class NotFoundException extends ReaderException
{
    private static ?\WP2FA_Vendor\Zxing\NotFoundException $instance = null;
    public static function getNotFoundInstance()
    {
        if (!self::$instance) {
            self::$instance = new NotFoundException();
        }
        return self::$instance;
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/ChecksumException.php000064400000002360150755130600021735 0ustar00<?php

/*
 * Copyright 2007 ZXing authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
namespace WP2FA_Vendor\Zxing;

/**
 * Thrown when a barcode was successfully detected and decoded, but
 * was not returned because its checksum feature failed.
 *
 * @author Sean Owen
 */
final class ChecksumException extends ReaderException
{
    private static ?\WP2FA_Vendor\Zxing\ChecksumException $instance = null;
    public static function getChecksumInstance($cause = null)
    {
        if (self::$isStackTrace) {
            return new ChecksumException($cause);
        } else {
            if (!self::$instance) {
                self::$instance = new ChecksumException($cause);
            }
            return self::$instance;
        }
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/Binarizer.php000064400000007117150755130600020246 0ustar00<?php

/*
* Copyright 2009 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace WP2FA_Vendor\Zxing;

use WP2FA_Vendor\Zxing\Common\BitArray;
use WP2FA_Vendor\Zxing\Common\BitMatrix;
/**
 * This class hierarchy provides a set of methods to convert luminance data to 1 bit data.
 * It allows the algorithm to vary polymorphically, for example allowing a very expensive
 * thresholding technique for servers and a fast one for mobile. It also permits the implementation
 * to vary, e.g. a JNI version for Android and a Java fallback version for other platforms.
 *
 * @author dswitkin@google.com (Daniel Switkin)
 */
abstract class Binarizer
{
    protected function __construct(private $source)
    {
    }
    /**
     * @return LuminanceSource
     */
    public final function getLuminanceSource()
    {
        return $this->source;
    }
    /**
     * Converts one row of luminance data to 1 bit data. May actually do the conversion, or return
     * cached data. Callers should assume this method is expensive and call it as seldom as possible.
     * This method is intended for decoding 1D barcodes and may choose to apply sharpening.
     * For callers which only examine one row of pixels at a time, the same BitArray should be reused
     * and passed in with each call for performance. However it is legal to keep more than one row
     * at a time if needed.
     *
     * @param $y   The row to fetch, which must be in [0, bitmap height)
     * @param An $row optional preallocated array. If null or too small, it will be ignored.
     *            If used, the Binarizer will call BitArray.clear(). Always use the returned object.
     *
     * @return array The array of bits for this row (true means black).
     * @throws NotFoundException if row can't be binarized
     */
    public abstract function getBlackRow($y, $row);
    /**
     * Converts a 2D array of luminance data to 1 bit data. As above, assume this method is expensive
     * and do not call it repeatedly. This method is intended for decoding 2D barcodes and may or
     * may not apply sharpening. Therefore, a row from this matrix may not be identical to one
     * fetched using getBlackRow(), so don't mix and match between them.
     *
     * @return BitMatrix The 2D array of bits for the image (true means black).
     * @throws NotFoundException if image can't be binarized to make a matrix
     */
    public abstract function getBlackMatrix();
    /**
     * Creates a new object with the same type as this Binarizer implementation, but with pristine
     * state. This is needed because Binarizer implementations may be stateful, e.g. keeping a cache
     * of 1 bit data. See Effective Java for why we can't use Java's clone() method.
     *
     * @param $source The LuminanceSource this Binarizer will operate on.
     *
     * @return Binarizer A new concrete Binarizer implementation object.
     */
    public abstract function createBinarizer($source);
    public final function getWidth()
    {
        return $this->source->getWidth();
    }
    public final function getHeight()
    {
        return $this->source->getHeight();
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/Qrcode/Decoder/QRCodeDecoderMetaData.php000064400000000470150755130600025102 0ustar00<?php

namespace WP2FA_Vendor\Zxing\Qrcode\Decoder;

class QRCodeDecoderMetaData
{
    /**
     * QRCodeDecoderMetaData constructor.
     * @param bool $mirrored
     */
    public function __construct(private $mirrored)
    {
    }
    public function isMirrored()
    {
        return $this->mirrored;
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/Qrcode/Decoder/FormatInformation.php000064400000013635150755130600024543 0ustar00<?php

/*
 * Copyright 2007 ZXing authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
namespace WP2FA_Vendor\Zxing\Qrcode\Decoder;

/**
 * <p>Encapsulates a QR Code's format information, including the data mask used and
 * error correction level.</p>
 *
 * @author Sean Owen
 * @see    DataMask
 * @see    ErrorCorrectionLevel
 */
final class FormatInformation
{
    public static $FORMAT_INFO_MASK_QR;
    /**
     * See ISO 18004:2006, Annex C, Table C.1
     */
    public static $FORMAT_INFO_DECODE_LOOKUP;
    /**
     * Offset i holds the number of 1 bits in the binary representation of i
     * @var int[]|null
     */
    private static ?array $BITS_SET_IN_HALF_BYTE = null;
    private readonly \WP2FA_Vendor\Zxing\Qrcode\Decoder\ErrorCorrectionLevel $errorCorrectionLevel;
    private readonly int $dataMask;
    private function __construct($formatInfo)
    {
        // Bits 3,4
        $this->errorCorrectionLevel = ErrorCorrectionLevel::forBits($formatInfo >> 3 & 0x3);
        // Bottom 3 bits
        $this->dataMask = $formatInfo & 0x7;
        //(byte)
    }
    public static function Init() : void
    {
        self::$FORMAT_INFO_MASK_QR = 0x5412;
        self::$BITS_SET_IN_HALF_BYTE = [0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4];
        self::$FORMAT_INFO_DECODE_LOOKUP = [[0x5412, 0x0], [0x5125, 0x1], [0x5e7c, 0x2], [0x5b4b, 0x3], [0x45f9, 0x4], [0x40ce, 0x5], [0x4f97, 0x6], [0x4aa0, 0x7], [0x77c4, 0x8], [0x72f3, 0x9], [0x7daa, 0xa], [0x789d, 0xb], [0x662f, 0xc], [0x6318, 0xd], [0x6c41, 0xe], [0x6976, 0xf], [0x1689, 0x10], [0x13be, 0x11], [0x1ce7, 0x12], [0x19d0, 0x13], [0x762, 0x14], [0x255, 0x15], [0xd0c, 0x16], [0x83b, 0x17], [0x355f, 0x18], [0x3068, 0x19], [0x3f31, 0x1a], [0x3a06, 0x1b], [0x24b4, 0x1c], [0x2183, 0x1d], [0x2eda, 0x1e], [0x2bed, 0x1f]];
    }
    /**
     * @param $maskedFormatInfo1 ; format info indicator, with mask still applied
     * @param $maskedFormatInfo2 ; second copy of same info; both are checked at the same time
     *                          to establish best match
     *
     * @return information about the format it specifies, or {@code null}
     *  if doesn't seem to match any known pattern
     */
    public static function decodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2)
    {
        $formatInfo = self::doDecodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2);
        if ($formatInfo != null) {
            return $formatInfo;
        }
        // Should return null, but, some QR codes apparently
        // do not mask this info. Try again by actually masking the pattern
        // first
        return self::doDecodeFormatInformation($maskedFormatInfo1 ^ self::$FORMAT_INFO_MASK_QR, $maskedFormatInfo2 ^ self::$FORMAT_INFO_MASK_QR);
    }
    private static function doDecodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2)
    {
        // Find the int in FORMAT_INFO_DECODE_LOOKUP with fewest bits differing
        $bestDifference = \PHP_INT_MAX;
        $bestFormatInfo = 0;
        foreach (self::$FORMAT_INFO_DECODE_LOOKUP as $decodeInfo) {
            $targetInfo = $decodeInfo[0];
            if ($targetInfo == $maskedFormatInfo1 || $targetInfo == $maskedFormatInfo2) {
                // Found an exact match
                return new FormatInformation($decodeInfo[1]);
            }
            $bitsDifference = self::numBitsDiffering($maskedFormatInfo1, $targetInfo);
            if ($bitsDifference < $bestDifference) {
                $bestFormatInfo = $decodeInfo[1];
                $bestDifference = $bitsDifference;
            }
            if ($maskedFormatInfo1 != $maskedFormatInfo2) {
                // also try the other option
                $bitsDifference = self::numBitsDiffering($maskedFormatInfo2, $targetInfo);
                if ($bitsDifference < $bestDifference) {
                    $bestFormatInfo = $decodeInfo[1];
                    $bestDifference = $bitsDifference;
                }
            }
        }
        // Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits
        // differing means we found a match
        if ($bestDifference <= 3) {
            return new FormatInformation($bestFormatInfo);
        }
        return null;
    }
    public static function numBitsDiffering($a, $b)
    {
        $a ^= $b;
        // a now has a 1 bit exactly where its bit differs with b's
        // Count bits set quickly with a series of lookups:
        return self::$BITS_SET_IN_HALF_BYTE[$a & 0xf] + self::$BITS_SET_IN_HALF_BYTE[(int) (uRShift($a, 4) & 0xf)] + self::$BITS_SET_IN_HALF_BYTE[uRShift($a, 8) & 0xf] + self::$BITS_SET_IN_HALF_BYTE[uRShift($a, 12) & 0xf] + self::$BITS_SET_IN_HALF_BYTE[uRShift($a, 16) & 0xf] + self::$BITS_SET_IN_HALF_BYTE[uRShift($a, 20) & 0xf] + self::$BITS_SET_IN_HALF_BYTE[uRShift($a, 24) & 0xf] + self::$BITS_SET_IN_HALF_BYTE[uRShift($a, 28) & 0xf];
    }
    public function getErrorCorrectionLevel()
    {
        return $this->errorCorrectionLevel;
    }
    public function getDataMask()
    {
        return $this->dataMask;
    }
    //@Override
    public function hashCode()
    {
        return $this->errorCorrectionLevel->ordinal() << 3 | (int) $this->dataMask;
    }
    //@Override
    public function equals($o)
    {
        if (!$o instanceof FormatInformation) {
            return \false;
        }
        $other = $o;
        return $this->errorCorrectionLevel == $other->errorCorrectionLevel && $this->dataMask == $other->dataMask;
    }
}
FormatInformation::Init();
vendor/khanamiryan/qrcode-detector-decoder/lib/Qrcode/Decoder/DataBlock.php000064400000011606150755130600022725 0ustar00<?php

/*
 * Copyright 2007 ZXing authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
namespace WP2FA_Vendor\Zxing\Qrcode\Decoder;

/**
 * <p>Encapsulates a block of data within a QR Code. QR Codes may split their data into
 * multiple blocks, each of which is a unit of data and error-correction codewords. Each
 * is represented by an instance of this class.</p>
 *
 * @author Sean Owen
 */
final class DataBlock
{
    //byte[]
    private function __construct(private $numDataCodewords, private $codewords)
    {
    }
    /**
     * <p>When QR Codes use multiple data blocks, they are actually interleaved.
     * That is, the first byte of data block 1 to n is written, then the second bytes, and so on. This
     * method will separate the data into original blocks.</p>
     *
     * @param bytes $rawCodewords as read directly from the QR Code
     * @param version      $version of the QR Code
     * @param error      $ecLevel-correction level of the QR Code
     *
     * @return array DataBlocks containing original bytes, "de-interleaved" from representation in the
     *         QR Code
     */
    public static function getDataBlocks($rawCodewords, $version, $ecLevel)
    {
        if ((\is_countable($rawCodewords) ? \count($rawCodewords) : 0) != $version->getTotalCodewords()) {
            throw new \InvalidArgumentException();
        }
        // Figure out the number and size of data blocks used by this version and
        // error correction level
        $ecBlocks = $version->getECBlocksForLevel($ecLevel);
        // First count the total number of data blocks
        $totalBlocks = 0;
        $ecBlockArray = $ecBlocks->getECBlocks();
        foreach ($ecBlockArray as $ecBlock) {
            $totalBlocks += $ecBlock->getCount();
        }
        // Now establish DataBlocks of the appropriate size and number of data codewords
        $result = [];
        //new DataBlock[$totalBlocks];
        $numResultBlocks = 0;
        foreach ($ecBlockArray as $ecBlock) {
            $ecBlockCount = $ecBlock->getCount();
            for ($i = 0; $i < $ecBlockCount; $i++) {
                $numDataCodewords = $ecBlock->getDataCodewords();
                $numBlockCodewords = $ecBlocks->getECCodewordsPerBlock() + $numDataCodewords;
                $result[$numResultBlocks++] = new DataBlock($numDataCodewords, fill_array(0, $numBlockCodewords, 0));
            }
        }
        // All blocks have the same amount of data, except that the last n
        // (where n may be 0) have 1 more byte. Figure out where these start.
        $shorterBlocksTotalCodewords = \is_countable($result[0]->codewords) ? \count($result[0]->codewords) : 0;
        $longerBlocksStartAt = \count($result) - 1;
        while ($longerBlocksStartAt >= 0) {
            $numCodewords = \is_countable($result[$longerBlocksStartAt]->codewords) ? \count($result[$longerBlocksStartAt]->codewords) : 0;
            if ($numCodewords == $shorterBlocksTotalCodewords) {
                break;
            }
            $longerBlocksStartAt--;
        }
        $longerBlocksStartAt++;
        $shorterBlocksNumDataCodewords = $shorterBlocksTotalCodewords - $ecBlocks->getECCodewordsPerBlock();
        // The last elements of result may be 1 element longer;
        // first fill out as many elements as all of them have
        $rawCodewordsOffset = 0;
        for ($i = 0; $i < $shorterBlocksNumDataCodewords; $i++) {
            for ($j = 0; $j < $numResultBlocks; $j++) {
                $result[$j]->codewords[$i] = $rawCodewords[$rawCodewordsOffset++];
            }
        }
        // Fill out the last data block in the longer ones
        for ($j = $longerBlocksStartAt; $j < $numResultBlocks; $j++) {
            $result[$j]->codewords[$shorterBlocksNumDataCodewords] = $rawCodewords[$rawCodewordsOffset++];
        }
        // Now add in error correction blocks
        $max = \is_countable($result[0]->codewords) ? \count($result[0]->codewords) : 0;
        for ($i = $shorterBlocksNumDataCodewords; $i < $max; $i++) {
            for ($j = 0; $j < $numResultBlocks; $j++) {
                $iOffset = $j < $longerBlocksStartAt ? $i : $i + 1;
                $result[$j]->codewords[$iOffset] = $rawCodewords[$rawCodewordsOffset++];
            }
        }
        return $result;
    }
    public function getNumDataCodewords()
    {
        return $this->numDataCodewords;
    }
    public function getCodewords()
    {
        return $this->codewords;
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/Qrcode/Decoder/ErrorCorrectionLevel.php000064400000004617150755130600025216 0ustar00<?php

/*
 * Copyright 2007 ZXing authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
namespace WP2FA_Vendor\Zxing\Qrcode\Decoder;

/**
 * <p>See ISO 18004:2006, 6.5.1. This enum encapsulates the four error correction levels
 * defined by the QR code standard.</p>
 *
 * @author Sean Owen
 */
class ErrorCorrectionLevel
{
    /**
     * @var \Zxing\Qrcode\Decoder\ErrorCorrectionLevel[]|null
     */
    private static ?array $FOR_BITS = null;
    public function __construct(private $bits, private $ordinal = 0)
    {
    }
    public static function Init() : void
    {
        self::$FOR_BITS = [
            new ErrorCorrectionLevel(0x0, 1),
            //M
            new ErrorCorrectionLevel(0x1, 0),
            //L
            new ErrorCorrectionLevel(0x2, 3),
            //H
            new ErrorCorrectionLevel(0x3, 2),
        ];
    }
    /** L = ~7% correction */
    //  self::$L = new ErrorCorrectionLevel(0x01);
    /** M = ~15% correction */
    //self::$M = new ErrorCorrectionLevel(0x00);
    /** Q = ~25% correction */
    //self::$Q = new ErrorCorrectionLevel(0x03);
    /** H = ~30% correction */
    //self::$H = new ErrorCorrectionLevel(0x02);
    /**
     * @param int $bits containing the two bits encoding a QR Code's error correction level
     *
     * @return ErrorCorrectionLevel representing the encoded error correction level
     */
    public static function forBits($bits)
    {
        if ($bits < 0 || $bits >= (\is_countable(self::$FOR_BITS) ? \count(self::$FOR_BITS) : 0)) {
            throw new \InvalidArgumentException();
        }
        $level = self::$FOR_BITS[$bits];
        // $lev = self::$$bit;
        return $level;
    }
    public function getBits()
    {
        return $this->bits;
    }
    public function toString()
    {
        return $this->bits;
    }
    public function getOrdinal()
    {
        return $this->ordinal;
    }
}
ErrorCorrectionLevel::Init();
vendor/khanamiryan/qrcode-detector-decoder/lib/Qrcode/Decoder/DecodedBitStreamParser.php000064400000033124150755130600025417 0ustar00<?php

/*
 * Copyright 2007 ZXing authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
namespace WP2FA_Vendor\Zxing\Qrcode\Decoder;

use WP2FA_Vendor\Zxing\Common\BitSource;
use WP2FA_Vendor\Zxing\Common\CharacterSetECI;
use WP2FA_Vendor\Zxing\Common\DecoderResult;
use WP2FA_Vendor\Zxing\FormatException;
/**
 * <p>QR Codes can encode text as bits in one of several modes, and can use multiple modes
 * in one QR Code. This class decodes the bits back into text.</p>
 *
 * <p>See ISO 18004:2006, 6.4.3 - 6.4.7</p>
 *
 * @author Sean Owen
 */
final class DecodedBitStreamParser
{
    /**
     * See ISO 18004:2006, 6.4.4 Table 5
     */
    private static array $ALPHANUMERIC_CHARS = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', '$', '%', '*', '+', '-', '.', '/', ':'];
    private static int $GB2312_SUBSET = 1;
    public static function decode($bytes, $version, $ecLevel, $hints) : \WP2FA_Vendor\Zxing\Common\DecoderResult
    {
        $bits = new BitSource($bytes);
        $result = '';
        //new StringBuilder(50);
        $byteSegments = [];
        $symbolSequence = -1;
        $parityData = -1;
        try {
            $currentCharacterSetECI = null;
            $fc1InEffect = \false;
            $mode = '';
            do {
                // While still another segment to read...
                if ($bits->available() < 4) {
                    // OK, assume we're done. Really, a TERMINATOR mode should have been recorded here
                    $mode = Mode::$TERMINATOR;
                } else {
                    $mode = Mode::forBits($bits->readBits(4));
                    // mode is encoded by 4 bits
                }
                if ($mode != Mode::$TERMINATOR) {
                    if ($mode == Mode::$FNC1_FIRST_POSITION || $mode == Mode::$FNC1_SECOND_POSITION) {
                        // We do little with FNC1 except alter the parsed result a bit according to the spec
                        $fc1InEffect = \true;
                    } elseif ($mode == Mode::$STRUCTURED_APPEND) {
                        if ($bits->available() < 16) {
                            throw FormatException::getFormatInstance();
                        }
                        // sequence number and parity is added later to the result metadata
                        // Read next 8 bits (symbol sequence #) and 8 bits (parity data), then continue
                        $symbolSequence = $bits->readBits(8);
                        $parityData = $bits->readBits(8);
                    } elseif ($mode == Mode::$ECI) {
                        // Count doesn't apply to ECI
                        $value = self::parseECIValue($bits);
                        $currentCharacterSetECI = CharacterSetECI::getCharacterSetECIByValue($value);
                        if ($currentCharacterSetECI == null) {
                            throw FormatException::getFormatInstance();
                        }
                    } else {
                        // First handle Hanzi mode which does not start with character count
                        if ($mode == Mode::$HANZI) {
                            //chinese mode contains a sub set indicator right after mode indicator
                            $subset = $bits->readBits(4);
                            $countHanzi = $bits->readBits($mode->getCharacterCountBits($version));
                            if ($subset == self::$GB2312_SUBSET) {
                                self::decodeHanziSegment($bits, $result, $countHanzi);
                            }
                        } else {
                            // "Normal" QR code modes:
                            // How many characters will follow, encoded in this mode?
                            $count = $bits->readBits($mode->getCharacterCountBits($version));
                            if ($mode == Mode::$NUMERIC) {
                                self::decodeNumericSegment($bits, $result, $count);
                            } elseif ($mode == Mode::$ALPHANUMERIC) {
                                self::decodeAlphanumericSegment($bits, $result, $count, $fc1InEffect);
                            } elseif ($mode == Mode::$BYTE) {
                                self::decodeByteSegment($bits, $result, $count, $currentCharacterSetECI, $byteSegments, $hints);
                            } elseif ($mode == Mode::$KANJI) {
                                self::decodeKanjiSegment($bits, $result, $count);
                            } else {
                                throw FormatException::getFormatInstance();
                            }
                        }
                    }
                }
            } while ($mode != Mode::$TERMINATOR);
        } catch (\InvalidArgumentException) {
            // from readBits() calls
            throw FormatException::getFormatInstance();
        }
        return new DecoderResult(
            $bytes,
            $result,
            empty($byteSegments) ? null : $byteSegments,
            $ecLevel == null ? null : 'L',
            //ErrorCorrectionLevel::toString($ecLevel),
            $symbolSequence,
            $parityData
        );
    }
    private static function parseECIValue($bits)
    {
        $firstByte = $bits->readBits(8);
        if (($firstByte & 0x80) == 0) {
            // just one byte
            return $firstByte & 0x7f;
        }
        if (($firstByte & 0xc0) == 0x80) {
            // two bytes
            $secondByte = $bits->readBits(8);
            return ($firstByte & 0x3f) << 8 | $secondByte;
        }
        if (($firstByte & 0xe0) == 0xc0) {
            // three bytes
            $secondThirdBytes = $bits->readBits(16);
            return ($firstByte & 0x1f) << 16 | $secondThirdBytes;
        }
        throw FormatException::getFormatInstance();
    }
    /**
     * See specification GBT 18284-2000
     */
    private static function decodeHanziSegment($bits, &$result, $count)
    {
        // Don't crash trying to read more bits than we have available.
        if ($count * 13 > $bits->available()) {
            throw FormatException::getFormatInstance();
        }
        // Each character will require 2 bytes. Read the characters as 2-byte pairs
        // and decode as GB2312 afterwards
        $buffer = fill_array(0, 2 * $count, 0);
        $offset = 0;
        while ($count > 0) {
            // Each 13 bits encodes a 2-byte character
            $twoBytes = $bits->readBits(13);
            $assembledTwoBytes = $twoBytes / 0x60 << 8 | $twoBytes % 0x60;
            if ($assembledTwoBytes < 0x3bf) {
                // In the 0xA1A1 to 0xAAFE range
                $assembledTwoBytes += 0xa1a1;
            } else {
                // In the 0xB0A1 to 0xFAFE range
                $assembledTwoBytes += 0xa6a1;
            }
            $buffer[$offset] = $assembledTwoBytes >> 8 & 0xff;
            //(byte)
            $buffer[$offset + 1] = $assembledTwoBytes & 0xff;
            //(byte)
            $offset += 2;
            $count--;
        }
        $result .= \iconv('GB2312', 'UTF-8', \implode($buffer));
    }
    private static function decodeNumericSegment($bits, &$result, $count)
    {
        // Read three digits at a time
        while ($count >= 3) {
            // Each 10 bits encodes three digits
            if ($bits->available() < 10) {
                throw FormatException::getFormatInstance();
            }
            $threeDigitsBits = $bits->readBits(10);
            if ($threeDigitsBits >= 1000) {
                throw FormatException::getFormatInstance();
            }
            $result .= self::toAlphaNumericChar($threeDigitsBits / 100);
            $result .= self::toAlphaNumericChar($threeDigitsBits / 10 % 10);
            $result .= self::toAlphaNumericChar($threeDigitsBits % 10);
            $count -= 3;
        }
        if ($count == 2) {
            // Two digits left over to read, encoded in 7 bits
            if ($bits->available() < 7) {
                throw FormatException::getFormatInstance();
            }
            $twoDigitsBits = $bits->readBits(7);
            if ($twoDigitsBits >= 100) {
                throw FormatException::getFormatInstance();
            }
            $result .= self::toAlphaNumericChar($twoDigitsBits / 10);
            $result .= self::toAlphaNumericChar($twoDigitsBits % 10);
        } elseif ($count == 1) {
            // One digit left over to read
            if ($bits->available() < 4) {
                throw FormatException::getFormatInstance();
            }
            $digitBits = $bits->readBits(4);
            if ($digitBits >= 10) {
                throw FormatException::getFormatInstance();
            }
            $result .= self::toAlphaNumericChar($digitBits);
        }
    }
    private static function toAlphaNumericChar($value)
    {
        if ($value >= \count(self::$ALPHANUMERIC_CHARS)) {
            throw FormatException::getFormatInstance();
        }
        return self::$ALPHANUMERIC_CHARS[$value];
    }
    private static function decodeAlphanumericSegment($bits, &$result, $count, $fc1InEffect)
    {
        // Read two characters at a time
        $start = \strlen((string) $result);
        while ($count > 1) {
            if ($bits->available() < 11) {
                throw FormatException::getFormatInstance();
            }
            $nextTwoCharsBits = $bits->readBits(11);
            $result .= self::toAlphaNumericChar($nextTwoCharsBits / 45);
            $result .= self::toAlphaNumericChar($nextTwoCharsBits % 45);
            $count -= 2;
        }
        if ($count == 1) {
            // special case: one character left
            if ($bits->available() < 6) {
                throw FormatException::getFormatInstance();
            }
            $result .= self::toAlphaNumericChar($bits->readBits(6));
        }
        // See section 6.4.8.1, 6.4.8.2
        if ($fc1InEffect) {
            // We need to massage the result a bit if in an FNC1 mode:
            for ($i = $start; $i < \strlen((string) $result); $i++) {
                if ($result[$i] == '%') {
                    if ($i < \strlen((string) $result) - 1 && $result[$i + 1] == '%') {
                        // %% is rendered as %
                        $result = \substr_replace($result, '', $i + 1, 1);
                        //deleteCharAt(i + 1);
                    } else {
                        // In alpha mode, % should be converted to FNC1 separator 0x1D
                        $result . setCharAt($i, \chr(0x1d));
                    }
                }
            }
        }
    }
    private static function decodeByteSegment($bits, &$result, $count, $currentCharacterSetECI, &$byteSegments, $hints)
    {
        // Don't crash trying to read more bits than we have available.
        if (8 * $count > $bits->available()) {
            throw FormatException::getFormatInstance();
        }
        $readBytes = fill_array(0, $count, 0);
        for ($i = 0; $i < $count; $i++) {
            $readBytes[$i] = $bits->readBits(8);
            //(byte)
        }
        $text = \implode(\array_map('chr', $readBytes));
        $encoding = '';
        if ($currentCharacterSetECI == null) {
            // The spec isn't clear on this mode; see
            // section 6.4.5: t does not say which encoding to assuming
            // upon decoding. I have seen ISO-8859-1 used as well as
            // Shift_JIS -- without anything like an ECI designator to
            // give a hint.
            $encoding = \mb_detect_encoding($text, $hints);
        } else {
            $encoding = $currentCharacterSetECI->name();
        }
        //  $result.= mb_convert_encoding($text ,$encoding);//(new String(readBytes, encoding));
        $result .= $text;
        //(new String(readBytes, encoding));
        $byteSegments = \array_merge($byteSegments, $readBytes);
    }
    private static function decodeKanjiSegment($bits, &$result, $count)
    {
        // Don't crash trying to read more bits than we have available.
        if ($count * 13 > $bits->available()) {
            throw FormatException::getFormatInstance();
        }
        // Each character will require 2 bytes. Read the characters as 2-byte pairs
        // and decode as Shift_JIS afterwards
        $buffer = [0, 2 * $count, 0];
        $offset = 0;
        while ($count > 0) {
            // Each 13 bits encodes a 2-byte character
            $twoBytes = $bits->readBits(13);
            $assembledTwoBytes = $twoBytes / 0xc0 << 8 | $twoBytes % 0xc0;
            if ($assembledTwoBytes < 0x1f00) {
                // In the 0x8140 to 0x9FFC range
                $assembledTwoBytes += 0x8140;
            } else {
                // In the 0xE040 to 0xEBBF range
                $assembledTwoBytes += 0xc140;
            }
            $buffer[$offset] = $assembledTwoBytes >> 8;
            //(byte)
            $buffer[$offset + 1] = $assembledTwoBytes;
            //(byte)
            $offset += 2;
            $count--;
        }
        // Shift_JIS may not be supported in some environments:
        $result .= \iconv('shift-jis', 'utf-8', \implode($buffer));
    }
    private function DecodedBitStreamParser() : void
    {
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/Qrcode/Decoder/Mode.php000064400000006621150755130600021766 0ustar00<?php

/*
 * Copyright 2007 ZXing authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
namespace WP2FA_Vendor\Zxing\Qrcode\Decoder;

/**
 * <p>See ISO 18004:2006, 6.4.1, Tables 2 and 3. This enum encapsulates the various modes in which
 * data can be encoded to bits in the QR code standard.</p>
 *
 * @author Sean Owen
 */
class Mode
{
    public static $TERMINATOR;
    public static $NUMERIC;
    public static $ALPHANUMERIC;
    public static $STRUCTURED_APPEND;
    public static $BYTE;
    public static $ECI;
    public static $KANJI;
    public static $FNC1_FIRST_POSITION;
    public static $FNC1_SECOND_POSITION;
    public static $HANZI;
    public function __construct(private $characterCountBitsForVersions, private $bits)
    {
    }
    public static function Init() : void
    {
        self::$TERMINATOR = new Mode([0, 0, 0], 0x0);
        // Not really a mode...
        self::$NUMERIC = new Mode([10, 12, 14], 0x1);
        self::$ALPHANUMERIC = new Mode([9, 11, 13], 0x2);
        self::$STRUCTURED_APPEND = new Mode([0, 0, 0], 0x3);
        // Not supported
        self::$BYTE = new Mode([8, 16, 16], 0x4);
        self::$ECI = new Mode([0, 0, 0], 0x7);
        // character counts don't apply
        self::$KANJI = new Mode([8, 10, 12], 0x8);
        self::$FNC1_FIRST_POSITION = new Mode([0, 0, 0], 0x5);
        self::$FNC1_SECOND_POSITION = new Mode([0, 0, 0], 0x9);
        /** See GBT 18284-2000; "Hanzi" is a transliteration of this mode name. */
        self::$HANZI = new Mode([8, 10, 12], 0xd);
    }
    /**
     * @param four $bits bits encoding a QR Code data mode
     *
     * @return Mode encoded by these bits
     * @throws InvalidArgumentException if bits do not correspond to a known mode
     */
    public static function forBits($bits)
    {
        return match ($bits) {
            0x0 => self::$TERMINATOR,
            0x1 => self::$NUMERIC,
            0x2 => self::$ALPHANUMERIC,
            0x3 => self::$STRUCTURED_APPEND,
            0x4 => self::$BYTE,
            0x5 => self::$FNC1_FIRST_POSITION,
            0x7 => self::$ECI,
            0x8 => self::$KANJI,
            0x9 => self::$FNC1_SECOND_POSITION,
            0xd => self::$HANZI,
            default => throw new \InvalidArgumentException(),
        };
    }
    /**
     * @param version $version in question
     *
     * @return number of bits used, in this QR Code symbol {@link Version}, to encode the
     *         count of characters that will follow encoded in this Mode
     */
    public function getCharacterCountBits($version)
    {
        $number = $version->getVersionNumber();
        $offset = 0;
        if ($number <= 9) {
            $offset = 0;
        } elseif ($number <= 26) {
            $offset = 1;
        } else {
            $offset = 2;
        }
        return $this->characterCountBitsForVersions[$offset];
    }
    public function getBits()
    {
        return $this->bits;
    }
}
Mode::Init();
vendor/khanamiryan/qrcode-detector-decoder/lib/Qrcode/Decoder/Version.php000064400000042204150755130600022524 0ustar00<?php

/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace WP2FA_Vendor\Zxing\Qrcode\Decoder;

use WP2FA_Vendor\Zxing\Common\BitMatrix;
use WP2FA_Vendor\Zxing\FormatException;
/**
 * See ISO 18004:2006 Annex D
 *
 * @author Sean Owen
 */
class Version
{
    /**
     * See ISO 18004:2006 Annex D.
     * Element i represents the raw version bits that specify version i + 7
     */
    private static array $VERSION_DECODE_INFO = [0x7c94, 0x85bc, 0x9a99, 0xa4d3, 0xbbf6, 0xc762, 0xd847, 0xe60d, 0xf928, 0x10b78, 0x1145d, 0x12a17, 0x13532, 0x149a6, 0x15683, 0x168c9, 0x177ec, 0x18ec4, 0x191e1, 0x1afab, 0x1b08e, 0x1cc1a, 0x1d33f, 0x1ed75, 0x1f250, 0x209d5, 0x216f0, 0x228ba, 0x2379f, 0x24b0b, 0x2542e, 0x26a64, 0x27541, 0x28c69];
    /**
     * @var mixed|null
     */
    private static $VERSIONS;
    private readonly float|int $totalCodewords;
    public function __construct(private $versionNumber, private $alignmentPatternCenters, private $ecBlocks)
    {
        $total = 0;
        if (\is_array($ecBlocks)) {
            $ecCodewords = $ecBlocks[0]->getECCodewordsPerBlock();
            $ecbArray = $ecBlocks[0]->getECBlocks();
        } else {
            $ecCodewords = $ecBlocks->getECCodewordsPerBlock();
            $ecbArray = $ecBlocks->getECBlocks();
        }
        foreach ($ecbArray as $ecBlock) {
            $total += $ecBlock->getCount() * ($ecBlock->getDataCodewords() + $ecCodewords);
        }
        $this->totalCodewords = $total;
    }
    public function getVersionNumber()
    {
        return $this->versionNumber;
    }
    public function getAlignmentPatternCenters()
    {
        return $this->alignmentPatternCenters;
    }
    public function getTotalCodewords()
    {
        return $this->totalCodewords;
    }
    public function getDimensionForVersion()
    {
        return 17 + 4 * $this->versionNumber;
    }
    public function getECBlocksForLevel($ecLevel)
    {
        return $this->ecBlocks[$ecLevel->getOrdinal()];
    }
    /**
     * <p>Deduces version information purely from QR Code dimensions.</p>
     *
     * @param dimension $dimension in modules
     * @return Version for a QR Code of that dimension
     * @throws FormatException if dimension is not 1 mod 4
     */
    public static function getProvisionalVersionForDimension($dimension)
    {
        if ($dimension % 4 != 1) {
            throw FormatException::getFormatInstance();
        }
        try {
            return self::getVersionForNumber(($dimension - 17) / 4);
        } catch (\InvalidArgumentException) {
            throw FormatException::getFormatInstance();
        }
    }
    public static function getVersionForNumber($versionNumber)
    {
        if ($versionNumber < 1 || $versionNumber > 40) {
            throw new \InvalidArgumentException();
        }
        if (!self::$VERSIONS) {
            self::$VERSIONS = self::buildVersions();
        }
        return self::$VERSIONS[$versionNumber - 1];
    }
    public static function decodeVersionInformation($versionBits)
    {
        $bestDifference = \PHP_INT_MAX;
        $bestVersion = 0;
        for ($i = 0; $i < \count(self::$VERSION_DECODE_INFO); $i++) {
            $targetVersion = self::$VERSION_DECODE_INFO[$i];
            // Do the version info bits match exactly? done.
            if ($targetVersion == $versionBits) {
                return self::getVersionForNumber($i + 7);
            }
            // Otherwise see if this is the closest to a real version info bit string
            // we have seen so far
            $bitsDifference = FormatInformation::numBitsDiffering($versionBits, $targetVersion);
            if ($bitsDifference < $bestDifference) {
                $bestVersion = $i + 7;
                $bestDifference = $bitsDifference;
            }
        }
        // We can tolerate up to 3 bits of error since no two version info codewords will
        // differ in less than 8 bits.
        if ($bestDifference <= 3) {
            return self::getVersionForNumber($bestVersion);
        }
        // If we didn't find a close enough match, fail
        return null;
    }
    /**
     * See ISO 18004:2006 Annex E
     */
    public function buildFunctionPattern()
    {
        $dimension = self::getDimensionForVersion();
        $bitMatrix = new BitMatrix($dimension);
        // Top left finder pattern + separator + format
        $bitMatrix->setRegion(0, 0, 9, 9);
        // Top right finder pattern + separator + format
        $bitMatrix->setRegion($dimension - 8, 0, 8, 9);
        // Bottom left finder pattern + separator + format
        $bitMatrix->setRegion(0, $dimension - 8, 9, 8);
        // Alignment patterns
        $max = \is_countable($this->alignmentPatternCenters) ? \count($this->alignmentPatternCenters) : 0;
        for ($x = 0; $x < $max; $x++) {
            $i = $this->alignmentPatternCenters[$x] - 2;
            for ($y = 0; $y < $max; $y++) {
                if ($x == 0 && ($y == 0 || $y == $max - 1) || $x == $max - 1 && $y == 0) {
                    // No alignment patterns near the three finder paterns
                    continue;
                }
                $bitMatrix->setRegion($this->alignmentPatternCenters[$y] - 2, $i, 5, 5);
            }
        }
        // Vertical timing pattern
        $bitMatrix->setRegion(6, 9, 1, $dimension - 17);
        // Horizontal timing pattern
        $bitMatrix->setRegion(9, 6, $dimension - 17, 1);
        if ($this->versionNumber > 6) {
            // Version info, top right
            $bitMatrix->setRegion($dimension - 11, 0, 3, 6);
            // Version info, bottom left
            $bitMatrix->setRegion(0, $dimension - 11, 6, 3);
        }
        return $bitMatrix;
    }
    /**
     * See ISO 18004:2006 6.5.1 Table 9
     */
    private static function buildVersions()
    {
        return [new Version(1, [], [new ECBlocks(7, [new ECB(1, 19)]), new ECBlocks(10, [new ECB(1, 16)]), new ECBlocks(13, [new ECB(1, 13)]), new ECBlocks(17, [new ECB(1, 9)])]), new Version(2, [6, 18], [new ECBlocks(10, [new ECB(1, 34)]), new ECBlocks(16, [new ECB(1, 28)]), new ECBlocks(22, [new ECB(1, 22)]), new ECBlocks(28, [new ECB(1, 16)])]), new Version(3, [6, 22], [new ECBlocks(15, [new ECB(1, 55)]), new ECBlocks(26, [new ECB(1, 44)]), new ECBlocks(18, [new ECB(2, 17)]), new ECBlocks(22, [new ECB(2, 13)])]), new Version(4, [6, 26], [new ECBlocks(20, [new ECB(1, 80)]), new ECBlocks(18, [new ECB(2, 32)]), new ECBlocks(26, [new ECB(2, 24)]), new ECBlocks(16, [new ECB(4, 9)])]), new Version(5, [6, 30], [new ECBlocks(26, [new ECB(1, 108)]), new ECBlocks(24, [new ECB(2, 43)]), new ECBlocks(18, [new ECB(2, 15), new ECB(2, 16)]), new ECBlocks(22, [new ECB(2, 11), new ECB(2, 12)])]), new Version(6, [6, 34], [new ECBlocks(18, [new ECB(2, 68)]), new ECBlocks(16, [new ECB(4, 27)]), new ECBlocks(24, [new ECB(4, 19)]), new ECBlocks(28, [new ECB(4, 15)])]), new Version(7, [6, 22, 38], [new ECBlocks(20, [new ECB(2, 78)]), new ECBlocks(18, [new ECB(4, 31)]), new ECBlocks(18, [new ECB(2, 14), new ECB(4, 15)]), new ECBlocks(26, [new ECB(4, 13), new ECB(1, 14)])]), new Version(8, [6, 24, 42], [new ECBlocks(24, [new ECB(2, 97)]), new ECBlocks(22, [new ECB(2, 38), new ECB(2, 39)]), new ECBlocks(22, [new ECB(4, 18), new ECB(2, 19)]), new ECBlocks(26, [new ECB(4, 14), new ECB(2, 15)])]), new Version(9, [6, 26, 46], [new ECBlocks(30, [new ECB(2, 116)]), new ECBlocks(22, [new ECB(3, 36), new ECB(2, 37)]), new ECBlocks(20, [new ECB(4, 16), new ECB(4, 17)]), new ECBlocks(24, [new ECB(4, 12), new ECB(4, 13)])]), new Version(10, [6, 28, 50], [new ECBlocks(18, [new ECB(2, 68), new ECB(2, 69)]), new ECBlocks(26, [new ECB(4, 43), new ECB(1, 44)]), new ECBlocks(24, [new ECB(6, 19), new ECB(2, 20)]), new ECBlocks(28, [new ECB(6, 15), new ECB(2, 16)])]), new Version(11, [6, 30, 54], [new ECBlocks(20, [new ECB(4, 81)]), new ECBlocks(30, [new ECB(1, 50), new ECB(4, 51)]), new ECBlocks(28, [new ECB(4, 22), new ECB(4, 23)]), new ECBlocks(24, [new ECB(3, 12), new ECB(8, 13)])]), new Version(12, [6, 32, 58], [new ECBlocks(24, [new ECB(2, 92), new ECB(2, 93)]), new ECBlocks(22, [new ECB(6, 36), new ECB(2, 37)]), new ECBlocks(26, [new ECB(4, 20), new ECB(6, 21)]), new ECBlocks(28, [new ECB(7, 14), new ECB(4, 15)])]), new Version(13, [6, 34, 62], [new ECBlocks(26, [new ECB(4, 107)]), new ECBlocks(22, [new ECB(8, 37), new ECB(1, 38)]), new ECBlocks(24, [new ECB(8, 20), new ECB(4, 21)]), new ECBlocks(22, [new ECB(12, 11), new ECB(4, 12)])]), new Version(14, [6, 26, 46, 66], [new ECBlocks(30, [new ECB(3, 115), new ECB(1, 116)]), new ECBlocks(24, [new ECB(4, 40), new ECB(5, 41)]), new ECBlocks(20, [new ECB(11, 16), new ECB(5, 17)]), new ECBlocks(24, [new ECB(11, 12), new ECB(5, 13)])]), new Version(15, [6, 26, 48, 70], [new ECBlocks(22, [new ECB(5, 87), new ECB(1, 88)]), new ECBlocks(24, [new ECB(5, 41), new ECB(5, 42)]), new ECBlocks(30, [new ECB(5, 24), new ECB(7, 25)]), new ECBlocks(24, [new ECB(11, 12), new ECB(7, 13)])]), new Version(16, [6, 26, 50, 74], [new ECBlocks(24, [new ECB(5, 98), new ECB(1, 99)]), new ECBlocks(28, [new ECB(7, 45), new ECB(3, 46)]), new ECBlocks(24, [new ECB(15, 19), new ECB(2, 20)]), new ECBlocks(30, [new ECB(3, 15), new ECB(13, 16)])]), new Version(17, [6, 30, 54, 78], [new ECBlocks(28, [new ECB(1, 107), new ECB(5, 108)]), new ECBlocks(28, [new ECB(10, 46), new ECB(1, 47)]), new ECBlocks(28, [new ECB(1, 22), new ECB(15, 23)]), new ECBlocks(28, [new ECB(2, 14), new ECB(17, 15)])]), new Version(18, [6, 30, 56, 82], [new ECBlocks(30, [new ECB(5, 120), new ECB(1, 121)]), new ECBlocks(26, [new ECB(9, 43), new ECB(4, 44)]), new ECBlocks(28, [new ECB(17, 22), new ECB(1, 23)]), new ECBlocks(28, [new ECB(2, 14), new ECB(19, 15)])]), new Version(19, [6, 30, 58, 86], [new ECBlocks(28, [new ECB(3, 113), new ECB(4, 114)]), new ECBlocks(26, [new ECB(3, 44), new ECB(11, 45)]), new ECBlocks(26, [new ECB(17, 21), new ECB(4, 22)]), new ECBlocks(26, [new ECB(9, 13), new ECB(16, 14)])]), new Version(20, [6, 34, 62, 90], [new ECBlocks(28, [new ECB(3, 107), new ECB(5, 108)]), new ECBlocks(26, [new ECB(3, 41), new ECB(13, 42)]), new ECBlocks(30, [new ECB(15, 24), new ECB(5, 25)]), new ECBlocks(28, [new ECB(15, 15), new ECB(10, 16)])]), new Version(21, [6, 28, 50, 72, 94], [new ECBlocks(28, [new ECB(4, 116), new ECB(4, 117)]), new ECBlocks(26, [new ECB(17, 42)]), new ECBlocks(28, [new ECB(17, 22), new ECB(6, 23)]), new ECBlocks(30, [new ECB(19, 16), new ECB(6, 17)])]), new Version(22, [6, 26, 50, 74, 98], [new ECBlocks(28, [new ECB(2, 111), new ECB(7, 112)]), new ECBlocks(28, [new ECB(17, 46)]), new ECBlocks(30, [new ECB(7, 24), new ECB(16, 25)]), new ECBlocks(24, [new ECB(34, 13)])]), new Version(23, [6, 30, 54, 78, 102], new ECBlocks(30, [new ECB(4, 121), new ECB(5, 122)]), new ECBlocks(28, [new ECB(4, 47), new ECB(14, 48)]), new ECBlocks(30, [new ECB(11, 24), new ECB(14, 25)]), new ECBlocks(30, [new ECB(16, 15), new ECB(14, 16)])), new Version(24, [6, 28, 54, 80, 106], [new ECBlocks(30, [new ECB(6, 117), new ECB(4, 118)]), new ECBlocks(28, [new ECB(6, 45), new ECB(14, 46)]), new ECBlocks(30, [new ECB(11, 24), new ECB(16, 25)]), new ECBlocks(30, [new ECB(30, 16), new ECB(2, 17)])]), new Version(25, [6, 32, 58, 84, 110], [new ECBlocks(26, [new ECB(8, 106), new ECB(4, 107)]), new ECBlocks(28, [new ECB(8, 47), new ECB(13, 48)]), new ECBlocks(30, [new ECB(7, 24), new ECB(22, 25)]), new ECBlocks(30, [new ECB(22, 15), new ECB(13, 16)])]), new Version(26, [6, 30, 58, 86, 114], [new ECBlocks(28, [new ECB(10, 114), new ECB(2, 115)]), new ECBlocks(28, [new ECB(19, 46), new ECB(4, 47)]), new ECBlocks(28, [new ECB(28, 22), new ECB(6, 23)]), new ECBlocks(30, [new ECB(33, 16), new ECB(4, 17)])]), new Version(27, [6, 34, 62, 90, 118], [new ECBlocks(30, [new ECB(8, 122), new ECB(4, 123)]), new ECBlocks(28, [new ECB(22, 45), new ECB(3, 46)]), new ECBlocks(30, [new ECB(8, 23), new ECB(26, 24)]), new ECBlocks(30, [new ECB(12, 15), new ECB(28, 16)])]), new Version(28, [6, 26, 50, 74, 98, 122], [new ECBlocks(30, [new ECB(3, 117), new ECB(10, 118)]), new ECBlocks(28, [new ECB(3, 45), new ECB(23, 46)]), new ECBlocks(30, [new ECB(4, 24), new ECB(31, 25)]), new ECBlocks(30, [new ECB(11, 15), new ECB(31, 16)])]), new Version(29, [6, 30, 54, 78, 102, 126], [new ECBlocks(30, [new ECB(7, 116), new ECB(7, 117)]), new ECBlocks(28, [new ECB(21, 45), new ECB(7, 46)]), new ECBlocks(30, [new ECB(1, 23), new ECB(37, 24)]), new ECBlocks(30, [new ECB(19, 15), new ECB(26, 16)])]), new Version(30, [6, 26, 52, 78, 104, 130], [new ECBlocks(30, [new ECB(5, 115), new ECB(10, 116)]), new ECBlocks(28, [new ECB(19, 47), new ECB(10, 48)]), new ECBlocks(30, [new ECB(15, 24), new ECB(25, 25)]), new ECBlocks(30, [new ECB(23, 15), new ECB(25, 16)])]), new Version(31, [6, 30, 56, 82, 108, 134], [new ECBlocks(30, [new ECB(13, 115), new ECB(3, 116)]), new ECBlocks(28, [new ECB(2, 46), new ECB(29, 47)]), new ECBlocks(30, [new ECB(42, 24), new ECB(1, 25)]), new ECBlocks(30, [new ECB(23, 15), new ECB(28, 16)])]), new Version(32, [6, 34, 60, 86, 112, 138], [new ECBlocks(30, [new ECB(17, 115)]), new ECBlocks(28, [new ECB(10, 46), new ECB(23, 47)]), new ECBlocks(30, [new ECB(10, 24), new ECB(35, 25)]), new ECBlocks(30, [new ECB(19, 15), new ECB(35, 16)])]), new Version(33, [6, 30, 58, 86, 114, 142], [new ECBlocks(30, [new ECB(17, 115), new ECB(1, 116)]), new ECBlocks(28, [new ECB(14, 46), new ECB(21, 47)]), new ECBlocks(30, [new ECB(29, 24), new ECB(19, 25)]), new ECBlocks(30, [new ECB(11, 15), new ECB(46, 16)])]), new Version(34, [6, 34, 62, 90, 118, 146], [new ECBlocks(30, [new ECB(13, 115), new ECB(6, 116)]), new ECBlocks(28, [new ECB(14, 46), new ECB(23, 47)]), new ECBlocks(30, [new ECB(44, 24), new ECB(7, 25)]), new ECBlocks(30, [new ECB(59, 16), new ECB(1, 17)])]), new Version(35, [6, 30, 54, 78, 102, 126, 150], [new ECBlocks(30, [new ECB(12, 121), new ECB(7, 122)]), new ECBlocks(28, [new ECB(12, 47), new ECB(26, 48)]), new ECBlocks(30, [new ECB(39, 24), new ECB(14, 25)]), new ECBlocks(30, [new ECB(22, 15), new ECB(41, 16)])]), new Version(36, [6, 24, 50, 76, 102, 128, 154], [new ECBlocks(30, [new ECB(6, 121), new ECB(14, 122)]), new ECBlocks(28, [new ECB(6, 47), new ECB(34, 48)]), new ECBlocks(30, [new ECB(46, 24), new ECB(10, 25)]), new ECBlocks(30, [new ECB(2, 15), new ECB(64, 16)])]), new Version(37, [6, 28, 54, 80, 106, 132, 158], [new ECBlocks(30, [new ECB(17, 122), new ECB(4, 123)]), new ECBlocks(28, [new ECB(29, 46), new ECB(14, 47)]), new ECBlocks(30, [new ECB(49, 24), new ECB(10, 25)]), new ECBlocks(30, [new ECB(24, 15), new ECB(46, 16)])]), new Version(38, [6, 32, 58, 84, 110, 136, 162], [new ECBlocks(30, [new ECB(4, 122), new ECB(18, 123)]), new ECBlocks(28, [new ECB(13, 46), new ECB(32, 47)]), new ECBlocks(30, [new ECB(48, 24), new ECB(14, 25)]), new ECBlocks(30, [new ECB(42, 15), new ECB(32, 16)])]), new Version(39, [6, 26, 54, 82, 110, 138, 166], [new ECBlocks(30, [new ECB(20, 117), new ECB(4, 118)]), new ECBlocks(28, [new ECB(40, 47), new ECB(7, 48)]), new ECBlocks(30, [new ECB(43, 24), new ECB(22, 25)]), new ECBlocks(30, [new ECB(10, 15), new ECB(67, 16)])]), new Version(40, [6, 30, 58, 86, 114, 142, 170], [new ECBlocks(30, [new ECB(19, 118), new ECB(6, 119)]), new ECBlocks(28, [new ECB(18, 47), new ECB(31, 48)]), new ECBlocks(30, [new ECB(34, 24), new ECB(34, 25)]), new ECBlocks(30, [new ECB(20, 15), new ECB(61, 16)])])];
    }
}
/**
 * <p>Encapsulates a set of error-correction blocks in one symbol version. Most versions will
 * use blocks of differing sizes within one version, so, this encapsulates the parameters for
 * each set of blocks. It also holds the number of error-correction codewords per block since it
 * will be the same across all blocks within one version.</p>
 */
final class ECBlocks
{
    public function __construct(private $ecCodewordsPerBlock, private $ecBlocks)
    {
    }
    public function getECCodewordsPerBlock()
    {
        return $this->ecCodewordsPerBlock;
    }
    public function getNumBlocks()
    {
        $total = 0;
        foreach ($this->ecBlocks as $ecBlock) {
            $total += $ecBlock->getCount();
        }
        return $total;
    }
    public function getTotalECCodewords()
    {
        return $this->ecCodewordsPerBlock * $this->getNumBlocks();
    }
    public function getECBlocks()
    {
        return $this->ecBlocks;
    }
}
/**
 * <p>Encapsualtes the parameters for one error-correction block in one symbol version.
 * This includes the number of data codewords, and the number of times a block with these
 * parameters is used consecutively in the QR code version's format.</p>
 */
final class ECB
{
    public function __construct(private $count, private $dataCodewords)
    {
    }
    public function getCount()
    {
        return $this->count;
    }
    public function getDataCodewords()
    {
        return $this->dataCodewords;
    }
    //@Override
    public function toString() : never
    {
        die('Version ECB toString()');
        //  return parent::$versionNumber;
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/Qrcode/Decoder/BitMatrixParser.php000064400000022535150755130600024164 0ustar00<?php

/*
 * Copyright 2007 ZXing authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
namespace WP2FA_Vendor\Zxing\Qrcode\Decoder;

use WP2FA_Vendor\Zxing\Common\BitMatrix;
use WP2FA_Vendor\Zxing\FormatException;
/**
 * @author Sean Owen
 */
final class BitMatrixParser
{
    private $bitMatrix;
    /**
     * @var mixed|null
     */
    private $parsedVersion;
    private $parsedFormatInfo;
    private $mirror;
    /**
     * @param $bitMatrix {@link BitMatrix} to parse
     *
     * @throws FormatException if dimension is not >= 21 and 1 mod 4
     */
    public function __construct($bitMatrix)
    {
        $dimension = $bitMatrix->getHeight();
        if ($dimension < 21 || ($dimension & 0x3) != 1) {
            throw FormatException::getFormatInstance();
        }
        $this->bitMatrix = $bitMatrix;
    }
    /**
     * <p>Reads the bits in the {@link BitMatrix} representing the finder pattern in the
     * correct order in order to reconstruct the codewords bytes contained within the
     * QR Code.</p>
     *
     * @return bytes encoded within the QR Code
     * @throws FormatException if the exact number of bytes expected is not read
     */
    public function readCodewords()
    {
        $formatInfo = $this->readFormatInformation();
        $version = $this->readVersion();
        // Get the data mask for the format used in this QR Code. This will exclude
        // some bits from reading as we wind through the bit matrix.
        $dataMask = DataMask::forReference($formatInfo->getDataMask());
        $dimension = $this->bitMatrix->getHeight();
        $dataMask->unmaskBitMatrix($this->bitMatrix, $dimension);
        $functionPattern = $version->buildFunctionPattern();
        $readingUp = \true;
        if ($version->getTotalCodewords()) {
            $result = fill_array(0, $version->getTotalCodewords(), 0);
        } else {
            $result = [];
        }
        $resultOffset = 0;
        $currentByte = 0;
        $bitsRead = 0;
        // Read columns in pairs, from right to left
        for ($j = $dimension - 1; $j > 0; $j -= 2) {
            if ($j == 6) {
                // Skip whole column with vertical alignment pattern;
                // saves time and makes the other code proceed more cleanly
                $j--;
            }
            // Read alternatingly from bottom to top then top to bottom
            for ($count = 0; $count < $dimension; $count++) {
                $i = $readingUp ? $dimension - 1 - $count : $count;
                for ($col = 0; $col < 2; $col++) {
                    // Ignore bits covered by the function pattern
                    if (!$functionPattern->get($j - $col, $i)) {
                        // Read a bit
                        $bitsRead++;
                        $currentByte <<= 1;
                        if ($this->bitMatrix->get($j - $col, $i)) {
                            $currentByte |= 1;
                        }
                        // If we've made a whole byte, save it off
                        if ($bitsRead == 8) {
                            $result[$resultOffset++] = $currentByte;
                            //(byte)
                            $bitsRead = 0;
                            $currentByte = 0;
                        }
                    }
                }
            }
            $readingUp ^= \true;
            // readingUp = !readingUp; // switch directions
        }
        if ($resultOffset != $version->getTotalCodewords()) {
            throw FormatException::getFormatInstance();
        }
        return $result;
    }
    /**
     * <p>Reads format information from one of its two locations within the QR Code.</p>
     *
     * @return {@link FormatInformation} encapsulating the QR Code's format info
     * @throws FormatException if both format information locations cannot be parsed as
     * the valid encoding of format information
     */
    public function readFormatInformation()
    {
        if ($this->parsedFormatInfo != null) {
            return $this->parsedFormatInfo;
        }
        // Read top-left format info bits
        $formatInfoBits1 = 0;
        for ($i = 0; $i < 6; $i++) {
            $formatInfoBits1 = $this->copyBit($i, 8, $formatInfoBits1);
        }
        // .. and skip a bit in the timing pattern ...
        $formatInfoBits1 = $this->copyBit(7, 8, $formatInfoBits1);
        $formatInfoBits1 = $this->copyBit(8, 8, $formatInfoBits1);
        $formatInfoBits1 = $this->copyBit(8, 7, $formatInfoBits1);
        // .. and skip a bit in the timing pattern ...
        for ($j = 5; $j >= 0; $j--) {
            $formatInfoBits1 = $this->copyBit(8, $j, $formatInfoBits1);
        }
        // Read the top-right/bottom-left pattern too
        $dimension = $this->bitMatrix->getHeight();
        $formatInfoBits2 = 0;
        $jMin = $dimension - 7;
        for ($j = $dimension - 1; $j >= $jMin; $j--) {
            $formatInfoBits2 = $this->copyBit(8, $j, $formatInfoBits2);
        }
        for ($i = $dimension - 8; $i < $dimension; $i++) {
            $formatInfoBits2 = $this->copyBit($i, 8, $formatInfoBits2);
        }
        $parsedFormatInfo = FormatInformation::decodeFormatInformation($formatInfoBits1, $formatInfoBits2);
        if ($parsedFormatInfo != null) {
            return $parsedFormatInfo;
        }
        throw FormatException::getFormatInstance();
    }
    private function copyBit($i, $j, $versionBits)
    {
        $bit = $this->mirror ? $this->bitMatrix->get($j, $i) : $this->bitMatrix->get($i, $j);
        return $bit ? $versionBits << 1 | 0x1 : $versionBits << 1;
    }
    /**
     * <p>Reads version information from one of its two locations within the QR Code.</p>
     *
     * @return {@link Version} encapsulating the QR Code's version
     * @throws FormatException if both version information locations cannot be parsed as
     * the valid encoding of version information
     */
    public function readVersion()
    {
        if ($this->parsedVersion != null) {
            return $this->parsedVersion;
        }
        $dimension = $this->bitMatrix->getHeight();
        $provisionalVersion = ($dimension - 17) / 4;
        if ($provisionalVersion <= 6) {
            return Version::getVersionForNumber($provisionalVersion);
        }
        // Read top-right version info: 3 wide by 6 tall
        $versionBits = 0;
        $ijMin = $dimension - 11;
        for ($j = 5; $j >= 0; $j--) {
            for ($i = $dimension - 9; $i >= $ijMin; $i--) {
                $versionBits = $this->copyBit($i, $j, $versionBits);
            }
        }
        $theParsedVersion = Version::decodeVersionInformation($versionBits);
        if ($theParsedVersion != null && $theParsedVersion->getDimensionForVersion() == $dimension) {
            $this->parsedVersion = $theParsedVersion;
            return $theParsedVersion;
        }
        // Hmm, failed. Try bottom left: 6 wide by 3 tall
        $versionBits = 0;
        for ($i = 5; $i >= 0; $i--) {
            for ($j = $dimension - 9; $j >= $ijMin; $j--) {
                $versionBits = $this->copyBit($i, $j, $versionBits);
            }
        }
        $theParsedVersion = Version::decodeVersionInformation($versionBits);
        if ($theParsedVersion != null && $theParsedVersion->getDimensionForVersion() == $dimension) {
            $this->parsedVersion = $theParsedVersion;
            return $theParsedVersion;
        }
        throw FormatException::getFormatInstance();
    }
    /**
     * Revert the mask removal done while reading the code words. The bit matrix should revert to its original state.
     */
    public function remask() : void
    {
        if ($this->parsedFormatInfo == null) {
            return;
            // We have no format information, and have no data mask
        }
        $dataMask = DataMask::forReference($this->parsedFormatInfo->getDataMask());
        $dimension = $this->bitMatrix->getHeight();
        $dataMask->unmaskBitMatrix($this->bitMatrix, $dimension);
    }
    /**
     * Prepare the parser for a mirrored operation.
     * This flag has effect only on the {@link #readFormatInformation()} and the
     * {@link #readVersion()}. Before proceeding with {@link #readCodewords()} the
     * {@link #mirror()} method should be called.
     *
     * @param Whether $mirror to read version and format information mirrored.
     */
    public function setMirror($mirror) : void
    {
        $parsedVersion = null;
        $parsedFormatInfo = null;
        $this->mirror = $mirror;
    }
    /** Mirror the bit matrix in order to attempt a second reading. */
    public function mirror() : void
    {
        for ($x = 0; $x < $this->bitMatrix->getWidth(); $x++) {
            for ($y = $x + 1; $y < $this->bitMatrix->getHeight(); $y++) {
                if ($this->bitMatrix->get($x, $y) != $this->bitMatrix->get($y, $x)) {
                    $this->bitMatrix->flip($y, $x);
                    $this->bitMatrix->flip($x, $y);
                }
            }
        }
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/Qrcode/Decoder/DataMask.php000064400000010720150755130600022562 0ustar00<?php

/*
 * Copyright 2007 ZXing authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
namespace WP2FA_Vendor\Zxing\Qrcode\Decoder;

use WP2FA_Vendor\Zxing\Common\BitMatrix;
/**
 * <p>Encapsulates data masks for the data bits in a QR code, per ISO 18004:2006 6.8. Implementations
 * of this class can un-mask a raw BitMatrix. For simplicity, they will unmask the entire BitMatrix,
 * including areas used for finder patterns, timing patterns, etc. These areas should be unused
 * after the point they are unmasked anyway.</p>
 *
 * <p>Note that the diagram in section 6.8.1 is misleading since it indicates that i is column position
 * and j is row position. In fact, as the text says, i is row position and j is column position.</p>
 *
 * @author Sean Owen
 */
abstract class DataMask
{
    /**
     * See ISO 18004:2006 6.8.1
     */
    private static array $DATA_MASKS = [];
    public function __construct()
    {
    }
    public static function Init() : void
    {
        self::$DATA_MASKS = [new DataMask000(), new DataMask001(), new DataMask010(), new DataMask011(), new DataMask100(), new DataMask101(), new DataMask110(), new DataMask111()];
    }
    /**
     * @param a $reference value between 0 and 7 indicating one of the eight possible
     *                  data mask patterns a QR Code may use
     *
     * @return DataMask encapsulating the data mask pattern
     */
    public static function forReference($reference)
    {
        if ($reference < 0 || $reference > 7) {
            throw new \InvalidArgumentException();
        }
        return self::$DATA_MASKS[$reference];
    }
    /**
     * <p>Implementations of this method reverse the data masking process applied to a QR Code and
     * make its bits ready to read.</p>
     *
     * @param representation      $bits of QR Code bits
     * @param dimension $dimension of QR Code, represented by bits, being unmasked
     */
    public final function unmaskBitMatrix($bits, $dimension) : void
    {
        for ($i = 0; $i < $dimension; $i++) {
            for ($j = 0; $j < $dimension; $j++) {
                if ($this->isMasked($i, $j)) {
                    $bits->flip($j, $i);
                }
            }
        }
    }
    public abstract function isMasked($i, $j);
}
DataMask::Init();
/**
 * 000: mask bits for which (x + y) mod 2 == 0
 */
final class DataMask000 extends DataMask
{
    // @Override
    public function isMasked($i, $j)
    {
        return ($i + $j & 0x1) == 0;
    }
}
/**
 * 001: mask bits for which x mod 2 == 0
 */
final class DataMask001 extends DataMask
{
    //@Override
    public function isMasked($i, $j)
    {
        return ($i & 0x1) == 0;
    }
}
/**
 * 010: mask bits for which y mod 3 == 0
 */
final class DataMask010 extends DataMask
{
    //@Override
    public function isMasked($i, $j)
    {
        return $j % 3 == 0;
    }
}
/**
 * 011: mask bits for which (x + y) mod 3 == 0
 */
final class DataMask011 extends DataMask
{
    //@Override
    public function isMasked($i, $j)
    {
        return ($i + $j) % 3 == 0;
    }
}
/**
 * 100: mask bits for which (x/2 + y/3) mod 2 == 0
 */
final class DataMask100 extends DataMask
{
    //@Override
    public function isMasked($i, $j)
    {
        return (int) ((int) ($i / 2) + (int) ($j / 3) & 0x1) == 0;
    }
}
/**
 * 101: mask bits for which xy mod 2 + xy mod 3 == 0
 */
final class DataMask101 extends DataMask
{
    //@Override
    public function isMasked($i, $j)
    {
        $temp = $i * $j;
        return ($temp & 0x1) + $temp % 3 == 0;
    }
}
/**
 * 110: mask bits for which (xy mod 2 + xy mod 3) mod 2 == 0
 */
final class DataMask110 extends DataMask
{
    //@Override
    public function isMasked($i, $j)
    {
        $temp = $i * $j;
        return (($temp & 0x1) + $temp % 3 & 0x1) == 0;
    }
}
/**
 * 111: mask bits for which ((x+y)mod 2 + xy mod 3) mod 2 == 0
 */
final class DataMask111 extends DataMask
{
    //@Override
    public function isMasked($i, $j)
    {
        return (($i + $j & 0x1) + $i * $j % 3 & 0x1) == 0;
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/Qrcode/Decoder/Decoder.php000064400000016703150755130600022451 0ustar00<?php

/*
 * Copyright 2007 ZXing authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
namespace WP2FA_Vendor\Zxing\Qrcode\Decoder;

use WP2FA_Vendor\Zxing\ChecksumException;
use WP2FA_Vendor\Zxing\Common\BitMatrix;
use WP2FA_Vendor\Zxing\Common\Reedsolomon\GenericGF;
use WP2FA_Vendor\Zxing\Common\Reedsolomon\ReedSolomonDecoder;
use WP2FA_Vendor\Zxing\Common\Reedsolomon\ReedSolomonException;
use WP2FA_Vendor\Zxing\FormatException;
/**
 * <p>The main class which implements QR Code decoding -- as opposed to locating and extracting
 * the QR Code from an image.</p>
 *
 * @author Sean Owen
 */
final class Decoder
{
    private readonly \WP2FA_Vendor\Zxing\Common\Reedsolomon\ReedSolomonDecoder $rsDecoder;
    public function __construct()
    {
        $this->rsDecoder = new ReedSolomonDecoder(GenericGF::$QR_CODE_FIELD_256);
    }
    public function decode($variable, $hints = null)
    {
        if (\is_array($variable)) {
            return $this->decodeImage($variable, $hints);
        } elseif ($variable instanceof BitMatrix) {
            return $this->decodeBits($variable, $hints);
        } elseif ($variable instanceof BitMatrixParser) {
            return $this->decodeParser($variable, $hints);
        }
        die('decode error Decoder.php');
    }
    /**
     * <p>Convenience method that can decode a QR Code represented as a 2D array of booleans.
     * "true" is taken to mean a black module.</p>
     *
     * @param array $image booleans representing white/black QR Code modules
     * @param       decoding  $hints hints that should be used to influence decoding
     *
     * @return text and bytes encoded within the QR Code
     * @throws FormatException if the QR Code cannot be decoded
     * @throws ChecksumException if error correction fails
     */
    public function decodeImage($image, $hints = null)
    {
        $dimension = \count($image);
        $bits = new BitMatrix($dimension);
        for ($i = 0; $i < $dimension; $i++) {
            for ($j = 0; $j < $dimension; $j++) {
                if ($image[$i][$j]) {
                    $bits->set($j, $i);
                }
            }
        }
        return $this->decode($bits, $hints);
    }
    /**
     * <p>Decodes a QR Code represented as a {@link BitMatrix}. A 1 or "true" is taken to mean a black module.</p>
     *
     * @param BitMatrix $bits booleans representing white/black QR Code modules
     * @param           decoding $hints hints that should be used to influence decoding
     *
     * @return text and bytes encoded within the QR Code
     * @throws FormatException if the QR Code cannot be decoded
     * @throws ChecksumException if error correction fails
     */
    public function decodeBits($bits, $hints = null)
    {
        // Construct a parser and read version, error-correction level
        $parser = new BitMatrixParser($bits);
        $fe = null;
        $ce = null;
        try {
            return $this->decode($parser, $hints);
        } catch (FormatException $e) {
            $fe = $e;
        } catch (ChecksumException $e) {
            $ce = $e;
        }
        try {
            // Revert the bit matrix
            $parser->remask();
            // Will be attempting a mirrored reading of the version and format info.
            $parser->setMirror(\true);
            // Preemptively read the version.
            $parser->readVersion();
            // Preemptively read the format information.
            $parser->readFormatInformation();
            /*
             * Since we're here, this means we have successfully detected some kind
             * of version and format information when mirrored. This is a good sign,
             * that the QR code may be mirrored, and we should try once more with a
             * mirrored content.
             */
            // Prepare for a mirrored reading.
            $parser->mirror();
            $result = $this->decode($parser, $hints);
            // Success! Notify the caller that the code was mirrored.
            $result->setOther(new QRCodeDecoderMetaData(\true));
            return $result;
        } catch (FormatException $e) {
            // catch (FormatException | ChecksumException e) {
            // Throw the exception from the original reading
            if ($fe != null) {
                throw $fe;
            }
            if ($ce != null) {
                throw $ce;
            }
            throw $e;
        }
    }
    private function decodeParser($parser, $hints = null)
    {
        $version = $parser->readVersion();
        $ecLevel = $parser->readFormatInformation()->getErrorCorrectionLevel();
        // Read codewords
        $codewords = $parser->readCodewords();
        // Separate into data blocks
        $dataBlocks = DataBlock::getDataBlocks($codewords, $version, $ecLevel);
        // Count total number of data bytes
        $totalBytes = 0;
        foreach ($dataBlocks as $dataBlock) {
            $totalBytes += $dataBlock->getNumDataCodewords();
        }
        $resultBytes = fill_array(0, $totalBytes, 0);
        $resultOffset = 0;
        // Error-correct and copy data blocks together into a stream of bytes
        foreach ($dataBlocks as $dataBlock) {
            $codewordBytes = $dataBlock->getCodewords();
            $numDataCodewords = $dataBlock->getNumDataCodewords();
            $this->correctErrors($codewordBytes, $numDataCodewords);
            for ($i = 0; $i < $numDataCodewords; $i++) {
                $resultBytes[$resultOffset++] = $codewordBytes[$i];
            }
        }
        // Decode the contents of that stream of bytes
        return DecodedBitStreamParser::decode($resultBytes, $version, $ecLevel, $hints);
    }
    /**
     * <p>Given data and error-correction codewords received, possibly corrupted by errors, attempts to
     * correct the errors in-place using Reed-Solomon error correction.</p>
     *
     * @param data    $codewordBytes and error correction codewords
     * @param number $numDataCodewords of codewords that are data bytes
     *
     * @throws ChecksumException if error correction fails
     */
    private function correctErrors(&$codewordBytes, $numDataCodewords)
    {
        $numCodewords = \is_countable($codewordBytes) ? \count($codewordBytes) : 0;
        // First read into an array of ints
        $codewordsInts = fill_array(0, $numCodewords, 0);
        for ($i = 0; $i < $numCodewords; $i++) {
            $codewordsInts[$i] = $codewordBytes[$i] & 0xff;
        }
        $numECCodewords = (\is_countable($codewordBytes) ? \count($codewordBytes) : 0) - $numDataCodewords;
        try {
            $this->rsDecoder->decode($codewordsInts, $numECCodewords);
        } catch (ReedSolomonException) {
            throw ChecksumException::getChecksumInstance();
        }
        // Copy back into array of bytes -- only need to worry about the bytes that were data
        // We don't care about errors in the error-correction codewords
        for ($i = 0; $i < $numDataCodewords; $i++) {
            $codewordBytes[$i] = $codewordsInts[$i];
        }
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/Qrcode/QRCodeReader.php000064400000017610150755130600021775 0ustar00<?php

/*
 * Copyright 2007 ZXing authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
namespace WP2FA_Vendor\Zxing\Qrcode;

use WP2FA_Vendor\Zxing\BinaryBitmap;
use WP2FA_Vendor\Zxing\ChecksumException;
use WP2FA_Vendor\Zxing\Common\BitMatrix;
use WP2FA_Vendor\Zxing\FormatException;
use WP2FA_Vendor\Zxing\NotFoundException;
use WP2FA_Vendor\Zxing\Qrcode\Decoder\Decoder;
use WP2FA_Vendor\Zxing\Qrcode\Detector\Detector;
use WP2FA_Vendor\Zxing\Reader;
use WP2FA_Vendor\Zxing\Result;
/**
 * This implementation can detect and decode QR Codes in an image.
 *
 * @author Sean Owen
 */
class QRCodeReader implements Reader
{
    private static array $NO_POINTS = [];
    private readonly \WP2FA_Vendor\Zxing\Qrcode\Decoder\Decoder $decoder;
    public function __construct()
    {
        $this->decoder = new Decoder();
    }
    /**
     * @param null         $hints
     *
     * @return Result
     * @throws \Zxing\FormatException
     * @throws \Zxing\NotFoundException
     */
    public function decode(BinaryBitmap $image, $hints = null)
    {
        $decoderResult = null;
        if ($hints !== null && $hints['PURE_BARCODE']) {
            $bits = self::extractPureBits($image->getBlackMatrix());
            $decoderResult = $this->decoder->decode($bits, $hints);
            $points = self::$NO_POINTS;
        } else {
            $detector = new Detector($image->getBlackMatrix());
            $detectorResult = $detector->detect($hints);
            $decoderResult = $this->decoder->decode($detectorResult->getBits(), $hints);
            $points = $detectorResult->getPoints();
        }
        $result = new Result($decoderResult->getText(), $decoderResult->getRawBytes(), $points, 'QR_CODE');
        //BarcodeFormat.QR_CODE
        $byteSegments = $decoderResult->getByteSegments();
        if ($byteSegments !== null) {
            $result->putMetadata('BYTE_SEGMENTS', $byteSegments);
            //ResultMetadataType.BYTE_SEGMENTS
        }
        $ecLevel = $decoderResult->getECLevel();
        if ($ecLevel !== null) {
            $result->putMetadata('ERROR_CORRECTION_LEVEL', $ecLevel);
            //ResultMetadataType.ERROR_CORRECTION_LEVEL
        }
        if ($decoderResult->hasStructuredAppend()) {
            $result->putMetadata(
                'STRUCTURED_APPEND_SEQUENCE',
                //ResultMetadataType.STRUCTURED_APPEND_SEQUENCE
                $decoderResult->getStructuredAppendSequenceNumber()
            );
            $result->putMetadata(
                'STRUCTURED_APPEND_PARITY',
                //ResultMetadataType.STRUCTURED_APPEND_PARITY
                $decoderResult->getStructuredAppendParity()
            );
        }
        return $result;
    }
    /**
     * Locates and decodes a QR code in an image.
     *
     * @return a String representing the content encoded by the QR code
     * @throws NotFoundException if a QR code cannot be found
     * @throws FormatException if a QR code cannot be decoded
     * @throws ChecksumException if error correction fails
     */
    /**
     * This method detects a code in a "pure" image -- that is, pure monochrome image
     * which contains only an unrotated, unskewed, image of a code, with some white border
     * around it. This is a specialized method that works exceptionally fast in this special
     * case.
     *
     * @see com.google.zxing.datamatrix.DataMatrixReader#extractPureBits(BitMatrix)
     */
    private static function extractPureBits(BitMatrix $image)
    {
        $leftTopBlack = $image->getTopLeftOnBit();
        $rightBottomBlack = $image->getBottomRightOnBit();
        if ($leftTopBlack === null || $rightBottomBlack == null) {
            throw NotFoundException::getNotFoundInstance();
        }
        $moduleSize = self::moduleSize($leftTopBlack, $image);
        $top = $leftTopBlack[1];
        $bottom = $rightBottomBlack[1];
        $left = $leftTopBlack[0];
        $right = $rightBottomBlack[0];
        // Sanity check!
        if ($left >= $right || $top >= $bottom) {
            throw NotFoundException::getNotFoundInstance();
        }
        if ($bottom - $top != $right - $left) {
            // Special case, where bottom-right module wasn't black so we found something else in the last row
            // Assume it's a square, so use height as the width
            $right = $left + ($bottom - $top);
        }
        $matrixWidth = \round(($right - $left + 1) / $moduleSize);
        $matrixHeight = \round(($bottom - $top + 1) / $moduleSize);
        if ($matrixWidth <= 0 || $matrixHeight <= 0) {
            throw NotFoundException::getNotFoundInstance();
        }
        if ($matrixHeight != $matrixWidth) {
            // Only possibly decode square regions
            throw NotFoundException::getNotFoundInstance();
        }
        // Push in the "border" by half the module width so that we start
        // sampling in the middle of the module. Just in case the image is a
        // little off, this will help recover.
        $nudge = (int) ($moduleSize / 2.0);
        // $nudge = (int) ($moduleSize / 2.0f);
        $top += $nudge;
        $left += $nudge;
        // But careful that this does not sample off the edge
        // "right" is the farthest-right valid pixel location -- right+1 is not necessarily
        // This is positive by how much the inner x loop below would be too large
        $nudgedTooFarRight = $left + (int) (($matrixWidth - 1) * $moduleSize) - $right;
        if ($nudgedTooFarRight > 0) {
            if ($nudgedTooFarRight > $nudge) {
                // Neither way fits; abort
                throw NotFoundException::getNotFoundInstance();
            }
            $left -= $nudgedTooFarRight;
        }
        // See logic above
        $nudgedTooFarDown = $top + (int) (($matrixHeight - 1) * $moduleSize) - $bottom;
        if ($nudgedTooFarDown > 0) {
            if ($nudgedTooFarDown > $nudge) {
                // Neither way fits; abort
                throw NotFoundException::getNotFoundInstance();
            }
            $top -= $nudgedTooFarDown;
        }
        // Now just read off the bits
        $bits = new BitMatrix($matrixWidth, $matrixHeight);
        for ($y = 0; $y < $matrixHeight; $y++) {
            $iOffset = $top + (int) ($y * $moduleSize);
            for ($x = 0; $x < $matrixWidth; $x++) {
                if ($image->get($left + (int) ($x * $moduleSize), $iOffset)) {
                    $bits->set($x, $y);
                }
            }
        }
        return $bits;
    }
    private static function moduleSize($leftTopBlack, BitMatrix $image)
    {
        $height = $image->getHeight();
        $width = $image->getWidth();
        $x = $leftTopBlack[0];
        $y = $leftTopBlack[1];
        /*$x           = $leftTopBlack[0];
        		$y           = $leftTopBlack[1];*/
        $inBlack = \true;
        $transitions = 0;
        while ($x < $width && $y < $height) {
            if ($inBlack != $image->get($x, $y)) {
                if (++$transitions == 5) {
                    break;
                }
                $inBlack = !$inBlack;
            }
            $x++;
            $y++;
        }
        if ($x == $width || $y == $height) {
            throw NotFoundException::getNotFoundInstance();
        }
        return ($x - $leftTopBlack[0]) / 7.0;
        //return ($x - $leftTopBlack[0]) / 7.0f;
    }
    public function reset() : void
    {
        // do nothing
    }
    protected final function getDecoder()
    {
        return $this->decoder;
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/Qrcode/Detector/AlignmentPatternFinder.php000064400000024624150755130600025715 0ustar00<?php

/*
 * Copyright 2007 ZXing authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
namespace WP2FA_Vendor\Zxing\Qrcode\Detector;

use WP2FA_Vendor\Zxing\NotFoundException;
/**
 * <p>This class attempts to find alignment patterns in a QR Code. Alignment patterns look like finder
 * patterns but are smaller and appear at regular intervals throughout the image.</p>
 *
 * <p>At the moment this only looks for the bottom-right alignment pattern.</p>
 *
 * <p>This is mostly a simplified copy of {@link FinderPatternFinder}. It is copied,
 * pasted and stripped down here for maximum performance but does unfortunately duplicate
 * some code.</p>
 *
 * <p>This class is thread-safe but not reentrant. Each thread must allocate its own object.</p>
 *
 * @author Sean Owen
 */
final class AlignmentPatternFinder
{
    private array $possibleCenters = [];
    private array $crossCheckStateCount = [];
    /**
     * <p>Creates a finder that will look in a portion of the whole image.</p>
     *
     * @param \Imagick image      $image to search
     * @param int left     $startX column from which to start searching
     * @param int top     $startY row from which to start searching
     * @param float width      $width of region to search
     * @param float height     $height of region to search
     * @param float estimated $moduleSize module size so far
     */
    public function __construct(private $image, private $startX, private $startY, private $width, private $height, private $moduleSize, private $resultPointCallback)
    {
    }
    /**
     * <p>This method attempts to find the bottom-right alignment pattern in the image. It is a bit messy since
     * it's pretty performance-critical and so is written to be fast foremost.</p>
     *
     * @return {@link AlignmentPattern} if found
     * @throws NotFoundException if not found
     */
    public function find()
    {
        $startX = $this->startX;
        $height = $this->height;
        $maxJ = $startX + $this->width;
        $middleI = $this->startY + $height / 2;
        // We are looking for black/white/black modules in 1:1:1 ratio;
        // this tracks the number of black/white/black modules seen so far
        $stateCount = [];
        for ($iGen = 0; $iGen < $height; $iGen++) {
            // Search from middle outwards
            $i = $middleI + (($iGen & 0x1) == 0 ? ($iGen + 1) / 2 : -(($iGen + 1) / 2));
            $i = (int) $i;
            $stateCount[0] = 0;
            $stateCount[1] = 0;
            $stateCount[2] = 0;
            $j = $startX;
            // Burn off leading white pixels before anything else; if we start in the middle of
            // a white run, it doesn't make sense to count its length, since we don't know if the
            // white run continued to the left of the start point
            while ($j < $maxJ && !$this->image->get($j, $i)) {
                $j++;
            }
            $currentState = 0;
            while ($j < $maxJ) {
                if ($this->image->get($j, $i)) {
                    // Black pixel
                    if ($currentState == 1) {
                        // Counting black pixels
                        $stateCount[$currentState]++;
                    } else {
                        // Counting white pixels
                        if ($currentState == 2) {
                            // A winner?
                            if ($this->foundPatternCross($stateCount)) {
                                // Yes
                                $confirmed = $this->handlePossibleCenter($stateCount, $i, $j);
                                if ($confirmed != null) {
                                    return $confirmed;
                                }
                            }
                            $stateCount[0] = $stateCount[2];
                            $stateCount[1] = 1;
                            $stateCount[2] = 0;
                            $currentState = 1;
                        } else {
                            $stateCount[++$currentState]++;
                        }
                    }
                } else {
                    // White pixel
                    if ($currentState == 1) {
                        // Counting black pixels
                        $currentState++;
                    }
                    $stateCount[$currentState]++;
                }
                $j++;
            }
            if ($this->foundPatternCross($stateCount)) {
                $confirmed = $this->handlePossibleCenter($stateCount, $i, $maxJ);
                if ($confirmed != null) {
                    return $confirmed;
                }
            }
        }
        // Hmm, nothing we saw was observed and confirmed twice. If we had
        // any guess at all, return it.
        if (\count($this->possibleCenters)) {
            return $this->possibleCenters[0];
        }
        throw NotFoundException::getNotFoundInstance();
    }
    /**
     * @param count $stateCount of black/white/black pixels just read
     *
     * @return true iff the proportions of the counts is close enough to the 1/1/1 ratios
     *         used by alignment patterns to be considered a match
     */
    private function foundPatternCross($stateCount)
    {
        $moduleSize = $this->moduleSize;
        $maxVariance = $moduleSize / 2.0;
        for ($i = 0; $i < 3; $i++) {
            if (\abs($moduleSize - $stateCount[$i]) >= $maxVariance) {
                return \false;
            }
        }
        return \true;
    }
    /**
     * <p>This is called when a horizontal scan finds a possible alignment pattern. It will
     * cross check with a vertical scan, and if successful, will see if this pattern had been
     * found on a previous horizontal scan. If so, we consider it confirmed and conclude we have
     * found the alignment pattern.</p>
     *
     * @param reading $stateCount state module counts from horizontal scan
     * @param row          $i where alignment pattern may be found
     * @param end          $j of possible alignment pattern in row
     *
     * @return {@link AlignmentPattern} if we have found the same pattern twice, or null if not
     */
    private function handlePossibleCenter($stateCount, $i, $j)
    {
        $stateCountTotal = $stateCount[0] + $stateCount[1] + $stateCount[2];
        $centerJ = self::centerFromEnd($stateCount, $j);
        $centerI = $this->crossCheckVertical($i, (int) $centerJ, 2 * $stateCount[1], $stateCountTotal);
        if (!\is_nan($centerI)) {
            $estimatedModuleSize = (float) ($stateCount[0] + $stateCount[1] + $stateCount[2]) / 3.0;
            foreach ($this->possibleCenters as $center) {
                // Look for about the same center and module size:
                if ($center->aboutEquals($estimatedModuleSize, $centerI, $centerJ)) {
                    return $center->combineEstimate($centerI, $centerJ, $estimatedModuleSize);
                }
            }
            // Hadn't found this before; save it
            $point = new AlignmentPattern($centerJ, $centerI, $estimatedModuleSize);
            $this->possibleCenters[] = $point;
            if ($this->resultPointCallback != null) {
                $this->resultPointCallback->foundPossibleResultPoint($point);
            }
        }
        return null;
    }
    /**
     * Given a count of black/white/black pixels just seen and an end position,
     * figures the location of the center of this black/white/black run.
     */
    private static function centerFromEnd($stateCount, $end)
    {
        return (float) ($end - $stateCount[2]) - $stateCount[1] / 2.0;
    }
    /**
     * <p>After a horizontal scan finds a potential alignment pattern, this method
     * "cross-checks" by scanning down vertically through the center of the possible
     * alignment pattern to see if the same proportion is detected.</p>
     *
     * @param int row   $startI where an alignment pattern was detected
     * @param float center  $centerJ of the section that appears to cross an alignment pattern
     * @param int maximum $maxCount reasonable number of modules that should be
     *                 observed in any reading state, based on the results of the horizontal scan
     *
     * @return float vertical center of alignment pattern, or {@link Float#NaN} if not found
     */
    private function crossCheckVertical($startI, $centerJ, $maxCount, $originalStateCountTotal)
    {
        $image = $this->image;
        $maxI = $image->getHeight();
        $stateCount = $this->crossCheckStateCount;
        $stateCount[0] = 0;
        $stateCount[1] = 0;
        $stateCount[2] = 0;
        // Start counting up from center
        $i = $startI;
        while ($i >= 0 && $image->get($centerJ, $i) && $stateCount[1] <= $maxCount) {
            $stateCount[1]++;
            $i--;
        }
        // If already too many modules in this state or ran off the edge:
        if ($i < 0 || $stateCount[1] > $maxCount) {
            return \NAN;
        }
        while ($i >= 0 && !$image->get($centerJ, $i) && $stateCount[0] <= $maxCount) {
            $stateCount[0]++;
            $i--;
        }
        if ($stateCount[0] > $maxCount) {
            return \NAN;
        }
        // Now also count down from center
        $i = $startI + 1;
        while ($i < $maxI && $image->get($centerJ, $i) && $stateCount[1] <= $maxCount) {
            $stateCount[1]++;
            $i++;
        }
        if ($i == $maxI || $stateCount[1] > $maxCount) {
            return \NAN;
        }
        while ($i < $maxI && !$image->get($centerJ, $i) && $stateCount[2] <= $maxCount) {
            $stateCount[2]++;
            $i++;
        }
        if ($stateCount[2] > $maxCount) {
            return \NAN;
        }
        $stateCountTotal = $stateCount[0] + $stateCount[1] + $stateCount[2];
        if (5 * \abs($stateCountTotal - $originalStateCountTotal) >= 2 * $originalStateCountTotal) {
            return \NAN;
        }
        return $this->foundPatternCross($stateCount) ? self::centerFromEnd($stateCount, $i) : \NAN;
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/Qrcode/Detector/FinderPattern.php000064400000005217150755130600024053 0ustar00<?php

/*
 * Copyright 2007 ZXing authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
namespace WP2FA_Vendor\Zxing\Qrcode\Detector;

use WP2FA_Vendor\Zxing\ResultPoint;
/**
 * <p>Encapsulates a finder pattern, which are the three square patterns found in
 * the corners of QR Codes. It also encapsulates a count of similar finder patterns,
 * as a convenience to the finder's bookkeeping.</p>
 *
 * @author Sean Owen
 */
final class FinderPattern extends ResultPoint
{
    public function __construct($posX, $posY, private $estimatedModuleSize, private $count = 1)
    {
        parent::__construct($posX, $posY);
    }
    public function getEstimatedModuleSize()
    {
        return $this->estimatedModuleSize;
    }
    public function getCount()
    {
        return $this->count;
    }
    /*
    	void incrementCount() {
     this.count++;
    	}
    */
    /**
     * <p>Determines if this finder pattern "about equals" a finder pattern at the stated
     * position and size -- meaning, it is at nearly the same center with nearly the same size.</p>
     */
    public function aboutEquals($moduleSize, $i, $j)
    {
        if (\abs($i - $this->getY()) <= $moduleSize && \abs($j - $this->getX()) <= $moduleSize) {
            $moduleSizeDiff = \abs($moduleSize - $this->estimatedModuleSize);
            return $moduleSizeDiff <= 1.0 || $moduleSizeDiff <= $this->estimatedModuleSize;
        }
        return \false;
    }
    /**
     * Combines this object's current estimate of a finder pattern position and module size
     * with a new estimate. It returns a new {@code FinderPattern} containing a weighted average
     * based on count.
     */
    public function combineEstimate($i, $j, $newModuleSize) : \WP2FA_Vendor\Zxing\Qrcode\Detector\FinderPattern
    {
        $combinedCount = $this->count + 1;
        $combinedX = ($this->count * $this->getX() + $j) / $combinedCount;
        $combinedY = ($this->count * $this->getY() + $i) / $combinedCount;
        $combinedModuleSize = ($this->count * $this->estimatedModuleSize + $newModuleSize) / $combinedCount;
        return new FinderPattern($combinedX, $combinedY, $combinedModuleSize, $combinedCount);
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/Qrcode/Detector/FinderPatternInfo.php000064400000002547150755130600024672 0ustar00<?php

/*
 * Copyright 2007 ZXing authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
namespace WP2FA_Vendor\Zxing\Qrcode\Detector;

/**
 * <p>Encapsulates information about finder patterns in an image, including the location of
 * the three finder patterns, and their estimated module size.</p>
 *
 * @author Sean Owen
 */
final class FinderPatternInfo
{
    private $bottomLeft;
    private $topLeft;
    private $topRight;
    public function __construct($patternCenters)
    {
        $this->bottomLeft = $patternCenters[0];
        $this->topLeft = $patternCenters[1];
        $this->topRight = $patternCenters[2];
    }
    public function getBottomLeft()
    {
        return $this->bottomLeft;
    }
    public function getTopLeft()
    {
        return $this->topLeft;
    }
    public function getTopRight()
    {
        return $this->topRight;
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/Qrcode/Detector/Detector.php000064400000036612150755130600023062 0ustar00<?php

/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace WP2FA_Vendor\Zxing\Qrcode\Detector;

use WP2FA_Vendor\Zxing\Common\Detector\MathUtils;
use WP2FA_Vendor\Zxing\Common\DetectorResult;
use WP2FA_Vendor\Zxing\Common\GridSampler;
use WP2FA_Vendor\Zxing\Common\PerspectiveTransform;
use WP2FA_Vendor\Zxing\DecodeHintType;
use WP2FA_Vendor\Zxing\FormatException;
use WP2FA_Vendor\Zxing\NotFoundException;
use WP2FA_Vendor\Zxing\Qrcode\Decoder\Version;
use WP2FA_Vendor\Zxing\ResultPoint;
use WP2FA_Vendor\Zxing\ResultPointCallback;
/**
 * <p>Encapsulates logic that can detect a QR Code in an image, even if the QR Code
 * is rotated or skewed, or partially obscured.</p>
 *
 * @author Sean Owen
 */
class Detector
{
    private $resultPointCallback;
    public function __construct(private $image)
    {
    }
    /**
     * <p>Detects a QR Code in an image.</p>
     *
     * @param array|null optional $hints hints to detector
     *
     * @return {@link DetectorResult} encapsulating results of detecting a QR Code
     * @throws NotFoundException if QR Code cannot be found
     * @throws FormatException if a QR Code cannot be decoded
     */
    public final function detect($hints = null)
    {
        /*Map<DecodeHintType,?>*/
        $resultPointCallback = $hints == null ? null : $hints->get('NEED_RESULT_POINT_CALLBACK');
        /* resultPointCallback = hints == null ? null :
        			(ResultPointCallback) hints.get(DecodeHintType.NEED_RESULT_POINT_CALLBACK);*/
        $finder = new FinderPatternFinder($this->image, $resultPointCallback);
        $info = $finder->find($hints);
        return $this->processFinderPatternInfo($info);
    }
    protected final function processFinderPatternInfo($info) : \WP2FA_Vendor\Zxing\Common\DetectorResult
    {
        $topLeft = $info->getTopLeft();
        $topRight = $info->getTopRight();
        $bottomLeft = $info->getBottomLeft();
        $moduleSize = (float) $this->calculateModuleSize($topLeft, $topRight, $bottomLeft);
        if ($moduleSize < 1.0) {
            throw NotFoundException::getNotFoundInstance();
        }
        $dimension = (int) self::computeDimension($topLeft, $topRight, $bottomLeft, $moduleSize);
        $provisionalVersion = \WP2FA_Vendor\Zxing\Qrcode\Decoder\Version::getProvisionalVersionForDimension($dimension);
        $modulesBetweenFPCenters = $provisionalVersion->getDimensionForVersion() - 7;
        $alignmentPattern = null;
        // Anything above version 1 has an alignment pattern
        if ((\is_countable($provisionalVersion->getAlignmentPatternCenters()) ? \count($provisionalVersion->getAlignmentPatternCenters()) : 0) > 0) {
            // Guess where a "bottom right" finder pattern would have been
            $bottomRightX = $topRight->getX() - $topLeft->getX() + $bottomLeft->getX();
            $bottomRightY = $topRight->getY() - $topLeft->getY() + $bottomLeft->getY();
            // Estimate that alignment pattern is closer by 3 modules
            // from "bottom right" to known top left location
            $correctionToTopLeft = 1.0 - 3.0 / (float) $modulesBetweenFPCenters;
            $estAlignmentX = (int) ($topLeft->getX() + $correctionToTopLeft * ($bottomRightX - $topLeft->getX()));
            $estAlignmentY = (int) ($topLeft->getY() + $correctionToTopLeft * ($bottomRightY - $topLeft->getY()));
            // Kind of arbitrary -- expand search radius before giving up
            for ($i = 4; $i <= 16; $i <<= 1) {
                //??????????
                try {
                    $alignmentPattern = $this->findAlignmentInRegion($moduleSize, $estAlignmentX, $estAlignmentY, (float) $i);
                    break;
                } catch (NotFoundException) {
                    // try next round
                }
            }
            // If we didn't find alignment pattern... well try anyway without it
        }
        $transform = self::createTransform($topLeft, $topRight, $bottomLeft, $alignmentPattern, $dimension);
        $bits = self::sampleGrid($this->image, $transform, $dimension);
        $points = [];
        if ($alignmentPattern == null) {
            $points = [$bottomLeft, $topLeft, $topRight];
        } else {
            // die('$points = new ResultPoint[]{bottomLeft, topLeft, topRight, alignmentPattern};');
            $points = [$bottomLeft, $topLeft, $topRight, $alignmentPattern];
        }
        return new DetectorResult($bits, $points);
    }
    /**
     * <p>Detects a QR Code in an image.</p>
     *
     * @return {@link DetectorResult} encapsulating results of detecting a QR Code
     * @throws NotFoundException if QR Code cannot be found
     * @throws FormatException if a QR Code cannot be decoded
     */
    /**
     * <p>Computes an average estimated module size based on estimated derived from the positions
     * of the three finder patterns.</p>
     *
     * @param detected    $topLeft top-left finder pattern center
     * @param detected   $topRight top-right finder pattern center
     * @param detected $bottomLeft bottom-left finder pattern center
     *
     * @return estimated module size
     */
    protected final function calculateModuleSize($topLeft, $topRight, $bottomLeft)
    {
        // Take the average
        return ($this->calculateModuleSizeOneWay($topLeft, $topRight) + $this->calculateModuleSizeOneWay($topLeft, $bottomLeft)) / 2.0;
    }
    /**
     * <p>Estimates module size based on two finder patterns -- it uses
     * {@link #sizeOfBlackWhiteBlackRunBothWays(int, int, int, int)} to figure the
     * width of each, measuring along the axis between their centers.</p>
     */
    private function calculateModuleSizeOneWay($pattern, $otherPattern)
    {
        $moduleSizeEst1 = $this->sizeOfBlackWhiteBlackRunBothWays($pattern->getX(), (int) $pattern->getY(), (int) $otherPattern->getX(), (int) $otherPattern->getY());
        $moduleSizeEst2 = $this->sizeOfBlackWhiteBlackRunBothWays((int) $otherPattern->getX(), (int) $otherPattern->getY(), (int) $pattern->getX(), (int) $pattern->getY());
        if (\is_nan($moduleSizeEst1)) {
            return $moduleSizeEst2 / 7.0;
        }
        if (\is_nan($moduleSizeEst2)) {
            return $moduleSizeEst1 / 7.0;
        }
        // Average them, and divide by 7 since we've counted the width of 3 black modules,
        // and 1 white and 1 black module on either side. Ergo, divide sum by 14.
        return ($moduleSizeEst1 + $moduleSizeEst2) / 14.0;
    }
    /**
     * See {@link #sizeOfBlackWhiteBlackRun(int, int, int, int)}; computes the total width of
     * a finder pattern by looking for a black-white-black run from the center in the direction
     * of another po$(another finder pattern center), and in the opposite direction too.</p>
     */
    private function sizeOfBlackWhiteBlackRunBothWays($fromX, $fromY, $toX, $toY)
    {
        $result = $this->sizeOfBlackWhiteBlackRun($fromX, $fromY, $toX, $toY);
        // Now count other way -- don't run off image though of course
        $scale = 1.0;
        $otherToX = $fromX - ($toX - $fromX);
        if ($otherToX < 0) {
            $scale = (float) $fromX / (float) ($fromX - $otherToX);
            $otherToX = 0;
        } elseif ($otherToX >= $this->image->getWidth()) {
            $scale = (float) ($this->image->getWidth() - 1 - $fromX) / (float) ($otherToX - $fromX);
            $otherToX = $this->image->getWidth() - 1;
        }
        $otherToY = (int) ($fromY - ($toY - $fromY) * $scale);
        $scale = 1.0;
        if ($otherToY < 0) {
            $scale = (float) $fromY / (float) ($fromY - $otherToY);
            $otherToY = 0;
        } elseif ($otherToY >= $this->image->getHeight()) {
            $scale = (float) ($this->image->getHeight() - 1 - $fromY) / (float) ($otherToY - $fromY);
            $otherToY = $this->image->getHeight() - 1;
        }
        $otherToX = (int) ($fromX + ($otherToX - $fromX) * $scale);
        $result += $this->sizeOfBlackWhiteBlackRun($fromX, $fromY, $otherToX, $otherToY);
        // Middle pixel is double-counted this way; subtract 1
        return $result - 1.0;
    }
    /**
     * <p>This method traces a line from a po$in the image, in the direction towards another point.
     * It begins in a black region, and keeps going until it finds white, then black, then white again.
     * It reports the distance from the start to this point.</p>
     *
     * <p>This is used when figuring out how wide a finder pattern is, when the finder pattern
     * may be skewed or rotated.</p>
     */
    private function sizeOfBlackWhiteBlackRun($fromX, $fromY, $toX, $toY)
    {
        // Mild variant of Bresenham's algorithm;
        // see http://en.wikipedia.org/wiki/Bresenham's_line_algorithm
        $steep = \abs($toY - $fromY) > \abs($toX - $fromX);
        if ($steep) {
            $temp = $fromX;
            $fromX = $fromY;
            $fromY = $temp;
            $temp = $toX;
            $toX = $toY;
            $toY = $temp;
        }
        $dx = \abs($toX - $fromX);
        $dy = \abs($toY - $fromY);
        $error = -$dx / 2;
        $xstep = $fromX < $toX ? 1 : -1;
        $ystep = $fromY < $toY ? 1 : -1;
        // In black pixels, looking for white, first or second time.
        $state = 0;
        // Loop up until x == toX, but not beyond
        $xLimit = $toX + $xstep;
        for ($x = $fromX, $y = $fromY; $x != $xLimit; $x += $xstep) {
            $realX = $steep ? $y : $x;
            $realY = $steep ? $x : $y;
            // Does current pixel mean we have moved white to black or vice versa?
            // Scanning black in state 0,2 and white in state 1, so if we find the wrong
            // color, advance to next state or end if we are in state 2 already
            if (($state == 1) == $this->image->get($realX, $realY)) {
                if ($state == 2) {
                    return MathUtils::distance($x, $y, $fromX, $fromY);
                }
                $state++;
            }
            $error += $dy;
            if ($error > 0) {
                if ($y == $toY) {
                    break;
                }
                $y += $ystep;
                $error -= $dx;
            }
        }
        // Found black-white-black; give the benefit of the doubt that the next pixel outside the image
        // is "white" so this last po$at (toX+xStep,toY) is the right ending. This is really a
        // small approximation; (toX+xStep,toY+yStep) might be really correct. Ignore this.
        if ($state == 2) {
            return MathUtils::distance($toX + $xstep, $toY, $fromX, $fromY);
        }
        // else we didn't find even black-white-black; no estimate is really possible
        return \NAN;
    }
    /**
     * <p>Computes the dimension (number of modules on a size) of the QR Code based on the position
     * of the finder patterns and estimated module size.</p>
     */
    private static function computeDimension($topLeft, $topRight, $bottomLeft, $moduleSize)
    {
        $tltrCentersDimension = MathUtils::round(ResultPoint::distance($topLeft, $topRight) / $moduleSize);
        $tlblCentersDimension = MathUtils::round(ResultPoint::distance($topLeft, $bottomLeft) / $moduleSize);
        $dimension = ($tltrCentersDimension + $tlblCentersDimension) / 2 + 7;
        switch ($dimension & 0x3) {
            // mod 4
            case 0:
                $dimension++;
                break;
            // 1? do nothing
            case 2:
                $dimension--;
                break;
            case 3:
                throw NotFoundException::getNotFoundInstance();
        }
        return $dimension;
    }
    /**
     * <p>Attempts to locate an alignment pattern in a limited region of the image, which is
     * guessed to contain it. This method uses {@link AlignmentPattern}.</p>
     *
     * @param estimated $overallEstModuleSize module size so far
     * @param x        $estAlignmentX coordinate of center of area probably containing alignment pattern
     * @param y        $estAlignmentY coordinate of above
     * @param number      $allowanceFactor of pixels in all directions to search from the center
     *
     * @return {@link AlignmentPattern} if found, or null otherwise
     * @throws NotFoundException if an unexpected error occurs during detection
     */
    protected final function findAlignmentInRegion($overallEstModuleSize, $estAlignmentX, $estAlignmentY, $allowanceFactor)
    {
        // Look for an alignment pattern (3 modules in size) around where it
        // should be
        $allowance = (int) ($allowanceFactor * $overallEstModuleSize);
        $alignmentAreaLeftX = \max(0, $estAlignmentX - $allowance);
        $alignmentAreaRightX = \min($this->image->getWidth() - 1, $estAlignmentX + $allowance);
        if ($alignmentAreaRightX - $alignmentAreaLeftX < $overallEstModuleSize * 3) {
            throw NotFoundException::getNotFoundInstance();
        }
        $alignmentAreaTopY = \max(0, $estAlignmentY - $allowance);
        $alignmentAreaBottomY = \min($this->image->getHeight() - 1, $estAlignmentY + $allowance);
        if ($alignmentAreaBottomY - $alignmentAreaTopY < $overallEstModuleSize * 3) {
            throw NotFoundException::getNotFoundInstance();
        }
        $alignmentFinder = new AlignmentPatternFinder($this->image, $alignmentAreaLeftX, $alignmentAreaTopY, $alignmentAreaRightX - $alignmentAreaLeftX, $alignmentAreaBottomY - $alignmentAreaTopY, $overallEstModuleSize, $this->resultPointCallback);
        return $alignmentFinder->find();
    }
    private static function createTransform($topLeft, $topRight, $bottomLeft, $alignmentPattern, $dimension)
    {
        $dimMinusThree = (float) $dimension - 3.5;
        $bottomRightX = 0.0;
        $bottomRightY = 0.0;
        $sourceBottomRightX = 0.0;
        $sourceBottomRightY = 0.0;
        if ($alignmentPattern != null) {
            $bottomRightX = $alignmentPattern->getX();
            $bottomRightY = $alignmentPattern->getY();
            $sourceBottomRightX = $dimMinusThree - 3.0;
            $sourceBottomRightY = $sourceBottomRightX;
        } else {
            // Don't have an alignment pattern, just make up the bottom-right point
            $bottomRightX = $topRight->getX() - $topLeft->getX() + $bottomLeft->getX();
            $bottomRightY = $topRight->getY() - $topLeft->getY() + $bottomLeft->getY();
            $sourceBottomRightX = $dimMinusThree;
            $sourceBottomRightY = $dimMinusThree;
        }
        return PerspectiveTransform::quadrilateralToQuadrilateral(3.5, 3.5, $dimMinusThree, 3.5, $sourceBottomRightX, $sourceBottomRightY, 3.5, $dimMinusThree, $topLeft->getX(), $topLeft->getY(), $topRight->getX(), $topRight->getY(), $bottomRightX, $bottomRightY, $bottomLeft->getX(), $bottomLeft->getY());
    }
    private static function sampleGrid($image, $transform, $dimension)
    {
        $sampler = GridSampler::getInstance();
        return $sampler->sampleGrid_($image, $dimension, $dimension, $transform);
    }
    protected final function getImage()
    {
        return $this->image;
    }
    protected final function getResultPointCallback()
    {
        return $this->resultPointCallback;
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/Qrcode/Detector/AlignmentPattern.php000064400000004240150755130600024555 0ustar00<?php

/*
 * Copyright 2007 ZXing authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
namespace WP2FA_Vendor\Zxing\Qrcode\Detector;

use WP2FA_Vendor\Zxing\ResultPoint;
/**
 * <p>Encapsulates an alignment pattern, which are the smaller square patterns found in
 * all but the simplest QR Codes.</p>
 *
 * @author Sean Owen
 */
final class AlignmentPattern extends ResultPoint
{
    public function __construct($posX, $posY, private $estimatedModuleSize)
    {
        parent::__construct($posX, $posY);
    }
    /**
     * <p>Determines if this alignment pattern "about equals" an alignment pattern at the stated
     * position and size -- meaning, it is at nearly the same center with nearly the same size.</p>
     */
    public function aboutEquals($moduleSize, $i, $j)
    {
        if (\abs($i - $this->getY()) <= $moduleSize && \abs($j - $this->getX()) <= $moduleSize) {
            $moduleSizeDiff = \abs($moduleSize - $this->estimatedModuleSize);
            return $moduleSizeDiff <= 1.0 || $moduleSizeDiff <= $this->estimatedModuleSize;
        }
        return \false;
    }
    /**
     * Combines this object's current estimate of a finder pattern position and module size
     * with a new estimate. It returns a new {@code FinderPattern} containing an average of the two.
     */
    public function combineEstimate($i, $j, $newModuleSize) : \WP2FA_Vendor\Zxing\Qrcode\Detector\AlignmentPattern
    {
        $combinedX = ($this->getX() + $j) / 2.0;
        $combinedY = ($this->getY() + $i) / 2.0;
        $combinedModuleSize = ($this->estimatedModuleSize + $newModuleSize) / 2.0;
        return new AlignmentPattern($combinedX, $combinedY, $combinedModuleSize);
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/Qrcode/Detector/FinderPatternFinder.php000064400000066643150755130600025215 0ustar00<?php

/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace WP2FA_Vendor\Zxing\Qrcode\Detector;

use WP2FA_Vendor\Zxing\Common\BitMatrix;
use WP2FA_Vendor\Zxing\NotFoundException;
use WP2FA_Vendor\Zxing\ResultPoint;
/**
 * <p>This class attempts to find finder patterns in a QR Code. Finder patterns are the square
 * markers at three corners of a QR Code.</p>
 *
 * <p>This class is thread-safe but not reentrant. Each thread must allocate its own object.
 *
 * @author Sean Owen
 */
class FinderPatternFinder
{
    protected static int $MIN_SKIP = 3;
    protected static int $MAX_MODULES = 57;
    // 1 pixel/module times 3 modules/center
    private static int $CENTER_QUORUM = 2;
    private ?float $average = null;
    private array $possibleCenters = [];
    //private final List<FinderPattern> possibleCenters;
    private bool $hasSkipped = \false;
    /**
     * @var mixed|int[]
     */
    private $crossCheckStateCount;
    /**
     * <p>Creates a finder that will search the image for three finder patterns.</p>
     *
     * @param BitMatrix $image image to search
     */
    public function __construct(private $image, private $resultPointCallback = null)
    {
        //new ArrayList<>();
        $this->crossCheckStateCount = fill_array(0, 5, 0);
    }
    public final function find($hints) : \WP2FA_Vendor\Zxing\Qrcode\Detector\FinderPatternInfo
    {
        /*final FinderPatternInfo find(Map<DecodeHintType,?> hints) throws NotFoundException {*/
        $tryHarder = $hints != null && $hints['TRY_HARDER'];
        $pureBarcode = $hints != null && $hints['PURE_BARCODE'];
        $maxI = $this->image->getHeight();
        $maxJ = $this->image->getWidth();
        // We are looking for black/white/black/white/black modules in
        // 1:1:3:1:1 ratio; this tracks the number of such modules seen so far
        // Let's assume that the maximum version QR Code we support takes up 1/4 the height of the
        // image, and then account for the center being 3 modules in size. This gives the smallest
        // number of pixels the center could be, so skip this often. When trying harder, look for all
        // QR versions regardless of how dense they are.
        $iSkip = (int) (3 * $maxI / (4 * self::$MAX_MODULES));
        if ($iSkip < self::$MIN_SKIP || $tryHarder) {
            $iSkip = self::$MIN_SKIP;
        }
        $done = \false;
        $stateCount = [];
        for ($i = $iSkip - 1; $i < $maxI && !$done; $i += $iSkip) {
            // Get a row of black/white values
            $stateCount[0] = 0;
            $stateCount[1] = 0;
            $stateCount[2] = 0;
            $stateCount[3] = 0;
            $stateCount[4] = 0;
            $currentState = 0;
            for ($j = 0; $j < $maxJ; $j++) {
                if ($this->image->get($j, $i)) {
                    // Black pixel
                    if (($currentState & 1) == 1) {
                        // Counting white pixels
                        $currentState++;
                    }
                    $stateCount[$currentState]++;
                } else {
                    // White pixel
                    if (($currentState & 1) == 0) {
                        // Counting black pixels
                        if ($currentState == 4) {
                            // A winner?
                            if (self::foundPatternCross($stateCount)) {
                                // Yes
                                $confirmed = $this->handlePossibleCenter($stateCount, $i, $j, $pureBarcode);
                                if ($confirmed) {
                                    // Start examining every other line. Checking each line turned out to be too
                                    // expensive and didn't improve performance.
                                    $iSkip = 3;
                                    if ($this->hasSkipped) {
                                        $done = $this->haveMultiplyConfirmedCenters();
                                    } else {
                                        $rowSkip = $this->findRowSkip();
                                        if ($rowSkip > $stateCount[2]) {
                                            // Skip rows between row of lower confirmed center
                                            // and top of presumed third confirmed center
                                            // but back up a bit to get a full chance of detecting
                                            // it, entire width of center of finder pattern
                                            // Skip by rowSkip, but back off by $stateCount[2] (size of last center
                                            // of pattern we saw) to be conservative, and also back off by iSkip which
                                            // is about to be re-added
                                            $i += $rowSkip - $stateCount[2] - $iSkip;
                                            $j = $maxJ - 1;
                                        }
                                    }
                                } else {
                                    $stateCount[0] = $stateCount[2];
                                    $stateCount[1] = $stateCount[3];
                                    $stateCount[2] = $stateCount[4];
                                    $stateCount[3] = 1;
                                    $stateCount[4] = 0;
                                    $currentState = 3;
                                    continue;
                                }
                                // Clear state to start looking again
                                $currentState = 0;
                                $stateCount[0] = 0;
                                $stateCount[1] = 0;
                                $stateCount[2] = 0;
                                $stateCount[3] = 0;
                                $stateCount[4] = 0;
                            } else {
                                // No, shift counts back by two
                                $stateCount[0] = $stateCount[2];
                                $stateCount[1] = $stateCount[3];
                                $stateCount[2] = $stateCount[4];
                                $stateCount[3] = 1;
                                $stateCount[4] = 0;
                                $currentState = 3;
                            }
                        } else {
                            $stateCount[++$currentState]++;
                        }
                    } else {
                        // Counting white pixels
                        $stateCount[$currentState]++;
                    }
                }
            }
            if (self::foundPatternCross($stateCount)) {
                $confirmed = $this->handlePossibleCenter($stateCount, $i, $maxJ, $pureBarcode);
                if ($confirmed) {
                    $iSkip = $stateCount[0];
                    if ($this->hasSkipped) {
                        // Found a third one
                        $done = $this->haveMultiplyConfirmedCenters();
                    }
                }
            }
        }
        $patternInfo = $this->selectBestPatterns();
        $patternInfo = ResultPoint::orderBestPatterns($patternInfo);
        return new FinderPatternInfo($patternInfo);
    }
    /**
     * @param $stateCount ; count of black/white/black/white/black pixels just read
     *
     * @return true iff the proportions of the counts is close enough to the 1/1/3/1/1 ratios
     *         used by finder patterns to be considered a match
     */
    protected static function foundPatternCross($stateCount)
    {
        $totalModuleSize = 0;
        for ($i = 0; $i < 5; $i++) {
            $count = $stateCount[$i];
            if ($count == 0) {
                return \false;
            }
            $totalModuleSize += $count;
        }
        if ($totalModuleSize < 7) {
            return \false;
        }
        $moduleSize = $totalModuleSize / 7.0;
        $maxVariance = $moduleSize / 2.0;
        // Allow less than 50% variance from 1-1-3-1-1 proportions
        return \abs($moduleSize - $stateCount[0]) < $maxVariance && \abs($moduleSize - $stateCount[1]) < $maxVariance && \abs(3.0 * $moduleSize - $stateCount[2]) < 3 * $maxVariance && \abs($moduleSize - $stateCount[3]) < $maxVariance && \abs($moduleSize - $stateCount[4]) < $maxVariance;
    }
    /**
     * <p>This is called when a horizontal scan finds a possible alignment pattern. It will
     * cross check with a vertical scan, and if successful, will, ah, cross-cross-check
     * with another horizontal scan. This is needed primarily to locate the real horizontal
     * center of the pattern in cases of extreme skew.
     * And then we cross-cross-cross check with another diagonal scan.</p>
     *
     * <p>If that succeeds the finder pattern location is added to a list that tracks
     * the number of times each location has been nearly-matched as a finder pattern.
     * Each additional find is more evidence that the location is in fact a finder
     * pattern center
     *
     * @param reading $stateCount state module counts from horizontal scan
     * @param row           $i where finder pattern may be found
     * @param end           $j of possible finder pattern in row
     * @param true $pureBarcode if in "pure barcode" mode
     *
     * @return true if a finder pattern candidate was found this time
     */
    protected final function handlePossibleCenter($stateCount, $i, $j, $pureBarcode)
    {
        $stateCountTotal = $stateCount[0] + $stateCount[1] + $stateCount[2] + $stateCount[3] + $stateCount[4];
        $centerJ = self::centerFromEnd($stateCount, $j);
        $centerI = $this->crossCheckVertical($i, (int) $centerJ, $stateCount[2], $stateCountTotal);
        if (!\is_nan($centerI)) {
            // Re-cross check
            $centerJ = $this->crossCheckHorizontal((int) $centerJ, (int) $centerI, $stateCount[2], $stateCountTotal);
            if (!\is_nan($centerJ) && (!$pureBarcode || $this->crossCheckDiagonal((int) $centerI, (int) $centerJ, $stateCount[2], $stateCountTotal))) {
                $estimatedModuleSize = (float) $stateCountTotal / 7.0;
                $found = \false;
                for ($index = 0; $index < \count($this->possibleCenters); $index++) {
                    $center = $this->possibleCenters[$index];
                    // Look for about the same center and module size:
                    if ($center->aboutEquals($estimatedModuleSize, $centerI, $centerJ)) {
                        $this->possibleCenters[$index] = $center->combineEstimate($centerI, $centerJ, $estimatedModuleSize);
                        $found = \true;
                        break;
                    }
                }
                if (!$found) {
                    $point = new FinderPattern($centerJ, $centerI, $estimatedModuleSize);
                    $this->possibleCenters[] = $point;
                    if ($this->resultPointCallback != null) {
                        $this->resultPointCallback->foundPossibleResultPoint($point);
                    }
                }
                return \true;
            }
        }
        return \false;
    }
    /**
     * Given a count of black/white/black/white/black pixels just seen and an end position,
     * figures the location of the center of this run.
     */
    private static function centerFromEnd($stateCount, $end)
    {
        return (float) ($end - $stateCount[4] - $stateCount[3]) - $stateCount[2] / 2.0;
    }
    /**
     * <p>After a horizontal scan finds a potential finder pattern, this method
     * "cross-checks" by scanning down vertically through the center of the possible
     * finder pattern to see if the same proportion is detected.</p>
     *
     * @param $startI   ;  row where a finder pattern was detected
     * @param $centerJ   ; center of the section that appears to cross a finder pattern
     * @param $maxCount ; maximum reasonable number of modules that should be
     *                  observed in any reading state, based on the results of the horizontal scan
     *
     * @return float vertical center of finder pattern, or {@link Float#NaN} if not found
     */
    private function crossCheckVertical($startI, $centerJ, $maxCount, $originalStateCountTotal)
    {
        $image = $this->image;
        $maxI = $image->getHeight();
        $stateCount = $this->getCrossCheckStateCount();
        // Start counting up from center
        $i = $startI;
        while ($i >= 0 && $image->get($centerJ, $i)) {
            $stateCount[2]++;
            $i--;
        }
        if ($i < 0) {
            return \NAN;
        }
        while ($i >= 0 && !$image->get($centerJ, $i) && $stateCount[1] <= $maxCount) {
            $stateCount[1]++;
            $i--;
        }
        // If already too many modules in this state or ran off the edge:
        if ($i < 0 || $stateCount[1] > $maxCount) {
            return \NAN;
        }
        while ($i >= 0 && $image->get($centerJ, $i) && $stateCount[0] <= $maxCount) {
            $stateCount[0]++;
            $i--;
        }
        if ($stateCount[0] > $maxCount) {
            return \NAN;
        }
        // Now also count down from center
        $i = $startI + 1;
        while ($i < $maxI && $image->get($centerJ, $i)) {
            $stateCount[2]++;
            $i++;
        }
        if ($i == $maxI) {
            return \NAN;
        }
        while ($i < $maxI && !$image->get($centerJ, $i) && $stateCount[3] < $maxCount) {
            $stateCount[3]++;
            $i++;
        }
        if ($i == $maxI || $stateCount[3] >= $maxCount) {
            return \NAN;
        }
        while ($i < $maxI && $image->get($centerJ, $i) && $stateCount[4] < $maxCount) {
            $stateCount[4]++;
            $i++;
        }
        if ($stateCount[4] >= $maxCount) {
            return \NAN;
        }
        // If we found a finder-pattern-like section, but its size is more than 40% different than
        // the original, assume it's a false positive
        $stateCountTotal = $stateCount[0] + $stateCount[1] + $stateCount[2] + $stateCount[3] + $stateCount[4];
        if (5 * \abs($stateCountTotal - $originalStateCountTotal) >= 2 * $originalStateCountTotal) {
            return \NAN;
        }
        return self::foundPatternCross($stateCount) ? self::centerFromEnd($stateCount, $i) : \NAN;
    }
    private function getCrossCheckStateCount()
    {
        $this->crossCheckStateCount[0] = 0;
        $this->crossCheckStateCount[1] = 0;
        $this->crossCheckStateCount[2] = 0;
        $this->crossCheckStateCount[3] = 0;
        $this->crossCheckStateCount[4] = 0;
        return $this->crossCheckStateCount;
    }
    /**
     * <p>Like {@link #crossCheckVertical(int, int, int, int)}, and in fact is basically identical,
     * except it reads horizontally instead of vertically. This is used to cross-cross
     * check a vertical cross check and locate the real center of the alignment pattern.</p>
     */
    private function crossCheckHorizontal($startJ, $centerI, $maxCount, $originalStateCountTotal)
    {
        $image = $this->image;
        $maxJ = $this->image->getWidth();
        $stateCount = $this->getCrossCheckStateCount();
        $j = $startJ;
        while ($j >= 0 && $image->get($j, $centerI)) {
            $stateCount[2]++;
            $j--;
        }
        if ($j < 0) {
            return \NAN;
        }
        while ($j >= 0 && !$image->get($j, $centerI) && $stateCount[1] <= $maxCount) {
            $stateCount[1]++;
            $j--;
        }
        if ($j < 0 || $stateCount[1] > $maxCount) {
            return \NAN;
        }
        while ($j >= 0 && $image->get($j, $centerI) && $stateCount[0] <= $maxCount) {
            $stateCount[0]++;
            $j--;
        }
        if ($stateCount[0] > $maxCount) {
            return \NAN;
        }
        $j = $startJ + 1;
        while ($j < $maxJ && $image->get($j, $centerI)) {
            $stateCount[2]++;
            $j++;
        }
        if ($j == $maxJ) {
            return \NAN;
        }
        while ($j < $maxJ && !$image->get($j, $centerI) && $stateCount[3] < $maxCount) {
            $stateCount[3]++;
            $j++;
        }
        if ($j == $maxJ || $stateCount[3] >= $maxCount) {
            return \NAN;
        }
        while ($j < $maxJ && $this->image->get($j, $centerI) && $stateCount[4] < $maxCount) {
            $stateCount[4]++;
            $j++;
        }
        if ($stateCount[4] >= $maxCount) {
            return \NAN;
        }
        // If we found a finder-pattern-like section, but its size is significantly different than
        // the original, assume it's a false positive
        $stateCountTotal = $stateCount[0] + $stateCount[1] + $stateCount[2] + $stateCount[3] + $stateCount[4];
        if (5 * \abs($stateCountTotal - $originalStateCountTotal) >= $originalStateCountTotal) {
            return \NAN;
        }
        return static::foundPatternCross($stateCount) ? self::centerFromEnd($stateCount, $j) : \NAN;
    }
    /**
     * After a vertical and horizontal scan finds a potential finder pattern, this method
     * "cross-cross-cross-checks" by scanning down diagonally through the center of the possible
     * finder pattern to see if the same proportion is detected.
     *
     * @param $startI                 ;  row where a finder pattern was detected
     * @param $centerJ                 ; center of the section that appears to cross a finder pattern
     * @param $maxCount               ; maximum reasonable number of modules that should be
     *                                observed in any reading state, based on the results of the horizontal scan
     * @param $originalStateCountTotal ; The original state count total.
     *
     * @return true if proportions are withing expected limits
     */
    private function crossCheckDiagonal($startI, $centerJ, $maxCount, $originalStateCountTotal)
    {
        $stateCount = $this->getCrossCheckStateCount();
        // Start counting up, left from center finding black center mass
        $i = 0;
        $startI = (int) $startI;
        $centerJ = (int) $centerJ;
        while ($startI >= $i && $centerJ >= $i && $this->image->get($centerJ - $i, $startI - $i)) {
            $stateCount[2]++;
            $i++;
        }
        if ($startI < $i || $centerJ < $i) {
            return \false;
        }
        // Continue up, left finding white space
        while ($startI >= $i && $centerJ >= $i && !$this->image->get($centerJ - $i, $startI - $i) && $stateCount[1] <= $maxCount) {
            $stateCount[1]++;
            $i++;
        }
        // If already too many modules in this state or ran off the edge:
        if ($startI < $i || $centerJ < $i || $stateCount[1] > $maxCount) {
            return \false;
        }
        // Continue up, left finding black border
        while ($startI >= $i && $centerJ >= $i && $this->image->get($centerJ - $i, $startI - $i) && $stateCount[0] <= $maxCount) {
            $stateCount[0]++;
            $i++;
        }
        if ($stateCount[0] > $maxCount) {
            return \false;
        }
        $maxI = $this->image->getHeight();
        $maxJ = $this->image->getWidth();
        // Now also count down, right from center
        $i = 1;
        while ($startI + $i < $maxI && $centerJ + $i < $maxJ && $this->image->get($centerJ + $i, $startI + $i)) {
            $stateCount[2]++;
            $i++;
        }
        // Ran off the edge?
        if ($startI + $i >= $maxI || $centerJ + $i >= $maxJ) {
            return \false;
        }
        while ($startI + $i < $maxI && $centerJ + $i < $maxJ && !$this->image->get($centerJ + $i, $startI + $i) && $stateCount[3] < $maxCount) {
            $stateCount[3]++;
            $i++;
        }
        if ($startI + $i >= $maxI || $centerJ + $i >= $maxJ || $stateCount[3] >= $maxCount) {
            return \false;
        }
        while ($startI + $i < $maxI && $centerJ + $i < $maxJ && $this->image->get($centerJ + $i, $startI + $i) && $stateCount[4] < $maxCount) {
            $stateCount[4]++;
            $i++;
        }
        if ($stateCount[4] >= $maxCount) {
            return \false;
        }
        // If we found a finder-pattern-like section, but its size is more than 100% different than
        // the original, assume it's a false positive
        $stateCountTotal = $stateCount[0] + $stateCount[1] + $stateCount[2] + $stateCount[3] + $stateCount[4];
        return \abs($stateCountTotal - $originalStateCountTotal) < 2 * $originalStateCountTotal && self::foundPatternCross($stateCount);
    }
    /**
     * @return true iff we have found at least 3 finder patterns that have been detected
     *         at least {@link #CENTER_QUORUM} times each, and, the estimated module size of the
     *         candidates is "pretty similar"
     */
    private function haveMultiplyConfirmedCenters()
    {
        $confirmedCount = 0;
        $totalModuleSize = 0.0;
        $max = \count($this->possibleCenters);
        foreach ($this->possibleCenters as $pattern) {
            if ($pattern->getCount() >= self::$CENTER_QUORUM) {
                $confirmedCount++;
                $totalModuleSize += $pattern->getEstimatedModuleSize();
            }
        }
        if ($confirmedCount < 3) {
            return \false;
        }
        // OK, we have at least 3 confirmed centers, but, it's possible that one is a "false positive"
        // and that we need to keep looking. We detect this by asking if the estimated module sizes
        // vary too much. We arbitrarily say that when the total deviation from average exceeds
        // 5% of the total module size estimates, it's too much.
        $average = $totalModuleSize / (float) $max;
        $totalDeviation = 0.0;
        foreach ($this->possibleCenters as $pattern) {
            $totalDeviation += \abs($pattern->getEstimatedModuleSize() - $average);
        }
        return $totalDeviation <= 0.05 * $totalModuleSize;
    }
    /**
     * @return int number of rows we could safely skip during scanning, based on the first
     *         two finder patterns that have been located. In some cases their position will
     *         allow us to infer that the third pattern must lie below a certain point farther
     *         down in the image.
     */
    private function findRowSkip()
    {
        $max = \count($this->possibleCenters);
        if ($max <= 1) {
            return 0;
        }
        $firstConfirmedCenter = null;
        foreach ($this->possibleCenters as $center) {
            if ($center->getCount() >= self::$CENTER_QUORUM) {
                if ($firstConfirmedCenter == null) {
                    $firstConfirmedCenter = $center;
                } else {
                    // We have two confirmed centers
                    // How far down can we skip before resuming looking for the next
                    // pattern? In the worst case, only the difference between the
                    // difference in the x / y coordinates of the two centers.
                    // This is the case where you find top left last.
                    $this->hasSkipped = \true;
                    return (int) ((\abs($firstConfirmedCenter->getX() - $center->getX()) - \abs($firstConfirmedCenter->getY() - $center->getY())) / 2);
                }
            }
        }
        return 0;
    }
    /**
     * @return array the 3 best {@link FinderPattern}s from our list of candidates. The "best" are
     *         those that have been detected at least {@link #CENTER_QUORUM} times, and whose module
     *         size differs from the average among those patterns the least
     * @throws NotFoundException if 3 such finder patterns do not exist
     */
    private function selectBestPatterns()
    {
        $startSize = \count($this->possibleCenters);
        if ($startSize < 3) {
            // Couldn't find enough finder patterns
            throw new NotFoundException();
        }
        // Filter outlier possibilities whose module size is too different
        if ($startSize > 3) {
            // But we can only afford to do so if we have at least 4 possibilities to choose from
            $totalModuleSize = 0.0;
            $square = 0.0;
            foreach ($this->possibleCenters as $center) {
                $size = $center->getEstimatedModuleSize();
                $totalModuleSize += $size;
                $square += $size * $size;
            }
            $this->average = $totalModuleSize / (float) $startSize;
            $stdDev = (float) \sqrt($square / $startSize - $this->average * $this->average);
            \usort($this->possibleCenters, $this->FurthestFromAverageComparator(...));
            $limit = \max(0.2 * $this->average, $stdDev);
            for ($i = 0; $i < \count($this->possibleCenters) && \count($this->possibleCenters) > 3; $i++) {
                $pattern = $this->possibleCenters[$i];
                if (\abs($pattern->getEstimatedModuleSize() - $this->average) > $limit) {
                    unset($this->possibleCenters[$i]);
                    //возможно что ключи меняются в java при вызове .remove(i) ???
                    $this->possibleCenters = \array_values($this->possibleCenters);
                    $i--;
                }
            }
        }
        if (\count($this->possibleCenters) > 3) {
            // Throw away all but those first size candidate points we found.
            $totalModuleSize = 0.0;
            foreach ($this->possibleCenters as $possibleCenter) {
                $totalModuleSize += $possibleCenter->getEstimatedModuleSize();
            }
            $this->average = $totalModuleSize / (float) \count($this->possibleCenters);
            \usort($this->possibleCenters, $this->CenterComparator(...));
            \array_slice($this->possibleCenters, 3, \count($this->possibleCenters) - 3);
        }
        return [$this->possibleCenters[0], $this->possibleCenters[1], $this->possibleCenters[2]];
    }
    /**
     * <p>Orders by furthest from average</p>
     */
    public function FurthestFromAverageComparator($center1, $center2)
    {
        $dA = \abs($center2->getEstimatedModuleSize() - $this->average);
        $dB = \abs($center1->getEstimatedModuleSize() - $this->average);
        if ($dA < $dB) {
            return -1;
        } elseif ($dA == $dB) {
            return 0;
        } else {
            return 1;
        }
    }
    public function CenterComparator($center1, $center2)
    {
        if ($center2->getCount() == $center1->getCount()) {
            $dA = \abs($center2->getEstimatedModuleSize() - $this->average);
            $dB = \abs($center1->getEstimatedModuleSize() - $this->average);
            if ($dA < $dB) {
                return 1;
            } elseif ($dA == $dB) {
                return 0;
            } else {
                return -1;
            }
        } else {
            return $center2->getCount() - $center1->getCount();
        }
    }
    protected final function getImage()
    {
        return $this->image;
    }
    /**
     * <p>Orders by {@link FinderPattern#getCount()}, descending.</p>
     */
    //@Override
    protected final function getPossibleCenters()
    {
        //List<FinderPattern> getPossibleCenters()
        return $this->possibleCenters;
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/Common/AbstractEnum.php000064400000003710150755130600022134 0ustar00<?php

namespace WP2FA_Vendor\Zxing\Common;

use ReflectionClass;
/**
 * A general enum implementation until we got SplEnum.
 */
final class AbstractEnum implements \Stringable
{
    /**
     * Default value.
     */
    public const __default = null;
    /**
     * Current value.
     *
     * @var mixed
     */
    private $value;
    /**
     * Cache of constants.
     *
     * @var array<string, mixed>|null
     */
    private ?array $constants = null;
    /**
     * Creates a new enum.
     *
     * @param mixed   $initialValue
     * @param boolean $strict
     */
    public function __construct($initialValue = null, private $strict = \false)
    {
        $this->change($initialValue);
    }
    /**
     * Changes the value of the enum.
     *
     * @param  mixed $value
     *
     * @return void
     */
    public function change($value)
    {
        if (!\in_array($value, $this->getConstList(), $this->strict)) {
            throw new \UnexpectedValueException('Value not a const in enum ' . $this::class);
        }
        $this->value = $value;
    }
    /**
     * Gets all constants (possible values) as an array.
     *
     * @param  boolean $includeDefault
     *
     * @return array
     */
    public function getConstList($includeDefault = \true)
    {
        if ($this->constants === null) {
            $reflection = new ReflectionClass($this);
            $this->constants = $reflection->getConstants();
        }
        if ($includeDefault) {
            return $this->constants;
        }
        $constants = $this->constants;
        unset($constants['__default']);
        return $constants;
    }
    /**
     * Gets current value.
     *
     * @return mixed
     */
    public function get()
    {
        return $this->value;
    }
    /**
     * Gets the name of the enum.
     *
     * @return string
     */
    public function __toString() : string
    {
        return (string) \array_search($this->value, $this->getConstList());
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/Common/BitSource.php000064400000007310150755130600021443 0ustar00<?php

/*
 * Copyright 2007 ZXing authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
namespace WP2FA_Vendor\Zxing\Common;

/**
 * <p>This provides an easy abstraction to read bits at a time from a sequence of bytes, where the
 * number of bits read is not often a multiple of 8.</p>
 *
 * <p>This class is thread-safe but not reentrant -- unless the caller modifies the bytes array
 * it passed in, in which case all bets are off.</p>
 *
 * @author Sean Owen
 */
final class BitSource
{
    private int $byteOffset = 0;
    private int $bitOffset = 0;
    /**
     * @param bytes $bytes from which this will read bits. Bits will be read from the first byte first.
     *              Bits are read within a byte from most-significant to least-significant bit.
     */
    public function __construct(private $bytes)
    {
    }
    /**
     * @return index of next bit in current byte which would be read by the next call to {@link #readBits(int)}.
     */
    public function getBitOffset()
    {
        return $this->bitOffset;
    }
    /**
     * @return index of next byte in input byte array which would be read by the next call to {@link #readBits(int)}.
     */
    public function getByteOffset()
    {
        return $this->byteOffset;
    }
    /**
     * @param number $numBits of bits to read
     *
     * @return int representing the bits read. The bits will appear as the least-significant
     *         bits of the int
     * @throws InvalidArgumentException if numBits isn't in [1,32] or more than is available
     */
    public function readBits($numBits)
    {
        if ($numBits < 1 || $numBits > 32 || $numBits > $this->available()) {
            throw new \InvalidArgumentException(\strval($numBits));
        }
        $result = 0;
        // First, read remainder from current byte
        if ($this->bitOffset > 0) {
            $bitsLeft = 8 - $this->bitOffset;
            $toRead = $numBits < $bitsLeft ? $numBits : $bitsLeft;
            $bitsToNotRead = $bitsLeft - $toRead;
            $mask = 0xff >> 8 - $toRead << $bitsToNotRead;
            $result = ($this->bytes[$this->byteOffset] & $mask) >> $bitsToNotRead;
            $numBits -= $toRead;
            $this->bitOffset += $toRead;
            if ($this->bitOffset == 8) {
                $this->bitOffset = 0;
                $this->byteOffset++;
            }
        }
        // Next read whole bytes
        if ($numBits > 0) {
            while ($numBits >= 8) {
                $result = $result << 8 | $this->bytes[$this->byteOffset] & 0xff;
                $this->byteOffset++;
                $numBits -= 8;
            }
            // Finally read a partial byte
            if ($numBits > 0) {
                $bitsToNotRead = 8 - $numBits;
                $mask = 0xff >> $bitsToNotRead << $bitsToNotRead;
                $result = $result << $numBits | ($this->bytes[$this->byteOffset] & $mask) >> $bitsToNotRead;
                $this->bitOffset += $numBits;
            }
        }
        return $result;
    }
    /**
     * @return number of bits that can be read successfully
     */
    public function available()
    {
        return 8 * ((\is_countable($this->bytes) ? \count($this->bytes) : 0) - $this->byteOffset) - $this->bitOffset;
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/Common/PerspectiveTransform.php000064400000012625150755130600023736 0ustar00<?php

/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace WP2FA_Vendor\Zxing\Common;

/**
 * <p>This class implements a perspective transform in two dimensions. Given four source and four
 * destination points, it will compute the transformation implied between them. The code is based
 * directly upon section 3.4.2 of George Wolberg's "Digital Image Warping"; see pages 54-56.</p>
 *
 * @author Sean Owen
 */
final class PerspectiveTransform
{
    private function __construct(private $a11, private $a21, private $a31, private $a12, private $a22, private $a32, private $a13, private $a23, private $a33)
    {
    }
    public static function quadrilateralToQuadrilateral($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $x0p, $y0p, $x1p, $y1p, $x2p, $y2p, $x3p, $y3p)
    {
        $qToS = self::quadrilateralToSquare($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3);
        $sToQ = self::squareToQuadrilateral($x0p, $y0p, $x1p, $y1p, $x2p, $y2p, $x3p, $y3p);
        return $sToQ->times($qToS);
    }
    public static function quadrilateralToSquare($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3)
    {
        // Here, the adjoint serves as the inverse:
        return self::squareToQuadrilateral($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3)->buildAdjoint();
    }
    public function buildAdjoint() : \WP2FA_Vendor\Zxing\Common\PerspectiveTransform
    {
        // Adjoint is the transpose of the cofactor matrix:
        return new PerspectiveTransform($this->a22 * $this->a33 - $this->a23 * $this->a32, $this->a23 * $this->a31 - $this->a21 * $this->a33, $this->a21 * $this->a32 - $this->a22 * $this->a31, $this->a13 * $this->a32 - $this->a12 * $this->a33, $this->a11 * $this->a33 - $this->a13 * $this->a31, $this->a12 * $this->a31 - $this->a11 * $this->a32, $this->a12 * $this->a23 - $this->a13 * $this->a22, $this->a13 * $this->a21 - $this->a11 * $this->a23, $this->a11 * $this->a22 - $this->a12 * $this->a21);
    }
    public static function squareToQuadrilateral($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3) : \WP2FA_Vendor\Zxing\Common\PerspectiveTransform
    {
        $dx3 = $x0 - $x1 + $x2 - $x3;
        $dy3 = $y0 - $y1 + $y2 - $y3;
        if ($dx3 == 0.0 && $dy3 == 0.0) {
            // Affine
            return new PerspectiveTransform($x1 - $x0, $x2 - $x1, $x0, $y1 - $y0, $y2 - $y1, $y0, 0.0, 0.0, 1.0);
        } else {
            $dx1 = $x1 - $x2;
            $dx2 = $x3 - $x2;
            $dy1 = $y1 - $y2;
            $dy2 = $y3 - $y2;
            $denominator = $dx1 * $dy2 - $dx2 * $dy1;
            $a13 = ($dx3 * $dy2 - $dx2 * $dy3) / $denominator;
            $a23 = ($dx1 * $dy3 - $dx3 * $dy1) / $denominator;
            return new PerspectiveTransform($x1 - $x0 + $a13 * $x1, $x3 - $x0 + $a23 * $x3, $x0, $y1 - $y0 + $a13 * $y1, $y3 - $y0 + $a23 * $y3, $y0, $a13, $a23, 1.0);
        }
    }
    public function times($other) : \WP2FA_Vendor\Zxing\Common\PerspectiveTransform
    {
        return new PerspectiveTransform($this->a11 * $other->a11 + $this->a21 * $other->a12 + $this->a31 * $other->a13, $this->a11 * $other->a21 + $this->a21 * $other->a22 + $this->a31 * $other->a23, $this->a11 * $other->a31 + $this->a21 * $other->a32 + $this->a31 * $other->a33, $this->a12 * $other->a11 + $this->a22 * $other->a12 + $this->a32 * $other->a13, $this->a12 * $other->a21 + $this->a22 * $other->a22 + $this->a32 * $other->a23, $this->a12 * $other->a31 + $this->a22 * $other->a32 + $this->a32 * $other->a33, $this->a13 * $other->a11 + $this->a23 * $other->a12 + $this->a33 * $other->a13, $this->a13 * $other->a21 + $this->a23 * $other->a22 + $this->a33 * $other->a23, $this->a13 * $other->a31 + $this->a23 * $other->a32 + $this->a33 * $other->a33);
    }
    public function transformPoints(&$points, &$yValues = 0) : void
    {
        if ($yValues) {
            $this->transformPoints_($points, $yValues);
            return;
        }
        $max = \is_countable($points) ? \count($points) : 0;
        $a11 = $this->a11;
        $a12 = $this->a12;
        $a13 = $this->a13;
        $a21 = $this->a21;
        $a22 = $this->a22;
        $a23 = $this->a23;
        $a31 = $this->a31;
        $a32 = $this->a32;
        $a33 = $this->a33;
        for ($i = 0; $i < $max; $i += 2) {
            $x = $points[$i];
            $y = $points[$i + 1];
            $denominator = $a13 * $x + $a23 * $y + $a33;
            $points[$i] = ($a11 * $x + $a21 * $y + $a31) / $denominator;
            $points[$i + 1] = ($a12 * $x + $a22 * $y + $a32) / $denominator;
        }
    }
    public function transformPoints_(&$xValues, &$yValues) : void
    {
        $n = \is_countable($xValues) ? \count($xValues) : 0;
        for ($i = 0; $i < $n; $i++) {
            $x = $xValues[$i];
            $y = $yValues[$i];
            $denominator = $this->a13 * $x + $this->a23 * $y + $this->a33;
            $xValues[$i] = ($this->a11 * $x + $this->a21 * $y + $this->a31) / $denominator;
            $yValues[$i] = ($this->a12 * $x + $this->a22 * $y + $this->a32) / $denominator;
        }
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/Common/GridSampler.php000064400000015741150755130600021764 0ustar00<?php

/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace WP2FA_Vendor\Zxing\Common;

use WP2FA_Vendor\Zxing\NotFoundException;
/**
 * Implementations of this class can, given locations of finder patterns for a QR code in an
 * image, sample the right points in the image to reconstruct the QR code, accounting for
 * perspective distortion. It is abstracted since it is relatively expensive and should be allowed
 * to take advantage of platform-specific optimized implementations, like Sun's Java Advanced
 * Imaging library, but which may not be available in other environments such as J2ME, and vice
 * versa.
 *
 * The implementation used can be controlled by calling {@link #setGridSampler(GridSampler)}
 * with an instance of a class which implements this interface.
 *
 * @author Sean Owen
 */
abstract class GridSampler
{
    /**
     * @var mixed|\Zxing\Common\DefaultGridSampler|null
     */
    private static $gridSampler;
    /**
     * Sets the implementation of GridSampler used by the library. One global
     * instance is stored, which may sound problematic. But, the implementation provided
     * ought to be appropriate for the entire platform, and all uses of this library
     * in the whole lifetime of the JVM. For instance, an Android activity can swap in
     * an implementation that takes advantage of native platform libraries.
     *
     * @param $newGridSampler The platform-specific object to install.
     */
    public static function setGridSampler($newGridSampler) : void
    {
        self::$gridSampler = $newGridSampler;
    }
    /**
     * @return GridSampler the current implementation of GridSampler
     */
    public static function getInstance()
    {
        if (!self::$gridSampler) {
            self::$gridSampler = new DefaultGridSampler();
        }
        return self::$gridSampler;
    }
    /**
     * <p>Checks a set of points that have been transformed to sample points on an image against
     * the image's dimensions to see if the point are even within the image.</p>
     *
     * <p>This method will actually "nudge" the endpoints back onto the image if they are found to be
     * barely (less than 1 pixel) off the image. This accounts for imperfect detection of finder
     * patterns in an image where the QR Code runs all the way to the image border.</p>
     *
     * <p>For efficiency, the method will check points from either end of the line until one is found
     * to be within the image. Because the set of points are assumed to be linear, this is valid.</p>
     *
     * @param image  $image into which the points should map
     * @param actual $points points in x1,y1,...,xn,yn form
     *
     * @throws NotFoundException if an endpoint is lies outside the image boundaries
     */
    protected static function checkAndNudgePoints($image, $points)
    {
        $width = $image->getWidth();
        $height = $image->getHeight();
        // Check and nudge points from start until we see some that are OK:
        $nudged = \true;
        for ($offset = 0; $offset < (\is_countable($points) ? \count($points) : 0) && $nudged; $offset += 2) {
            $x = (int) $points[$offset];
            $y = (int) $points[$offset + 1];
            if ($x < -1 || $x > $width || $y < -1 || $y > $height) {
                throw NotFoundException::getNotFoundInstance();
            }
            $nudged = \false;
            if ($x == -1) {
                $points[$offset] = 0.0;
                $nudged = \true;
            } elseif ($x == $width) {
                $points[$offset] = $width - 1;
                $nudged = \true;
            }
            if ($y == -1) {
                $points[$offset + 1] = 0.0;
                $nudged = \true;
            } elseif ($y == $height) {
                $points[$offset + 1] = $height - 1;
                $nudged = \true;
            }
        }
        // Check and nudge points from end:
        $nudged = \true;
        for ($offset = (\is_countable($points) ? \count($points) : 0) - 2; $offset >= 0 && $nudged; $offset -= 2) {
            $x = (int) $points[$offset];
            $y = (int) $points[$offset + 1];
            if ($x < -1 || $x > $width || $y < -1 || $y > $height) {
                throw NotFoundException::getNotFoundInstance();
            }
            $nudged = \false;
            if ($x == -1) {
                $points[$offset] = 0.0;
                $nudged = \true;
            } elseif ($x == $width) {
                $points[$offset] = $width - 1;
                $nudged = \true;
            }
            if ($y == -1) {
                $points[$offset + 1] = 0.0;
                $nudged = \true;
            } elseif ($y == $height) {
                $points[$offset + 1] = $height - 1;
                $nudged = \true;
            }
        }
    }
    /**
     * Samples an image for a rectangular matrix of bits of the given dimension. The sampling
     * transformation is determined by the coordinates of 4 points, in the original and transformed
     * image space.
     *
     * @param image      $image to sample
     * @param width $dimensionX of {@link BitMatrix} to sample from image
     * @param height $dimensionY of {@link BitMatrix} to sample from image
     * @param point      $p1ToX 1 preimage X
     * @param point      $p1ToY 1 preimage Y
     * @param point      $p2ToX 2 preimage X
     * @param point      $p2ToY 2 preimage Y
     * @param point      $p3ToX 3 preimage X
     * @param point      $p3ToY 3 preimage Y
     * @param point      $p4ToX 4 preimage X
     * @param point      $p4ToY 4 preimage Y
     * @param point    $p1FromX 1 image X
     * @param point    $p1FromY 1 image Y
     * @param point    $p2FromX 2 image X
     * @param point    $p2FromY 2 image Y
     * @param point    $p3FromX 3 image X
     * @param point    $p3FromY 3 image Y
     * @param point    $p4FromX 4 image X
     * @param point    $p4FromY 4 image Y
     *
     * @return {@link BitMatrix} representing a grid of points sampled from the image within a region
     *   defined by the "from" parameters
     * @throws NotFoundException if image can't be sampled, for example, if the transformation defined
     *   by the given points is invalid or results in sampling outside the image boundaries
     */
    public abstract function sampleGrid($image, $dimensionX, $dimensionY, $p1ToX, $p1ToY, $p2ToX, $p2ToY, $p3ToX, $p3ToY, $p4ToX, $p4ToY, $p1FromX, $p1FromY, $p2FromX, $p2FromY, $p3FromX, $p3FromY, $p4FromX, $p4FromY);
    public abstract function sampleGrid_($image, $dimensionX, $dimensionY, $transform);
}
vendor/khanamiryan/qrcode-detector-decoder/lib/Common/BitArray.php000064400000032123150755130600021261 0ustar00<?php

/**
 * Created by PhpStorm.
 * User: Ashot
 * Date: 3/25/15
 * Time: 11:51
 */
/*
 * Copyright 2007 ZXing authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
namespace WP2FA_Vendor\Zxing\Common;

/**
 * <p>A simple, fast array of bits, represented compactly by an array of ints internally.</p>
 *
 * @author Sean Owen
 */
final class BitArray
{
    /**
     * @var mixed[]|mixed|int[]|null
     */
    private $bits;
    /**
     * @var mixed|null
     */
    private $size;
    public function __construct($bits = [], $size = 0)
    {
        if (!$bits && !$size) {
            $this->{$size} = 0;
            $this->bits = [];
        } elseif ($bits && !$size) {
            $this->size = $bits;
            $this->bits = self::makeArray($bits);
        } else {
            $this->bits = $bits;
            $this->size = $size;
        }
    }
    private static function makeArray($size)
    {
        return [];
    }
    public function getSize()
    {
        return $this->size;
    }
    public function getSizeInBytes()
    {
        return ($this->size + 7) / 8;
    }
    /**
     * Sets bit i.
     *
     * @param bit $i to set
     */
    public function set($i) : void
    {
        $this->bits[(int) ($i / 32)] |= 1 << ($i & 0x1f);
        $this->bits[(int) ($i / 32)] = $this->bits[(int) ($i / 32)];
    }
    /**
     * Flips bit i.
     *
     * @param bit $i to set
     */
    public function flip($i) : void
    {
        $this->bits[(int) ($i / 32)] ^= 1 << ($i & 0x1f);
        $this->bits[(int) ($i / 32)] = $this->bits[(int) ($i / 32)];
    }
    /**
     * @param first $from bit to check
     *
     * @return index of first bit that is set, starting from the given index, or size if none are set
     *  at or beyond this given index
     * @see #getNextUnset(int)
     */
    public function getNextSet($from)
    {
        if ($from >= $this->size) {
            return $this->size;
        }
        $bitsOffset = (int) ($from / 32);
        $currentBits = (int) $this->bits[$bitsOffset];
        // mask off lesser bits first
        $currentBits &= ~((1 << ($from & 0x1f)) - 1);
        while ($currentBits == 0) {
            if (++$bitsOffset == (\is_countable($this->bits) ? \count($this->bits) : 0)) {
                return $this->size;
            }
            $currentBits = $this->bits[$bitsOffset];
        }
        $result = $bitsOffset * 32 + numberOfTrailingZeros($currentBits);
        //numberOfTrailingZeros
        return $result > $this->size ? $this->size : $result;
    }
    /**
     * @param index $from to start looking for unset bit
     *
     * @return index of next unset bit, or {@code size} if none are unset until the end
     * @see #getNextSet(int)
     */
    public function getNextUnset($from)
    {
        if ($from >= $this->size) {
            return $this->size;
        }
        $bitsOffset = (int) ($from / 32);
        $currentBits = ~$this->bits[$bitsOffset];
        // mask off lesser bits first
        $currentBits &= ~((1 << ($from & 0x1f)) - 1);
        while ($currentBits == 0) {
            if (++$bitsOffset == (\is_countable($this->bits) ? \count($this->bits) : 0)) {
                return $this->size;
            }
            $currentBits = ~$this->bits[$bitsOffset];
        }
        $result = $bitsOffset * 32 + numberOfTrailingZeros($currentBits);
        return $result > $this->size ? $this->size : $result;
    }
    /**
     * Sets a block of 32 bits, starting at bit i.
     *
     * @param first       $i bit to set
     * @param the $newBits new value of the next 32 bits. Note again that the least-significant bit
     *                corresponds to bit i, the next-least-significant to i+1, and so on.
     */
    public function setBulk($i, $newBits) : void
    {
        $this->bits[(int) ($i / 32)] = $newBits;
    }
    /**
     * Sets a range of bits.
     *
     * @param start $start of range, inclusive.
     * @param end   $end of range, exclusive
     */
    public function setRange($start, $end)
    {
        if ($end < $start) {
            throw new \InvalidArgumentException();
        }
        if ($end == $start) {
            return;
        }
        $end--;
        // will be easier to treat this as the last actually set bit -- inclusive
        $firstInt = (int) ($start / 32);
        $lastInt = (int) ($end / 32);
        for ($i = $firstInt; $i <= $lastInt; $i++) {
            $firstBit = $i > $firstInt ? 0 : $start & 0x1f;
            $lastBit = $i < $lastInt ? 31 : $end & 0x1f;
            $mask = 0;
            if ($firstBit == 0 && $lastBit == 31) {
                $mask = -1;
            } else {
                $mask = 0;
                for ($j = $firstBit; $j <= $lastBit; $j++) {
                    $mask |= 1 << $j;
                }
            }
            $this->bits[$i] = $this->bits[$i] | $mask;
        }
    }
    /**
     * Clears all bits (sets to false).
     */
    public function clear() : void
    {
        $max = \is_countable($this->bits) ? \count($this->bits) : 0;
        for ($i = 0; $i < $max; $i++) {
            $this->bits[$i] = 0;
        }
    }
    /**
     * Efficient method to check if a range of bits is set, or not set.
     *
     * @param start $start of range, inclusive.
     * @param end   $end of range, exclusive
     * @param if $value true, checks that bits in range are set, otherwise checks that they are not set
     *
     * @return true iff all bits are set or not set in range, according to value argument
     * @throws InvalidArgumentException if end is less than or equal to start
     */
    public function isRange($start, $end, $value)
    {
        if ($end < $start) {
            throw new \InvalidArgumentException();
        }
        if ($end == $start) {
            return \true;
            // empty range matches
        }
        $end--;
        // will be easier to treat this as the last actually set bit -- inclusive
        $firstInt = (int) ($start / 32);
        $lastInt = (int) ($end / 32);
        for ($i = $firstInt; $i <= $lastInt; $i++) {
            $firstBit = $i > $firstInt ? 0 : $start & 0x1f;
            $lastBit = $i < $lastInt ? 31 : $end & 0x1f;
            $mask = 0;
            if ($firstBit == 0 && $lastBit == 31) {
                $mask = -1;
            } else {
                $mask = 0;
                for ($j = $firstBit; $j <= $lastBit; $j++) {
                    $mask = $mask | 1 << $j;
                }
            }
            // Return false if we're looking for 1s and the masked bits[i] isn't all 1s (that is,
            // equals the mask, or we're looking for 0s and the masked portion is not all 0s
            if (($this->bits[$i] & $mask) != ($value ? $mask : 0)) {
                return \false;
            }
        }
        return \true;
    }
    /**
     * Appends the least-significant bits, from value, in order from most-significant to
     * least-significant. For example, appending 6 bits from 0x000001E will append the bits
     * 0, 1, 1, 1, 1, 0 in that order.
     *
     * @param $value   {@code int} containing bits to append
     * @param bits $numBits from value to append
     */
    public function appendBits($value, $numBits)
    {
        if ($numBits < 0 || $numBits > 32) {
            throw new \InvalidArgumentException("Num bits must be between 0 and 32");
        }
        $this->ensureCapacity($this->size + $numBits);
        for ($numBitsLeft = $numBits; $numBitsLeft > 0; $numBitsLeft--) {
            $this->appendBit(($value >> $numBitsLeft - 1 & 0x1) == 1);
        }
    }
    private function ensureCapacity($size) : void
    {
        if ($size > (\is_countable($this->bits) ? \count($this->bits) : 0) * 32) {
            $newBits = self::makeArray($size);
            $newBits = arraycopy($this->bits, 0, $newBits, 0, \is_countable($this->bits) ? \count($this->bits) : 0);
            $this->bits = $newBits;
        }
    }
    public function appendBit($bit) : void
    {
        $this->ensureCapacity($this->size + 1);
        if ($bit) {
            $this->bits[(int) ($this->size / 32)] |= 1 << ($this->size & 0x1f);
        }
        $this->size++;
    }
    public function appendBitArray($other) : void
    {
        $otherSize = $other->size;
        $this->ensureCapacity($this->size + $otherSize);
        for ($i = 0; $i < $otherSize; $i++) {
            $this->appendBit($other->get($i));
        }
    }
    public function _xor($other)
    {
        if ((\is_countable($this->bits) ? \count($this->bits) : 0) !== (\is_countable($other->bits) ? \count($other->bits) : 0)) {
            throw new \InvalidArgumentException("Sizes don't match");
        }
        $count = \is_countable($this->bits) ? \count($this->bits) : 0;
        for ($i = 0; $i < $count; $i++) {
            // The last byte could be incomplete (i.e. not have 8 bits in
            // it) but there is no problem since 0 XOR 0 == 0.
            $this->bits[$i] ^= $other->bits[$i];
        }
    }
    /**
     *
     * @param first $bitOffset bit to start writing
     * @param array     $array to write into. Bytes are written most-significant byte first. This is the opposite
     *                  of the internal representation, which is exposed by {@link #getBitArray()}
     * @param position    $offset in array to start writing
     * @param how  $numBytes many bytes to write
     */
    public function toBytes($bitOffset, &$array, $offset, $numBytes) : void
    {
        for ($i = 0; $i < $numBytes; $i++) {
            $theByte = 0;
            for ($j = 0; $j < 8; $j++) {
                if ($this->get($bitOffset)) {
                    $theByte |= 1 << 7 - $j;
                }
                $bitOffset++;
            }
            $array[(int) ($offset + $i)] = $theByte;
        }
    }
    /**
     * @param $i ; bit to get
     *
     * @return true iff bit i is set
     */
    public function get($i)
    {
        $key = (int) ($i / 32);
        return ($this->bits[$key] & 1 << ($i & 0x1f)) != 0;
    }
    /**
     * @return array underlying array of ints. The first element holds the first 32 bits, and the least
     *         significant bit is bit 0.
     */
    public function getBitArray()
    {
        return $this->bits;
    }
    /**
     * Reverses all bits in the array.
     */
    public function reverse() : void
    {
        $newBits = [];
        // reverse all int's first
        $len = ($this->size - 1) / 32;
        $oldBitsLen = $len + 1;
        for ($i = 0; $i < $oldBitsLen; $i++) {
            $x = $this->bits[$i];
            /*
            			 $x = (($x >>  1) & 0x55555555L) | (($x & 0x55555555L) <<  1);
            				  $x = (($x >>  2) & 0x33333333L) | (($x & 0x33333333L) <<  2);
            				  $x = (($x >>  4) & 0x0f0f0f0fL) | (($x & 0x0f0f0f0fL) <<  4);
            				  $x = (($x >>  8) & 0x00ff00ffL) | (($x & 0x00ff00ffL) <<  8);
            				  $x = (($x >> 16) & 0x0000ffffL) | (($x & 0x0000ffffL) << 16);*/
            $x = $x >> 1 & 0x55555555 | ($x & 0x55555555) << 1;
            $x = $x >> 2 & 0x33333333 | ($x & 0x33333333) << 2;
            $x = $x >> 4 & 0xf0f0f0f | ($x & 0xf0f0f0f) << 4;
            $x = $x >> 8 & 0xff00ff | ($x & 0xff00ff) << 8;
            $x = $x >> 16 & 0xffff | ($x & 0xffff) << 16;
            $newBits[(int) $len - $i] = (int) $x;
        }
        // now correct the int's if the bit size isn't a multiple of 32
        if ($this->size != $oldBitsLen * 32) {
            $leftOffset = $oldBitsLen * 32 - $this->size;
            $mask = 1;
            for ($i = 0; $i < 31 - $leftOffset; $i++) {
                $mask = $mask << 1 | 1;
            }
            $currentInt = $newBits[0] >> $leftOffset & $mask;
            for ($i = 1; $i < $oldBitsLen; $i++) {
                $nextInt = $newBits[$i];
                $currentInt |= $nextInt << 32 - $leftOffset;
                $newBits[(int) $i - 1] = $currentInt;
                $currentInt = $nextInt >> $leftOffset & $mask;
            }
            $newBits[(int) $oldBitsLen - 1] = $currentInt;
        }
        //        $bits = $newBits;
    }
    public function equals($o)
    {
        if (!$o instanceof BitArray) {
            return \false;
        }
        $other = $o;
        return $this->size == $other->size && $this->bits === $other->bits;
    }
    public function hashCode()
    {
        return 31 * $this->size + hashCode($this->bits);
    }
    public function toString()
    {
        $result = '';
        for ($i = 0; $i < $this->size; $i++) {
            if (($i & 0x7) == 0) {
                $result .= ' ';
            }
            $result .= $this->get($i) ? 'X' : '.';
        }
        return (string) $result;
    }
    public function _clone() : \WP2FA_Vendor\Zxing\Common\BitArray
    {
        return new BitArray($this->bits, $this->size);
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/Common/HybridBinarizer.php000064400000025367150755130600022647 0ustar00<?php

/*
* Copyright 2009 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace WP2FA_Vendor\Zxing\Common;

use WP2FA_Vendor\Zxing\Binarizer;
/**
 * This class implements a local thresholding algorithm, which while slower than the
 * GlobalHistogramBinarizer, is fairly efficient for what it does. It is designed for
 * high frequency images of barcodes with black data on white backgrounds. For this application,
 * it does a much better job than a global blackpoint with severe shadows and gradients.
 * However it tends to produce artifacts on lower frequency images and is therefore not
 * a good general purpose binarizer for uses outside ZXing.
 *
 * This class extends GlobalHistogramBinarizer, using the older histogram approach for 1D readers,
 * and the newer local approach for 2D readers. 1D decoding using a per-row histogram is already
 * inherently local, and only fails for horizontal gradients. We can revisit that problem later,
 * but for now it was not a win to use local blocks for 1D.
 *
 * This Binarizer is the default for the unit tests and the recommended class for library users.
 *
 * @author dswitkin@google.com (Daniel Switkin)
 */
final class HybridBinarizer extends GlobalHistogramBinarizer
{
    // This class uses 5x5 blocks to compute local luminance, where each block is 8x8 pixels.
    // So this is the smallest dimension in each axis we can accept.
    private static int $BLOCK_SIZE_POWER = 3;
    private static int $BLOCK_SIZE = 8;
    // ...0100...00
    private static int $BLOCK_SIZE_MASK = 7;
    // ...0011...11
    private static int $MINIMUM_DIMENSION = 40;
    private static int $MIN_DYNAMIC_RANGE = 24;
    private ?\WP2FA_Vendor\Zxing\Common\BitMatrix $matrix = null;
    public function __construct($source)
    {
        parent::__construct($source);
        self::$BLOCK_SIZE_POWER = 3;
        self::$BLOCK_SIZE = 1 << self::$BLOCK_SIZE_POWER;
        // ...0100...00
        self::$BLOCK_SIZE_MASK = self::$BLOCK_SIZE - 1;
        // ...0011...11
        self::$MINIMUM_DIMENSION = self::$BLOCK_SIZE * 5;
        self::$MIN_DYNAMIC_RANGE = 24;
    }
    /**
     * Calculates the final BitMatrix once for all requests. This could be called once from the
     * constructor instead, but there are some advantages to doing it lazily, such as making
     * profiling easier, and not doing heavy lifting when callers don't expect it.
     */
    public function getBlackMatrix()
    {
        if ($this->matrix !== null) {
            return $this->matrix;
        }
        $source = $this->getLuminanceSource();
        $width = $source->getWidth();
        $height = $source->getHeight();
        if ($width >= self::$MINIMUM_DIMENSION && $height >= self::$MINIMUM_DIMENSION) {
            $luminances = $source->getMatrix();
            $subWidth = $width >> self::$BLOCK_SIZE_POWER;
            if (($width & self::$BLOCK_SIZE_MASK) != 0) {
                $subWidth++;
            }
            $subHeight = $height >> self::$BLOCK_SIZE_POWER;
            if (($height & self::$BLOCK_SIZE_MASK) != 0) {
                $subHeight++;
            }
            $blackPoints = self::calculateBlackPoints($luminances, $subWidth, $subHeight, $width, $height);
            $newMatrix = new BitMatrix($width, $height);
            self::calculateThresholdForBlock($luminances, $subWidth, $subHeight, $width, $height, $blackPoints, $newMatrix);
            $this->matrix = $newMatrix;
        } else {
            // If the image is too small, fall back to the global histogram approach.
            $this->matrix = parent::getBlackMatrix();
        }
        return $this->matrix;
    }
    /**
     * Calculates a single black point for each block of pixels and saves it away.
     * See the following thread for a discussion of this algorithm:
     *  http://groups.google.com/group/zxing/browse_thread/thread/d06efa2c35a7ddc0
     */
    private static function calculateBlackPoints($luminances, $subWidth, $subHeight, $width, $height)
    {
        $blackPoints = fill_array(0, $subHeight, 0);
        foreach ($blackPoints as $key => $point) {
            $blackPoints[$key] = fill_array(0, $subWidth, 0);
        }
        for ($y = 0; $y < $subHeight; $y++) {
            $yoffset = $y << self::$BLOCK_SIZE_POWER;
            $maxYOffset = $height - self::$BLOCK_SIZE;
            if ($yoffset > $maxYOffset) {
                $yoffset = $maxYOffset;
            }
            for ($x = 0; $x < $subWidth; $x++) {
                $xoffset = $x << self::$BLOCK_SIZE_POWER;
                $maxXOffset = $width - self::$BLOCK_SIZE;
                if ($xoffset > $maxXOffset) {
                    $xoffset = $maxXOffset;
                }
                $sum = 0;
                $min = 0xff;
                $max = 0;
                for ($yy = 0, $offset = $yoffset * $width + $xoffset; $yy < self::$BLOCK_SIZE; $yy++, $offset += $width) {
                    for ($xx = 0; $xx < self::$BLOCK_SIZE; $xx++) {
                        $pixel = (int) $luminances[(int) ($offset + $xx)] & 0xff;
                        $sum += $pixel;
                        // still looking for good contrast
                        if ($pixel < $min) {
                            $min = $pixel;
                        }
                        if ($pixel > $max) {
                            $max = $pixel;
                        }
                    }
                    // short-circuit min/max tests once dynamic range is met
                    if ($max - $min > self::$MIN_DYNAMIC_RANGE) {
                        // finish the rest of the rows quickly
                        for ($yy++, $offset += $width; $yy < self::$BLOCK_SIZE; $yy++, $offset += $width) {
                            for ($xx = 0; $xx < self::$BLOCK_SIZE; $xx++) {
                                $sum += $luminances[$offset + $xx] & 0xff;
                            }
                        }
                    }
                }
                // The default estimate is the average of the values in the block.
                $average = $sum >> self::$BLOCK_SIZE_POWER * 2;
                if ($max - $min <= self::$MIN_DYNAMIC_RANGE) {
                    // If variation within the block is low, assume this is a block with only light or only
                    // dark pixels. In that case we do not want to use the average, as it would divide this
                    // low contrast area into black and white pixels, essentially creating data out of noise.
                    //
                    // The default assumption is that the block is light/background. Since no estimate for
                    // the level of dark pixels exists locally, use half the min for the block.
                    $average = (int) ($min / 2);
                    if ($y > 0 && $x > 0) {
                        // Correct the "white background" assumption for blocks that have neighbors by comparing
                        // the pixels in this block to the previously calculated black points. This is based on
                        // the fact that dark barcode symbology is always surrounded by some amount of light
                        // background for which reasonable black point estimates were made. The bp estimated at
                        // the boundaries is used for the interior.
                        // The (min < bp) is arbitrary but works better than other heuristics that were tried.
                        $averageNeighborBlackPoint = (int) (($blackPoints[$y - 1][$x] + 2 * $blackPoints[$y][$x - 1] + $blackPoints[$y - 1][$x - 1]) / 4);
                        if ($min < $averageNeighborBlackPoint) {
                            $average = $averageNeighborBlackPoint;
                        }
                    }
                }
                $blackPoints[$y][$x] = (int) $average;
            }
        }
        return $blackPoints;
    }
    /**
     * For each block in the image, calculate the average black point using a 5x5 grid
     * of the blocks around it. Also handles the corner cases (fractional blocks are computed based
     * on the last pixels in the row/column which are also used in the previous block).
     */
    private static function calculateThresholdForBlock($luminances, $subWidth, $subHeight, $width, $height, $blackPoints, $matrix) : void
    {
        for ($y = 0; $y < $subHeight; $y++) {
            $yoffset = $y << self::$BLOCK_SIZE_POWER;
            $maxYOffset = $height - self::$BLOCK_SIZE;
            if ($yoffset > $maxYOffset) {
                $yoffset = $maxYOffset;
            }
            for ($x = 0; $x < $subWidth; $x++) {
                $xoffset = $x << self::$BLOCK_SIZE_POWER;
                $maxXOffset = $width - self::$BLOCK_SIZE;
                if ($xoffset > $maxXOffset) {
                    $xoffset = $maxXOffset;
                }
                $left = self::cap($x, 2, $subWidth - 3);
                $top = self::cap($y, 2, $subHeight - 3);
                $sum = 0;
                for ($z = -2; $z <= 2; $z++) {
                    $blackRow = $blackPoints[$top + $z];
                    $sum += $blackRow[$left - 2] + $blackRow[$left - 1] + $blackRow[$left] + $blackRow[$left + 1] + $blackRow[$left + 2];
                }
                $average = (int) ($sum / 25);
                self::thresholdBlock($luminances, $xoffset, $yoffset, $average, $width, $matrix);
            }
        }
    }
    private static function cap($value, $min, $max)
    {
        if ($value < $min) {
            return $min;
        } elseif ($value > $max) {
            return $max;
        } else {
            return $value;
        }
    }
    /**
     * Applies a single threshold to a block of pixels.
     */
    private static function thresholdBlock($luminances, $xoffset, $yoffset, $threshold, $stride, $matrix) : void
    {
        for ($y = 0, $offset = $yoffset * $stride + $xoffset; $y < self::$BLOCK_SIZE; $y++, $offset += $stride) {
            for ($x = 0; $x < self::$BLOCK_SIZE; $x++) {
                // Comparison needs to be <= so that black == 0 pixels are black even if the threshold is 0.
                if (($luminances[$offset + $x] & 0xff) <= $threshold) {
                    $matrix->set($xoffset + $x, $yoffset + $y);
                }
            }
        }
    }
    public function createBinarizer($source) : \WP2FA_Vendor\Zxing\Common\HybridBinarizer
    {
        return new HybridBinarizer($source);
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/Common/DetectorResult.php000064400000002256150755130600022520 0ustar00<?php

/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace WP2FA_Vendor\Zxing\Common;

/**
 * <p>Encapsulates the result of detecting a barcode in an image. This includes the raw
 * matrix of black/white pixels corresponding to the barcode, and possibly points of interest
 * in the image, like the location of finder patterns or corners of the barcode in the image.</p>
 *
 * @author Sean Owen
 */
class DetectorResult
{
    public function __construct(private $bits, private $points)
    {
    }
    public final function getBits()
    {
        return $this->bits;
    }
    public final function getPoints()
    {
        return $this->points;
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/Common/Reedsolomon/ReedSolomonDecoder.php000064400000017157150755130600025560 0ustar00<?php

/*
 * Copyright 2007 ZXing authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
namespace WP2FA_Vendor\Zxing\Common\Reedsolomon;

/**
 * <p>Implements Reed-Solomon decoding, as the name implies.</p>
 *
 * <p>The algorithm will not be explained here, but the following references were helpful
 * in creating this implementation:</p>
 *
 * <ul>
 * <li>Bruce Maggs.
 * <a href="http://www.cs.cmu.edu/afs/cs.cmu.edu/project/pscico-guyb/realworld/www/rs_decode.ps">
 * "Decoding Reed-Solomon Codes"</a> (see discussion of Forney's Formula)</li>
 * <li>J.I. Hall. <a href="www.mth.msu.edu/~jhall/classes/codenotes/GRS.pdf">
 * "Chapter 5. Generalized Reed-Solomon Codes"</a>
 * (see discussion of Euclidean algorithm)</li>
 * </ul>
 *
 * <p>Much credit is due to William Rucklidge since portions of this code are an indirect
 * port of his C++ Reed-Solomon implementation.</p>
 *
 * @author Sean Owen
 * @author William Rucklidge
 * @author sanfordsquires
 */
final class ReedSolomonDecoder
{
    public function __construct(private $field)
    {
    }
    /**
     * <p>Decodes given set of received codewords, which include both data and error-correction
     * codewords. Really, this means it uses Reed-Solomon to detect and correct errors, in-place,
     * in the input.</p>
     *
     * @param data $received and error-correction codewords
     * @param number     $twoS of error-correction codewords available
     *
     * @throws ReedSolomonException if decoding fails for any reason
     */
    public function decode(&$received, $twoS)
    {
        $poly = new GenericGFPoly($this->field, $received);
        $syndromeCoefficients = fill_array(0, $twoS, 0);
        $noError = \true;
        for ($i = 0; $i < $twoS; $i++) {
            $eval = $poly->evaluateAt($this->field->exp($i + $this->field->getGeneratorBase()));
            $syndromeCoefficients[(\is_countable($syndromeCoefficients) ? \count($syndromeCoefficients) : 0) - 1 - $i] = $eval;
            if ($eval != 0) {
                $noError = \false;
            }
        }
        if ($noError) {
            return;
        }
        $syndrome = new GenericGFPoly($this->field, $syndromeCoefficients);
        $sigmaOmega = $this->runEuclideanAlgorithm($this->field->buildMonomial($twoS, 1), $syndrome, $twoS);
        $sigma = $sigmaOmega[0];
        $omega = $sigmaOmega[1];
        $errorLocations = $this->findErrorLocations($sigma);
        $errorMagnitudes = $this->findErrorMagnitudes($omega, $errorLocations);
        $errorLocationsCount = \is_countable($errorLocations) ? \count($errorLocations) : 0;
        for ($i = 0; $i < $errorLocationsCount; $i++) {
            $position = (\is_countable($received) ? \count($received) : 0) - 1 - $this->field->log($errorLocations[$i]);
            if ($position < 0) {
                throw new ReedSolomonException("Bad error location");
            }
            $received[$position] = GenericGF::addOrSubtract($received[$position], $errorMagnitudes[$i]);
        }
    }
    private function runEuclideanAlgorithm($a, $b, $R)
    {
        // Assume a's degree is >= b's
        if ($a->getDegree() < $b->getDegree()) {
            $temp = $a;
            $a = $b;
            $b = $temp;
        }
        $rLast = $a;
        $r = $b;
        $tLast = $this->field->getZero();
        $t = $this->field->getOne();
        // Run Euclidean algorithm until r's degree is less than R/2
        while ($r->getDegree() >= $R / 2) {
            $rLastLast = $rLast;
            $tLastLast = $tLast;
            $rLast = $r;
            $tLast = $t;
            // Divide rLastLast by rLast, with quotient in q and remainder in r
            if ($rLast->isZero()) {
                // Oops, Euclidean algorithm already terminated?
                throw new ReedSolomonException("r_{i-1} was zero");
            }
            $r = $rLastLast;
            $q = $this->field->getZero();
            $denominatorLeadingTerm = $rLast->getCoefficient($rLast->getDegree());
            $dltInverse = $this->field->inverse($denominatorLeadingTerm);
            while ($r->getDegree() >= $rLast->getDegree() && !$r->isZero()) {
                $degreeDiff = $r->getDegree() - $rLast->getDegree();
                $scale = $this->field->multiply($r->getCoefficient($r->getDegree()), $dltInverse);
                $q = $q->addOrSubtract($this->field->buildMonomial($degreeDiff, $scale));
                $r = $r->addOrSubtract($rLast->multiplyByMonomial($degreeDiff, $scale));
            }
            $t = $q->multiply($tLast)->addOrSubtract($tLastLast);
            if ($r->getDegree() >= $rLast->getDegree()) {
                throw new ReedSolomonException("Division algorithm failed to reduce polynomial?");
            }
        }
        $sigmaTildeAtZero = $t->getCoefficient(0);
        if ($sigmaTildeAtZero == 0) {
            throw new ReedSolomonException("sigmaTilde(0) was zero");
        }
        $inverse = $this->field->inverse($sigmaTildeAtZero);
        $sigma = $t->multiply($inverse);
        $omega = $r->multiply($inverse);
        return [$sigma, $omega];
    }
    private function findErrorLocations($errorLocator)
    {
        // This is a direct application of Chien's search
        $numErrors = $errorLocator->getDegree();
        if ($numErrors == 1) {
            // shortcut
            return [$errorLocator->getCoefficient(1)];
        }
        $result = fill_array(0, $numErrors, 0);
        $e = 0;
        for ($i = 1; $i < $this->field->getSize() && $e < $numErrors; $i++) {
            if ($errorLocator->evaluateAt($i) == 0) {
                $result[$e] = $this->field->inverse($i);
                $e++;
            }
        }
        if ($e != $numErrors) {
            throw new ReedSolomonException("Error locator degree does not match number of roots");
        }
        return $result;
    }
    private function findErrorMagnitudes($errorEvaluator, $errorLocations)
    {
        // This is directly applying Forney's Formula
        $s = \is_countable($errorLocations) ? \count($errorLocations) : 0;
        $result = fill_array(0, $s, 0);
        for ($i = 0; $i < $s; $i++) {
            $xiInverse = $this->field->inverse($errorLocations[$i]);
            $denominator = 1;
            for ($j = 0; $j < $s; $j++) {
                if ($i != $j) {
                    //denominator = field.multiply(denominator,
                    //    GenericGF.addOrSubtract(1, field.multiply(errorLocations[j], xiInverse)));
                    // Above should work but fails on some Apple and Linux JDKs due to a Hotspot bug.
                    // Below is a funny-looking workaround from Steven Parkes
                    $term = $this->field->multiply($errorLocations[$j], $xiInverse);
                    $termPlus1 = ($term & 0x1) == 0 ? $term | 1 : $term & ~1;
                    $denominator = $this->field->multiply($denominator, $termPlus1);
                }
            }
            $result[$i] = $this->field->multiply($errorEvaluator->evaluateAt($xiInverse), $this->field->inverse($denominator));
            if ($this->field->getGeneratorBase() != 0) {
                $result[$i] = $this->field->multiply($result[$i], $xiInverse);
            }
        }
        return $result;
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/Common/Reedsolomon/GenericGF.php000064400000013024150755130600023622 0ustar00<?php

/*
 * Copyright 2007 ZXing authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
namespace WP2FA_Vendor\Zxing\Common\Reedsolomon;

/**
 * <p>This class contains utility methods for performing mathematical operations over
 * the Galois Fields. Operations use a given primitive polynomial in calculations.</p>
 *
 * <p>Throughout this package, elements of the GF are represented as an {@code int}
 * for convenience and speed (but at the cost of memory).
 * </p>
 *
 * @author Sean Owen
 * @author David Olivier
 */
final class GenericGF
{
    public static $AZTEC_DATA_12;
    public static $AZTEC_DATA_10;
    public static $AZTEC_DATA_6;
    public static $AZTEC_PARAM;
    public static $QR_CODE_FIELD_256;
    public static $DATA_MATRIX_FIELD_256;
    public static $AZTEC_DATA_8;
    public static $MAXICODE_FIELD_64;
    private array $expTable = [];
    private array $logTable = [];
    private readonly \WP2FA_Vendor\Zxing\Common\Reedsolomon\GenericGFPoly $zero;
    private readonly \WP2FA_Vendor\Zxing\Common\Reedsolomon\GenericGFPoly $one;
    /**
    * Create a representation of GF(size) using the given primitive polynomial.
    *
    * @param irreducible $primitive polynomial whose coefficients are represented by
    *                  the bits of an int, where the least-significant bit represents the constant
    *                  coefficient
    * @param the      $size size of the field
     * @param the $generatorBase factor b in the generator polynomial can be 0- or 1-based
                     (g(x) = (x+a^b)(x+a^(b+1))...(x+a^(b+2t-1))).
                     In most cases it should be 1, but for QR code it is 0.
    */
    public function __construct(private $primitive, private $size, private $generatorBase)
    {
        $x = 1;
        for ($i = 0; $i < $size; $i++) {
            $this->expTable[$i] = $x;
            $x *= 2;
            // we're assuming the generator alpha is 2
            if ($x >= $size) {
                $x ^= $primitive;
                $x &= $size - 1;
            }
        }
        for ($i = 0; $i < $size - 1; $i++) {
            $this->logTable[$this->expTable[$i]] = $i;
        }
        // logTable[0] == 0 but this should never be used
        $this->zero = new GenericGFPoly($this, [0]);
        $this->one = new GenericGFPoly($this, [1]);
    }
    public static function Init() : void
    {
        self::$AZTEC_DATA_12 = new GenericGF(0x1069, 4096, 1);
        // x^12 + x^6 + x^5 + x^3 + 1
        self::$AZTEC_DATA_10 = new GenericGF(0x409, 1024, 1);
        // x^10 + x^3 + 1
        self::$AZTEC_DATA_6 = new GenericGF(0x43, 64, 1);
        // x^6 + x + 1
        self::$AZTEC_PARAM = new GenericGF(0x13, 16, 1);
        // x^4 + x + 1
        self::$QR_CODE_FIELD_256 = new GenericGF(0x11d, 256, 0);
        // x^8 + x^4 + x^3 + x^2 + 1
        self::$DATA_MATRIX_FIELD_256 = new GenericGF(0x12d, 256, 1);
        // x^8 + x^5 + x^3 + x^2 + 1
        self::$AZTEC_DATA_8 = self::$DATA_MATRIX_FIELD_256;
        self::$MAXICODE_FIELD_64 = self::$AZTEC_DATA_6;
    }
    /**
     * Implements both addition and subtraction -- they are the same in GF(size).
     *
     * @return sum/difference of a and b
     */
    public static function addOrSubtract($a, $b)
    {
        return $a ^ $b;
    }
    public function getZero()
    {
        return $this->zero;
    }
    public function getOne()
    {
        return $this->one;
    }
    /**
     * @return GenericGFPoly  the monomial representing coefficient * x^degree
     */
    public function buildMonomial($degree, $coefficient)
    {
        if ($degree < 0) {
            throw new \InvalidArgumentException();
        }
        if ($coefficient == 0) {
            return $this->zero;
        }
        $coefficients = fill_array(0, $degree + 1, 0);
        //new int[degree + 1];
        $coefficients[0] = $coefficient;
        return new GenericGFPoly($this, $coefficients);
    }
    /**
     * @return 2 to the power of a in GF(size)
     */
    public function exp($a)
    {
        return $this->expTable[$a];
    }
    /**
     * @return base 2 log of a in GF(size)
     */
    public function log($a)
    {
        if ($a == 0) {
            throw new \InvalidArgumentException();
        }
        return $this->logTable[$a];
    }
    /**
     * @return multiplicative inverse of a
     */
    public function inverse($a)
    {
        if ($a == 0) {
            throw new \Exception();
        }
        return $this->expTable[$this->size - $this->logTable[$a] - 1];
    }
    /**
     * @return int product of a and b in GF(size)
     */
    public function multiply($a, $b)
    {
        if ($a == 0 || $b == 0) {
            return 0;
        }
        return $this->expTable[($this->logTable[$a] + $this->logTable[$b]) % ($this->size - 1)];
    }
    public function getSize()
    {
        return $this->size;
    }
    public function getGeneratorBase()
    {
        return $this->generatorBase;
    }
    // @Override
    public function toString()
    {
        return "GF(0x" . \dechex((int) $this->primitive) . ',' . $this->size . ')';
    }
}
GenericGF::Init();
vendor/khanamiryan/qrcode-detector-decoder/lib/Common/Reedsolomon/ReedSolomonException.php000064400000001527150755130600026143 0ustar00<?php

/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace WP2FA_Vendor\Zxing\Common\Reedsolomon;

/**
 * <p>Thrown when an exception occurs during Reed-Solomon decoding, such as when
 * there are too many errors to correct.</p>
 *
 * @author Sean Owen
 */
final class ReedSolomonException extends \Exception
{
}
vendor/khanamiryan/qrcode-detector-decoder/lib/Common/Reedsolomon/GenericGFPoly.php000064400000024177150755130600024501 0ustar00<?php

/*
 * Copyright 2007 ZXing authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
namespace WP2FA_Vendor\Zxing\Common\Reedsolomon;

/**
 * <p>Represents a polynomial whose coefficients are elements of a GF.
 * Instances of this class are immutable.</p>
 *
 * <p>Much credit is due to William Rucklidge since portions of this code are an indirect
 * port of his C++ Reed-Solomon implementation.</p>
 *
 * @author Sean Owen
 */
final class GenericGFPoly
{
    /**
     * @var int[]|mixed|null
     */
    private $coefficients;
    /**
     * @param the        $field {@link GenericGF} instance representing the field to use
     * to perform computations
     * @param array $coefficients coefficients as ints representing elements of GF(size), arranged
     *                     from most significant (highest-power term) coefficient to least significant
     *
     * @throws InvalidArgumentException if argument is null or empty,
     * or if leading coefficient is 0 and this is not a
     * constant polynomial (that is, it is not the monomial "0")
     */
    public function __construct(private $field, $coefficients)
    {
        if (\count($coefficients) == 0) {
            throw new \InvalidArgumentException();
        }
        $coefficientsLength = \count($coefficients);
        if ($coefficientsLength > 1 && $coefficients[0] == 0) {
            // Leading term must be non-zero for anything except the constant polynomial "0"
            $firstNonZero = 1;
            while ($firstNonZero < $coefficientsLength && $coefficients[$firstNonZero] == 0) {
                $firstNonZero++;
            }
            if ($firstNonZero == $coefficientsLength) {
                $this->coefficients = [0];
            } else {
                $this->coefficients = fill_array(0, $coefficientsLength - $firstNonZero, 0);
                $this->coefficients = arraycopy($coefficients, $firstNonZero, $this->coefficients, 0, \is_countable($this->coefficients) ? \count($this->coefficients) : 0);
            }
        } else {
            $this->coefficients = $coefficients;
        }
    }
    public function getCoefficients()
    {
        return $this->coefficients;
    }
    /**
     * @return evaluation of this polynomial at a given point
     */
    public function evaluateAt($a)
    {
        if ($a == 0) {
            // Just return the x^0 coefficient
            return $this->getCoefficient(0);
        }
        $size = \is_countable($this->coefficients) ? \count($this->coefficients) : 0;
        if ($a == 1) {
            // Just the sum of the coefficients
            $result = 0;
            foreach ($this->coefficients as $coefficient) {
                $result = GenericGF::addOrSubtract($result, $coefficient);
            }
            return $result;
        }
        $result = $this->coefficients[0];
        for ($i = 1; $i < $size; $i++) {
            $result = GenericGF::addOrSubtract($this->field->multiply($a, $result), $this->coefficients[$i]);
        }
        return $result;
    }
    /**
     * @return coefficient of x^degree term in this polynomial
     */
    public function getCoefficient($degree)
    {
        return $this->coefficients[(\is_countable($this->coefficients) ? \count($this->coefficients) : 0) - 1 - $degree];
    }
    public function multiply($other)
    {
        $aCoefficients = [];
        $bCoefficients = [];
        $aLength = null;
        $bLength = null;
        $product = [];
        if (\is_int($other)) {
            return $this->multiply_($other);
        }
        if ($this->field !== $other->field) {
            throw new \InvalidArgumentException("GenericGFPolys do not have same GenericGF field");
        }
        if ($this->isZero() || $other->isZero()) {
            return $this->field->getZero();
        }
        $aCoefficients = $this->coefficients;
        $aLength = \count($aCoefficients);
        $bCoefficients = $other->coefficients;
        $bLength = \count($bCoefficients);
        $product = fill_array(0, $aLength + $bLength - 1, 0);
        for ($i = 0; $i < $aLength; $i++) {
            $aCoeff = $aCoefficients[$i];
            for ($j = 0; $j < $bLength; $j++) {
                $product[$i + $j] = GenericGF::addOrSubtract($product[$i + $j], $this->field->multiply($aCoeff, $bCoefficients[$j]));
            }
        }
        return new GenericGFPoly($this->field, $product);
    }
    public function multiply_($scalar)
    {
        if ($scalar == 0) {
            return $this->field->getZero();
        }
        if ($scalar == 1) {
            return $this;
        }
        $size = \is_countable($this->coefficients) ? \count($this->coefficients) : 0;
        $product = fill_array(0, $size, 0);
        for ($i = 0; $i < $size; $i++) {
            $product[$i] = $this->field->multiply($this->coefficients[$i], $scalar);
        }
        return new GenericGFPoly($this->field, $product);
    }
    /**
     * @return true iff this polynomial is the monomial "0"
     */
    public function isZero()
    {
        return $this->coefficients[0] == 0;
    }
    public function multiplyByMonomial($degree, $coefficient)
    {
        if ($degree < 0) {
            throw new \InvalidArgumentException();
        }
        if ($coefficient == 0) {
            return $this->field->getZero();
        }
        $size = \is_countable($this->coefficients) ? \count($this->coefficients) : 0;
        $product = fill_array(0, $size + $degree, 0);
        for ($i = 0; $i < $size; $i++) {
            $product[$i] = $this->field->multiply($this->coefficients[$i], $coefficient);
        }
        return new GenericGFPoly($this->field, $product);
    }
    public function divide($other)
    {
        if ($this->field !== $other->field) {
            throw new \InvalidArgumentException("GenericGFPolys do not have same GenericGF field");
        }
        if ($other->isZero()) {
            throw new \InvalidArgumentException("Divide by 0");
        }
        $quotient = $this->field->getZero();
        $remainder = $this;
        $denominatorLeadingTerm = $other->getCoefficient($other->getDegree());
        $inverseDenominatorLeadingTerm = $this->field->inverse($denominatorLeadingTerm);
        while ($remainder->getDegree() >= $other->getDegree() && !$remainder->isZero()) {
            $degreeDifference = $remainder->getDegree() - $other->getDegree();
            $scale = $this->field->multiply($remainder->getCoefficient($remainder->getDegree()), $inverseDenominatorLeadingTerm);
            $term = $other->multiplyByMonomial($degreeDifference, $scale);
            $iterationQuotient = $this->field->buildMonomial($degreeDifference, $scale);
            $quotient = $quotient->addOrSubtract($iterationQuotient);
            $remainder = $remainder->addOrSubtract($term);
        }
        return [$quotient, $remainder];
    }
    /**
     * @return degree of this polynomial
     */
    public function getDegree()
    {
        return (\is_countable($this->coefficients) ? \count($this->coefficients) : 0) - 1;
    }
    public function addOrSubtract($other)
    {
        $smallerCoefficients = [];
        $largerCoefficients = [];
        $sumDiff = [];
        $lengthDiff = null;
        $countLargerCoefficients = null;
        if ($this->field !== $other->field) {
            throw new \InvalidArgumentException("GenericGFPolys do not have same GenericGF field");
        }
        if ($this->isZero()) {
            return $other;
        }
        if ($other->isZero()) {
            return $this;
        }
        $smallerCoefficients = $this->coefficients;
        $largerCoefficients = $other->coefficients;
        if (\count($smallerCoefficients) > \count($largerCoefficients)) {
            $temp = $smallerCoefficients;
            $smallerCoefficients = $largerCoefficients;
            $largerCoefficients = $temp;
        }
        $sumDiff = fill_array(0, \count($largerCoefficients), 0);
        $lengthDiff = \count($largerCoefficients) - \count($smallerCoefficients);
        // Copy high-order terms only found in higher-degree polynomial's coefficients
        $sumDiff = arraycopy($largerCoefficients, 0, $sumDiff, 0, $lengthDiff);
        $countLargerCoefficients = \count($largerCoefficients);
        for ($i = $lengthDiff; $i < $countLargerCoefficients; $i++) {
            $sumDiff[$i] = GenericGF::addOrSubtract($smallerCoefficients[$i - $lengthDiff], $largerCoefficients[$i]);
        }
        return new GenericGFPoly($this->field, $sumDiff);
    }
    //@Override
    public function toString()
    {
        $result = '';
        for ($degree = $this->getDegree(); $degree >= 0; $degree--) {
            $coefficient = $this->getCoefficient($degree);
            if ($coefficient != 0) {
                if ($coefficient < 0) {
                    $result .= " - ";
                    $coefficient = -$coefficient;
                } else {
                    if (\strlen((string) $result) > 0) {
                        $result .= " + ";
                    }
                }
                if ($degree == 0 || $coefficient != 1) {
                    $alphaPower = $this->field->log($coefficient);
                    if ($alphaPower == 0) {
                        $result .= '1';
                    } elseif ($alphaPower == 1) {
                        $result .= 'a';
                    } else {
                        $result .= "a^";
                        $result .= $alphaPower;
                    }
                }
                if ($degree != 0) {
                    if ($degree == 1) {
                        $result .= 'x';
                    } else {
                        $result .= "x^";
                        $result .= $degree;
                    }
                }
            }
        }
        return $result;
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/Common/CharacterSetECI.php000064400000007667150755130600022454 0ustar00<?php

namespace WP2FA_Vendor\Zxing\Common;

/**
 * Encapsulates a Character Set ECI, according to "Extended Channel
 * Interpretations" 5.3.1.1 of ISO 18004.
 */
final class CharacterSetECI
{
    /**#@+
     * Character set constants.
     */
    public const CP437 = 0;
    public const ISO8859_1 = 1;
    public const ISO8859_2 = 4;
    public const ISO8859_3 = 5;
    public const ISO8859_4 = 6;
    public const ISO8859_5 = 7;
    public const ISO8859_6 = 8;
    public const ISO8859_7 = 9;
    public const ISO8859_8 = 10;
    public const ISO8859_9 = 11;
    public const ISO8859_10 = 12;
    public const ISO8859_11 = 13;
    public const ISO8859_12 = 14;
    public const ISO8859_13 = 15;
    public const ISO8859_14 = 16;
    public const ISO8859_15 = 17;
    public const ISO8859_16 = 18;
    public const SJIS = 20;
    public const CP1250 = 21;
    public const CP1251 = 22;
    public const CP1252 = 23;
    public const CP1256 = 24;
    public const UNICODE_BIG_UNMARKED = 25;
    public const UTF8 = 26;
    public const ASCII = 27;
    public const BIG5 = 28;
    public const GB18030 = 29;
    public const EUC_KR = 30;
    /**
     * Map between character names and their ECI values.
     */
    private static array $nameToEci = ['ISO-8859-1' => self::ISO8859_1, 'ISO-8859-2' => self::ISO8859_2, 'ISO-8859-3' => self::ISO8859_3, 'ISO-8859-4' => self::ISO8859_4, 'ISO-8859-5' => self::ISO8859_5, 'ISO-8859-6' => self::ISO8859_6, 'ISO-8859-7' => self::ISO8859_7, 'ISO-8859-8' => self::ISO8859_8, 'ISO-8859-9' => self::ISO8859_9, 'ISO-8859-10' => self::ISO8859_10, 'ISO-8859-11' => self::ISO8859_11, 'ISO-8859-12' => self::ISO8859_12, 'ISO-8859-13' => self::ISO8859_13, 'ISO-8859-14' => self::ISO8859_14, 'ISO-8859-15' => self::ISO8859_15, 'ISO-8859-16' => self::ISO8859_16, 'SHIFT-JIS' => self::SJIS, 'WINDOWS-1250' => self::CP1250, 'WINDOWS-1251' => self::CP1251, 'WINDOWS-1252' => self::CP1252, 'WINDOWS-1256' => self::CP1256, 'UTF-16BE' => self::UNICODE_BIG_UNMARKED, 'UTF-8' => self::UTF8, 'ASCII' => self::ASCII, 'GBK' => self::GB18030, 'EUC-KR' => self::EUC_KR];
    /**#@-*/
    /**
     * Additional possible values for character sets.
     */
    private static array $additionalValues = [self::CP437 => 2, self::ASCII => 170];
    private static int|string|null $name = null;
    /**
     * Gets character set ECI by value.
     *
     * @param  string $value
     *
     * @return CharacterSetEci|null
     */
    public static function getCharacterSetECIByValue($value)
    {
        if ($value < 0 || $value >= 900) {
            throw new \InvalidArgumentException('Value must be between 0 and 900');
        }
        if (\false !== ($key = \array_search($value, self::$additionalValues))) {
            $value = $key;
        }
        \array_search($value, self::$nameToEci);
        try {
            self::setName($value);
            return new self($value);
        } catch (\UnexpectedValueException) {
            return null;
        }
    }
    private static function setName($value)
    {
        foreach (self::$nameToEci as $name => $key) {
            if ($key == $value) {
                self::$name = $name;
                return \true;
            }
        }
        if (self::$name == null) {
            foreach (self::$additionalValues as $name => $key) {
                if ($key == $value) {
                    self::$name = $name;
                    return \true;
                }
            }
        }
    }
    /**
     * Gets character set ECI name.
     *
     * @return character set ECI name|null
     */
    public static function name()
    {
        return self::$name;
    }
    /**
     * Gets character set ECI by name.
     *
     * @param  string $name
     *
     * @return CharacterSetEci|null
     */
    public static function getCharacterSetECIByName($name)
    {
        $name = \strtoupper($name);
        if (isset(self::$nameToEci[$name])) {
            return new self(self::$nameToEci[$name]);
        }
        return null;
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/Common/DefaultGridSampler.php000064400000006431150755130600023265 0ustar00<?php

/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace WP2FA_Vendor\Zxing\Common;

use WP2FA_Vendor\Zxing\NotFoundException;
/**
 * @author Sean Owen
 */
final class DefaultGridSampler extends GridSampler
{
    //@Override
    public function sampleGrid($image, $dimensionX, $dimensionY, $p1ToX, $p1ToY, $p2ToX, $p2ToY, $p3ToX, $p3ToY, $p4ToX, $p4ToY, $p1FromX, $p1FromY, $p2FromX, $p2FromY, $p3FromX, $p3FromY, $p4FromX, $p4FromY)
    {
        $transform = PerspectiveTransform::quadrilateralToQuadrilateral($p1ToX, $p1ToY, $p2ToX, $p2ToY, $p3ToX, $p3ToY, $p4ToX, $p4ToY, $p1FromX, $p1FromY, $p2FromX, $p2FromY, $p3FromX, $p3FromY, $p4FromX, $p4FromY);
        return $this->sampleGrid_($image, $dimensionX, $dimensionY, $transform);
    }
    //@Override
    public function sampleGrid_($image, $dimensionX, $dimensionY, $transform)
    {
        if ($dimensionX <= 0 || $dimensionY <= 0) {
            throw NotFoundException::getNotFoundInstance();
        }
        $bits = new BitMatrix($dimensionX, $dimensionY);
        $points = fill_array(0, 2 * $dimensionX, 0.0);
        for ($y = 0; $y < $dimensionY; $y++) {
            $max = \is_countable($points) ? \count($points) : 0;
            $iValue = (float) $y + 0.5;
            for ($x = 0; $x < $max; $x += 2) {
                $points[$x] = (float) ($x / 2) + 0.5;
                $points[$x + 1] = $iValue;
            }
            $transform->transformPoints($points);
            // Quick check to see if points transformed to something inside the image;
            // sufficient to check the endpoints
            self::checkAndNudgePoints($image, $points);
            try {
                for ($x = 0; $x < $max; $x += 2) {
                    if ($image->get((int) $points[$x], (int) $points[$x + 1])) {
                        // Black(-ish) pixel
                        $bits->set($x / 2, $y);
                    }
                }
            } catch (\Exception) {
                //ArrayIndexOutOfBoundsException
                // This feels wrong, but, sometimes if the finder patterns are misidentified, the resulting
                // transform gets "twisted" such that it maps a straight line of points to a set of points
                // whose endpoints are in bounds, but others are not. There is probably some mathematical
                // way to detect this about the transformation that I don't know yet.
                // This results in an ugly runtime exception despite our clever checks above -- can't have
                // that. We could check each point's coordinates but that feels duplicative. We settle for
                // catching and wrapping ArrayIndexOutOfBoundsException.
                throw NotFoundException::getNotFoundInstance();
            }
        }
        return $bits;
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/Common/Detector/MathUtils.php000064400000002666150755130600023240 0ustar00<?php

/*
* Copyright 2012 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace WP2FA_Vendor\Zxing\Common\Detector;

final class MathUtils
{
    private function __construct()
    {
    }
    /**
     * Ends up being a bit faster than {@link Math#round(float)}. This merely rounds its
     * argument to the nearest int, where x.5 rounds up to x+1. Semantics of this shortcut
     * differ slightly from {@link Math#round(float)} in that half rounds down for negative
     * values. -2.5 rounds to -3, not -2. For purposes here it makes no difference.
     *
     * @param float $d real value to round
     *
     * @return int {@code int}
     */
    public static function round($d)
    {
        return (int) ($d + ($d < 0.0 ? -0.5 : 0.5));
    }
    public static function distance($aX, $aY, $bX, $bY)
    {
        $xDiff = $aX - $bX;
        $yDiff = $aY - $bY;
        return (float) \sqrt($xDiff * $xDiff + $yDiff * $yDiff);
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/Common/Detector/MonochromeRectangleDetector.php000064400000021273150755130600026746 0ustar00<?php

/**
 * Created by PhpStorm.
 * User: Ashot
 * Date: 3/24/15
 * Time: 21:23
 */
namespace WP2FA_Vendor\Zxing\Common\Detector;

use WP2FA_Vendor\Zxing\BinaryBitmap;
use WP2FA_Vendor\Zxing\NotFoundException;
use WP2FA_Vendor\Zxing\ResultPoint;
/*
 *
 *
import com.google.zxing.NotFoundException;
import com.google.zxing.ResultPoint;
import com.google.zxing.common.BitMatrix;
*/
//require_once('./lib/NotFoundException.php');
//require_once('./lib/ResultPoint.php');
//require_once('./lib/common/BitMatrix.php');
/**
 * <p>A somewhat generic detector that looks for a barcode-like rectangular region within an image.
 * It looks within a mostly white region of an image for a region of black and white, but mostly
 * black. It returns the four corners of the region, as best it can determine.</p>
 *
 * @author Sean Owen
 * @port   Ashot Khanamiryan
 */
class MonochromeRectangleDetector
{
    private static int $MAX_MODULES = 32;
    public function __construct(private readonly BinaryBitmap $image)
    {
    }
    /**
     * <p>Detects a rectangular region of black and white -- mostly black -- with a region of mostly
     * white, in an image.</p>
     *
     * @return {@link ResultPoint}[] describing the corners of the rectangular region. The first and
     *  last points are opposed on the diagonal, as are the second and third. The first point will be
     *  the topmost point and the last, the bottommost. The second point will be leftmost and the
     *  third, the rightmost
     * @throws NotFoundException if no Data Matrix Code can be found
     */
    public function detect() : \WP2FA_Vendor\Zxing\ResultPoint
    {
        $height = $this->image->getHeight();
        $width = $this->image->getWidth();
        $halfHeight = $height / 2;
        $halfWidth = $width / 2;
        $deltaY = \max(1, $height / (self::$MAX_MODULES * 8));
        $deltaX = \max(1, $width / (self::$MAX_MODULES * 8));
        $top = 0;
        $bottom = $height;
        $left = 0;
        $right = $width;
        $pointA = $this->findCornerFromCenter($halfWidth, 0, $left, $right, $halfHeight, -$deltaY, $top, $bottom, $halfWidth / 2);
        $top = (int) $pointA->getY() - 1;
        $pointB = $this->findCornerFromCenter($halfWidth, -$deltaX, $left, $right, $halfHeight, 0, $top, $bottom, $halfHeight / 2);
        $left = (int) $pointB->getX() - 1;
        $pointC = $this->findCornerFromCenter($halfWidth, $deltaX, $left, $right, $halfHeight, 0, $top, $bottom, $halfHeight / 2);
        $right = (int) $pointC->getX() + 1;
        $pointD = $this->findCornerFromCenter($halfWidth, 0, $left, $right, $halfHeight, $deltaY, $top, $bottom, $halfWidth / 2);
        $bottom = (int) $pointD->getY() + 1;
        // Go try to find po$A again with better information -- might have been off at first.
        $pointA = $this->findCornerFromCenter($halfWidth, 0, $left, $right, $halfHeight, -$deltaY, $top, $bottom, $halfWidth / 4);
        return new ResultPoint($pointA, $pointB, $pointC, $pointD);
    }
    /**
     * Attempts to locate a corner of the barcode by scanning up, down, left or right from a center
     * point which should be within the barcode.
     *
     * @param float $centerX     center's x component (horizontal)
     * @param float $deltaX      same as deltaY but change in x per step instead
     * @param float $left        minimum value of x
     * @param float $right       maximum value of x
     * @param float $centerY     center's y component (vertical)
     * @param float $deltaY      change in y per step. If scanning up this is negative; down, positive;
     *                    left or right, 0
     * @param float $top         minimum value of y to search through (meaningless when di == 0)
     * @param float $bottom      maximum value of y
     * @param float $maxWhiteRun maximum run of white pixels that can still be considered to be within
     *                    the barcode
     *
     * @return ResultPoint {@link com.google.zxing.ResultPoint} encapsulating the corner that was found
     * @throws NotFoundException if such a point cannot be found
     */
    private function findCornerFromCenter($centerX, $deltaX, $left, $right, $centerY, $deltaY, $top, $bottom, $maxWhiteRun) : \WP2FA_Vendor\Zxing\ResultPoint
    {
        $lastRange = null;
        for ($y = $centerY, $x = $centerX; $y < $bottom && $y >= $top && $x < $right && $x >= $left; $y += $deltaY, $x += $deltaX) {
            $range = 0;
            if ($deltaX == 0) {
                // horizontal slices, up and down
                $range = $this->blackWhiteRange($y, $maxWhiteRun, $left, $right, \true);
            } else {
                // vertical slices, left and right
                $range = $this->blackWhiteRange($x, $maxWhiteRun, $top, $bottom, \false);
            }
            if ($range == null) {
                if ($lastRange == null) {
                    throw NotFoundException::getNotFoundInstance();
                }
                // lastRange was found
                if ($deltaX == 0) {
                    $lastY = $y - $deltaY;
                    if ($lastRange[0] < $centerX) {
                        if ($lastRange[1] > $centerX) {
                            // straddle, choose one or the other based on direction
                            return new ResultPoint($deltaY > 0 ? $lastRange[0] : $lastRange[1], $lastY);
                        }
                        return new ResultPoint($lastRange[0], $lastY);
                    } else {
                        return new ResultPoint($lastRange[1], $lastY);
                    }
                } else {
                    $lastX = $x - $deltaX;
                    if ($lastRange[0] < $centerY) {
                        if ($lastRange[1] > $centerY) {
                            return new ResultPoint($lastX, $deltaX < 0 ? $lastRange[0] : $lastRange[1]);
                        }
                        return new ResultPoint($lastX, $lastRange[0]);
                    } else {
                        return new ResultPoint($lastX, $lastRange[1]);
                    }
                }
            }
            $lastRange = $range;
        }
        throw NotFoundException::getNotFoundInstance();
    }
    /**
     * Computes the start and end of a region of pixels, either horizontally or vertically, that could
     * be part of a Data Matrix barcode.
     *
     * @param if $fixedDimension scanning horizontally, this is the row (the fixed vertical location)
     *                       where we are scanning. If scanning vertically it's the column, the fixed horizontal location
     * @param largest    $maxWhiteRun run of white pixels that can still be considered part of the
     *                       barcode region
     * @param minimum         $minDim pixel location, horizontally or vertically, to consider
     * @param maximum         $maxDim pixel location, horizontally or vertically, to consider
     * @param if     $horizontal true, we're scanning left-right, instead of up-down
     *
     * @return int[] with start and end of found range, or null if no such range is found
     *  (e.g. only white was found)
     */
    private function blackWhiteRange($fixedDimension, $maxWhiteRun, $minDim, $maxDim, $horizontal)
    {
        $center = ($minDim + $maxDim) / 2;
        // Scan left/up first
        $start = $center;
        while ($start >= $minDim) {
            if ($horizontal ? $this->image->get($start, $fixedDimension) : $this->image->get($fixedDimension, $start)) {
                $start--;
            } else {
                $whiteRunStart = $start;
                do {
                    $start--;
                } while ($start >= $minDim && !($horizontal ? $this->image->get($start, $fixedDimension) : $this->image->get($fixedDimension, $start)));
                $whiteRunSize = $whiteRunStart - $start;
                if ($start < $minDim || $whiteRunSize > $maxWhiteRun) {
                    $start = $whiteRunStart;
                    break;
                }
            }
        }
        $start++;
        // Then try right/down
        $end = $center;
        while ($end < $maxDim) {
            if ($horizontal ? $this->image->get($end, $fixedDimension) : $this->image->get($fixedDimension, $end)) {
                $end++;
            } else {
                $whiteRunStart = $end;
                do {
                    $end++;
                } while ($end < $maxDim && !($horizontal ? $this->image->get($end, $fixedDimension) : $this->image->get($fixedDimension, $end)));
                $whiteRunSize = $end - $whiteRunStart;
                if ($end >= $maxDim || $whiteRunSize > $maxWhiteRun) {
                    $end = $whiteRunStart;
                    break;
                }
            }
        }
        $end--;
        return $end > $start ? [$start, $end] : null;
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/Common/BitMatrix.php000064400000071061150755130600021453 0ustar00<?php

namespace WP2FA_Vendor\Zxing\Common;

final class BitMatrix
{
    private $width;
    private $height;
    private $rowSize;
    /**
     * @var mixed|int[]
     */
    private $bits;
    public function __construct($width, $height = \false, $rowSize = \false, $bits = \false)
    {
        if (!$height) {
            $height = $width;
        }
        if (!$rowSize) {
            $rowSize = (int) (($width + 31) / 32);
        }
        if (!$bits) {
            $bits = fill_array(0, $rowSize * $height, 0);
            //            [];//new int[rowSize * height];
        }
        $this->width = $width;
        $this->height = $height;
        $this->rowSize = $rowSize;
        $this->bits = $bits;
    }
    public static function parse($stringRepresentation, $setString, $unsetString)
    {
        if (!$stringRepresentation) {
            throw new \InvalidArgumentException();
        }
        $bits = [];
        $bitsPos = 0;
        $rowStartPos = 0;
        $rowLength = -1;
        $nRows = 0;
        $pos = 0;
        while ($pos < \strlen((string) $stringRepresentation)) {
            if ($stringRepresentation[$pos] == '\\n' || $stringRepresentation->{$pos} == '\\r') {
                if ($bitsPos > $rowStartPos) {
                    if ($rowLength == -1) {
                        $rowLength = $bitsPos - $rowStartPos;
                    } elseif ($bitsPos - $rowStartPos != $rowLength) {
                        throw new \InvalidArgumentException("row lengths do not match");
                    }
                    $rowStartPos = $bitsPos;
                    $nRows++;
                }
                $pos++;
            } elseif (\substr((string) $stringRepresentation, $pos, \strlen((string) $setString)) == $setString) {
                $pos += \strlen((string) $setString);
                $bits[$bitsPos] = \true;
                $bitsPos++;
            } elseif (\substr((string) $stringRepresentation, $pos + \strlen((string) $unsetString)) == $unsetString) {
                $pos += \strlen((string) $unsetString);
                $bits[$bitsPos] = \false;
                $bitsPos++;
            } else {
                throw new \InvalidArgumentException("illegal character encountered: " . \substr((string) $stringRepresentation, $pos));
            }
        }
        // no EOL at end?
        if ($bitsPos > $rowStartPos) {
            if ($rowLength == -1) {
                $rowLength = $bitsPos - $rowStartPos;
            } elseif ($bitsPos - $rowStartPos != $rowLength) {
                throw new \InvalidArgumentException("row lengths do not match");
            }
            $nRows++;
        }
        $matrix = new BitMatrix($rowLength, $nRows);
        for ($i = 0; $i < $bitsPos; $i++) {
            if ($bits[$i]) {
                $matrix->set($i % $rowLength, $i / $rowLength);
            }
        }
        return $matrix;
    }
    /**
     * <p>Sets the given bit to true.</p>
     *
     * @param $x ;  The horizontal component (i.e. which column)
     * @param $y ;   The vertical component (i.e. which row)
     */
    public function set($x, $y) : void
    {
        $offset = (int) ($y * $this->rowSize + $x / 32);
        if (!isset($this->bits[$offset])) {
            $this->bits[$offset] = 0;
        }
        //$this->bits[$offset] = $this->bits[$offset];
        //  if($this->bits[$offset]>200748364){
        //$this->bits= array(0,0,-16777216,-1,-1,-1,-1,65535,0,0,0,0,0,0,0,-16777216,-1,-1,-1,-1,65535,0,0,0,0,0,0,0,-16777216,-1,-1,-1,-1,65535,0,0,0,0,0,0,0,-16777216,-1,-1,-1,-1,65535,0,0,0,0,0,0,0,-16777216,-1,-1,-1,-1,65535,0,0,0,0,0,0,0,-16777216,-1,-1,-1,-1,65535,0,0,0,0,0,0,0,-16777216,-1,-1,-1,-1,65535,0,0,0,0,0,0,0,-16777216,-1,-1,-1,-1,65535,0,0,0,0,0,0,0,-16777216,-1,-1,-1,-1,65535,0,0,0,0,0,0,0,-16777216,-1,-1,-1,-1,65535,0,0,0,0,0,0,0,-16777216,-1,-1,-1,-1,65535,0,0,0,0,0,0,0,-1090519040,-1,-1,-1,-1,65535,0,0,0,0,0,0,0,1056964608,-1,-1,-1,-1,65535,0,0,0,0,0,0,0,-1358954496,-1,-1,-1,-1,65535,0,0,0,0,0,0,0,117440512,-1,-1,-1,-1,65535,0,0,0,0,0,0,0,50331648,-1,-1,-1,-1,65535,0,0,0,0,0,0,0,33554432,-1,-1,536870911,-4096,65279,0,0,0,0,0,0,0,0,-1,-1,65535,-4096,65535,0,0,0,0,0,0,0,0,-193,536870911,0,-4096,65279,0,0,0,0,0,0,0,0,-254,32767,0,-4096,61951,0,0,0,0,0,0,0,0,20913920,0,0,-4096,50175,0,0,0,0,0,0,0,0,0,0,0,-4096,60159,0,0,0,0,0,0,0,0,0,0,0,-4096,64255,0,0,0,0,0,0,0,0,0,0,0,-8192,56319,0,0,0,0,0,0,0,0,0,0,0,-4096,16777215,0,0,0,0,0,0,0,0,0,0,0,-4096,16777215,0,0,0,0,0,0,0,0,0,0,0,-4096,16777215,0,0,0,0,0,0,0,0,0,0,0,-4096,16777215,0,0,0,0,0,0,0,0,0,0,0,-4096,16777215,0,0,0,0,0,0,0,0,0,0,0,-4096,16777215,0,0,0,0,0,0,0,0,0,0,0,-4096,16777215,0,0,0,0,0,0,0,0,0,0,0,-4096,16777215,0,0,0,0,0,0,0,251658240,0,0,0,-4096,-1,255,0,256,0,0,0,0,117440512,0,0,0,-4096,-1,255,0,512,0,0,0,0,117440512,0,0,0,-4096,-1,255,0,1024,0,0,0,0,117440512,0,0,0,-4096,-1,223,0,256,0,0,0,0,117440512,0,0,33030144,-4096,-1,191,0,256,0,0,0,0,117440512,0,0,33554428,-4096,-1,255,0,768,0,0,0,0,117440512,0,402849792,67108862,-8192,-1,255,0,768,0,0,0,0,117440512,0,470278396,63045630,-8192,-1,255,0,256,0,0,0,0,251658240,-8388608,470278399,58720286,-8192,-1,2686975,0,3842,0,0,0,0,251658240,-131072,1007149567,58720286,-8192,-1,2031615,0,3879,0,0,0,0,251658240,536739840,1007092192,58720286,-8192,-1,851967,0,3840,0,0,0,0,251658240,917504,1007092192,58720284,-8192,-1,2031615,0,3968,0,0,0,0,251658240,917504,1007092160,59244060,-8192,-1,65535,0,7936,0,0,0,0,251658240,917504,1009779136,59244060,-8192,-1,9371647,0,1792,0,0,0,0,251658240,917504,946921920,59244060,-8192,-1,8585215,0,1792,0,0,0,0,117440512,-15859712,477159875,59244060,-8192,-1,65535,0,12032,0,0,0,0,251658240,-15859712,52490691,59244060,-8192,-1,-1,0,65408,0,0,0,0,251658240,-15859712,58778051,59244060,-8192,-1,-1,0,65473,0,0,0,0,251658240,-15859712,125886915,59244060,-8192,-1,-1,0,65472,0,0,0,0,251658240,-15859712,58778051,59244060,-8192,-1,-1,0,65408,0,0,0,0,251658240,-15859712,8380867,59244060,-8192,-1,-1,0,65473,0,0,0,0,251658240,-15859712,8380867,59244060,-8192,-1,-1,0,131011,0,0,0,0,251658240,-15859712,8380867,58720284,-8192,-1,-1,0,130947,0,0,0,0,251658240,-15859712,2089411,58720284,-8192,-1,-1,0,130947,0,0,0,0,251658240,-32636928,449,58720284,-8192,-1,-1,33554431,131015,0,0,0,0,251658240,786432,448,62914588,-8192,-1,-1,16777215,131015,0,0,0,0,251658240,786432,448,67108860,-8192,-1,-1,553648127,131015,0,0,0,0,251658240,786432,946864576,67108860,-8192,-1,-1,32505855,131015,0,0,0,0,251658240,786432,946921976,8388604,-8192,-1,-1,8191999,131015,0,0,0,0,251658240,-262144,946921983,248,-8192,-1,-1,8126463,196551,0,0,0,0,251658240,-262144,7397887,0,-8192,-1,-1,16777215,262087,0,0,0,0,251658240,-262144,8257543,0,-8192,-1,-1,-2121269249,262095,0,0,0,0,520093696,0,8257536,0,-8192,-1,-1,-201326593,262095,0,0,0,0,520290304,0,8257536,117963776,-8192,-1,-1,-201326593,262095,0,0,0,0,520093696,0,-2140143616,118488579,-8192,-1,-1,-201326593,131023,0,0,0,0,520093696,0,-2131697280,118488579,-8192,-1,-1,-503316481,131023,0,0,0,0,520093696,2145386496,-2131631232,118484995,-16384,-1,-1,-469762049,262095,0,0,0,0,520093696,2147221504,552649600,118481344,-16384,-1,-1,-469762049,131023,0,0,0,0,520290304,2147221504,2029002240,118481344,-16384,-1,-1,-469762049,262031,0,0,0,0,520290304,-266600448,2029001791,125952960,-16384,-1,-1,-469762049,262031,0,0,0,0,1057423360,-266600448,2027953215,133177312,-16384,-1,-1,-134217729,262111,0,0,0,0,1058471936,-266600448,-119531393,133177343,-16384,-1,-1,-134217729,262111,0,0,0,0,1058471936,-2145648640,-253754369,66068479,-16384,-1,-1,-134217729,262111,0,0,0,0,1058471936,236716032,-253754369,15729663,-16384,-1,-1,-134217729,262095,0,0,0,0,1057947648,236716032,-253754369,6348807,-16384,-1,-1,-134217729,262095,0,0,0,0,524222464,236716032,-253690305,6348803,-16384,-1,-1,-134217729,262111,0,0,0,0,521076736,2115764224,-253625344,14737411,-16384,-1,-1,-134217729,262095,0,0,0,0,522125312,2115764224,-253625344,14743555,-16384,-1,-1,-134217729,262111,0,0,16772608,0,1073676288,-31719424,-2014283776,14810115,-16384,-1,-1,-1,262143,0,0,16776704,0,1065287680,-1642594304,-1879178880,14810115,-16384,-1,-1,-1,524287,0,0,16776192,0,2139029504,264241152,-2013396089,14809091,-16384,-1,-1,-1,262095,0,0,16776192,0,2139029504,264241152,-2080636025,14803335,-16384,-1,-1,-1,262087,0,0,16776192,0,2147418112,264241152,-2132803581,14803847,-16384,-1,-1,-402653185,524259,0,0,8386048,0,2147418112,0,-2132688896,123783,-16384,-1,-1,1207959551,262112,0,0,16775168,0,2147418112,0,14794752,1046535,-16384,-1,-1,268435455,262128,0,0,16775168,0,2147418112,0,14712832,1047615,-16384,-1,-1,536870911,524284,0,0,16776705,0,2147418112,0,14680832,1047615,-16384,-1,-1,-1,524287,0,0,16776704,0,2147418112,-1048576,14681087,1046591,-32768,-1,-1,-1,524287,0,0,16776704,0,2147418112,-524288,-2132802561,2080831,-32768,-1,-1,-1,524287,0,0,16776705,0,2147418112,-524288,-31718401,2080831,-32768,-1,-1,-1,1048575,0,0,16776193,0,2147418112,3670016,-31718528,2080831,-32768,-1,-1,-1,524287,0,0,16776195,0,2147418112,3670016,-31718528,134086719,-32768,-1,-1,-1,524287,0,0,16776195,0,2147418112,3670016,253494144,268173368,-32768,-1,-1,-1,524287,0,0,16775171,0,2147418112,3670016,268174208,268173368,-32768,-1,-1,-1,1048575,0,0,16771072,0,2147418112,-63438848,268174223,31457328,-32768,-1,-1,-1,1048575,0,0,10418176,0,-65536,-63438848,133957519,14807040,-32768,-1,-1,-1,2097151,0,0,15923200,0,2147418112,-63438848,1968015,14809095,-32768,-1,-1,-1,1048575,0,0,12808192,0,2147418112,-63438848,2082703,12711943,-32768,-1,-1,-1,2097151,0,0,6420480,0,2147418112,-63438848,2082703,14343,-32768,-1,-1,-1,2097151,0,0,15202304,0,-65536,-63438848,2082703,1849351,-32768,-1,-1,-1,2097151,0,0,15464448,0,-65536,-63438848,264472335,1849351,-32768,-1,-1,-1,4194303,0,0,16371712,0,-65536,-63438848,264472335,14343,-32768,-1,-1,-1,8388607,0,0,0,0,-65536,-63438848,532907791,235010048,-32768,-1,-1,-1,16777215,0,0,0,0,-65536,-63438848,-1603833,235010160,-32768,-1,-1,-1,16777215,0,0,0,0,-65536,3670016,-30976,67238000,-32768,-1,-1,-1,16777215,0,0,0,0,-65536,3670016,-30976,48,-32768,-1,-1,-1,16777215,0,0,0,0,-65536,3670016,-29391104,768,-32768,-1,-1,-1,16777215,0,0,0,0,-65536,3670016,-29391104,768,-32768,-1,-1,-1,16777215,0,0,0,0,-65536,-524287,-65042433,768,-32768,-1,-1,-1,16777215,0,0,0,0,-65536,-524287,2082441215,0,-65536,-1,-1,-1,16777215,0,0,0,0,-13697024,-524287,511,0,-65536,-1,-1,-1,16777215,0,0,0,0,-12648448,1,0,0,-65536,-1,-1,-1,14680063,0,0,0,0,-12648448,1,0,0,-65536,-1,-1,-1,16777215,0,0,0,0,-65536,1,0,0,-65536,-1,-1,-1,14680063,0,0,0,0,-8454144,1,0,0,-65536,-1,-1,-1,12582911,0,0,0,0,-12648448,1,0,0,-65536,-1,-1,-1,2097151,0,0,0,0,-12648448,1,0,0,-65536,-1,-1,-1,1048575,0,0,0,0,-14745600,1,0,0,-65536,-1,-1,-1,3145727,0,0,0,0,1056964608,1,0,0,-65536,-1,-1,-1,1048575,0,0,0,0,1056964608,1,0,0,-65536,-1,-1,-1,1048575,0,0,0,0,1056964608,1,0,0,-65536,-1,-1,-1,524287,0,0,0,0,2130706432,1,0,0,-65536,-1,-1,-1,1048575,0,0,0,0,1056964608,1,0,0,-65536,-1,-1,-1,524287,0,0,0,0,2130706432,1,0,0,-65536,-1,-1,-1,524287,0,0,0,0,2130706432,1,0,0,-65536,-1,-1,-1,1048575,0,0,0,0,2130706432,1,0,0,-65536,-1,-1,-1,1048575,0,0,0,0,50331648,1,0,0,-65536,-1,-1,-1,1048575,0,0,0,0,117440512,1,0,-268435456,-1,-1,-1,-1,524287,0,0,0,0,251658240,1,0,-320,-1,-1,-1,-1,262143,0,0,0,0,520093696,1,-2048,-1,-1,-1,-1,-1,262143,0,0,0,0,1056964608,-16777213,-1,-1,-1,-1,-1,-1,131071,0,0,0,0,-16777216,-121,-1,-1,-1,-1,-1,-1,131071,0,0,0,0,-16777216,-1,-1,-1,-1,-1,-1,-1,131071,0,0,0,0,-16777216,-1,-1,-1,-1,-1,-1,-1,131071,0,0,0,0,-16777216,-1,-1,-1,-1,-1,-1,-1,131071,0,0,0,0,-16777216,-1,-1,-1,-1,-1,-1,-1,65535,0,0,0,0,-16777216,-1,-1,-1,-1,-1,-1,-1,65535,0,0,0,0,-16777216,-1,-1,-1,-1,-1,-1,-1,131071,0,0,0,0,-16777216,-1,-1,-1,-1,-1,-1,-1,262143,0,0,0,0,-16777216,-1,-1,-1,-1,-1,-1,-1,524287,0,0,0,0,-16777216,-1,-1,-1,-1,-1,-1,-1,524287,0,0,0,0,-16777216,-1,-1,-1,-1,-1,-1,-1,589823,0,0,0,0,0,-1,-1,-1,-1,-1,-1,-1,8179,0,0,0,0,50331648,-1,-1,-1,-1,-1,-1,-1,4080,0,0,0,0,117440512,-1,-1,-1,-1,-1,-1,-1,1016,0,0,0,0,251658240,-1,-1,-1,-1,-1,-1,1073741823,1020,0,0,0,0,50331648,-1,-1,-1,-1,-1,-1,536870911,254,0,0,0,0,50331648,-1,-1,-1,-1,-1,-1,536870911,255,0,0,0,0,50331648,-1,-1,-1,-1,-1,-1,-1879048193,127,0,0,0,0,50331648,-1,-1,-1,-1,-1,-1,-469762049,63,0,0,0,0,1191182336,-1,-1,-1,-1,1023999,0,-520093712,15,0,0,0,0,-218103808,-1,-1,-1,-1,0,-8454144,-260046849,7,0,0,0,0,0,-193,-1,-1,-1057947649,-2147483648,-1,-58720257,1,0,0,0,0,0,-251,-1,-1,-1057423361,-2074,-1,-1,0,0,0,0,0,0,-59648,-1,-1,-1,-1,-1,1073741823,0,0,0,0,0,0,-65536,-1,-1,-1,-1,-1,268435455,0,0,0,0,0,0,-65536,-1,-1,-1,-1,-1,67108863,0,0,0,0,0,0,-65536,-1,-1,-1,-1,-1,8388607,0,0,0,0,0,0,0,-403603456,-1,-1,-1,-1,262143,0,0,0,8388656,0,0,0,-1891434496,-1,-1,-1,-1,16383,0,0,0,8388608,0,0,0,-1612513280,-1,-1,-1,-1,63,0,0,0,0,0,0,0,-24320,-1,-1,-1,8388607,0,0,0,0,0,0,0,0,-256,-1,-1,1073741823,1,0,0,0,0,0,0,0,1610612736,-15,-1,-1,16383,0,0,0,0,0,0,0,0,-16646144,-1,-1,251658239,0,0,0,0,0,0,0,0,0,-51200,-1,-1,40959,0,0,0,0,0,0,268419584,103809024,-12713984,-1,-2147483137,4194303,0,0,0,0,0,0,0,402620416,-2144010240,-13631487,-32513,3,20480,0,0,0,0,0,0,0,419299328,0,-262144,-1,0,0,0,0,0,0,0,0,0,0,0,-5832704,268049407,0,0,0,0,0,0,0,0,0,0,0,0,33030144,0,0,0,0,0,0,0,0,0,0,0,0,3670016,0,0,0,0,0,0,0,0,0,0,0,0,1572864,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,458752,0,0,0,0,0,0,0,0,0,0,0,0,229376,0,0,0,0,0,0,0,0,0,0,0,0,32768,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8192,0,0,0,0,0,0,0,0,0,0,0,0,8192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31744,0,0,0,0,0,0,0,0,0,0,0,0,31744,0,0,0,0,0,0,0,0,0,0,0,0,64512,0,0,0,0,0,0,0,0,0,0,0,0,15872,0,0,0,0,0,0,0,0,0,0,0,0,3584,0,0,0,0,0,0,0,0,0,0,0,0,7680,0,0,0,0,0,0,0,0,0,0,0,0,512,0,0,0,0,0,0,0,0,0,0,0,0,3968,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3840,0,0,0,0,0,0,0,0,0,0,0,0,1855,0,0,0,0,0,0,0,0,0,0,0,0,63,0,0,0,0,0,0,0,0,0,0,0,0,15,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,134217728,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,0,0,124,0,0,0,0,0,0,0,0,0,0,0,-260046848,63,0,0,0,0,0,0,0,0,0,0,0,-17301504,127,0,0,0,0,0,0,0,0,0,0,0,-524288,127,0,0,0,0,0,0,0,0,0,0,0,-262144,127,0,0,0,0,0,0,0,0,0,0,0,-262144,63,0,0,0,0,0,0,0,0,0,0,0,-262144,63,0,0,0,0,0,0,0,0,0,0,0,-262144,63,0,0,0,0,0,0,0,0,0,0,0,-262144,31,0,0,0,0,0,0,0,0,0,0,0,-262144,63,0,0,0,0,3,0,0,0,0,0,0,-262144,63,0,0,0,0,7,0,0,0,0,0,0,-262144,63,0,0,0,0,63,0,0,0,0,0,0,-262144,63,0,0,0,0,511,0,0,0,0,0,0,-524288,31,0,0,0,0,8191,0,0,0,0,0,0,-1048576,63,0,0,0,0,131071,0,0,0,0,0,0,-524288,63,0,0,0,0,262143,0,0,0,0,0,0,-524288,63,0,0,0,0,131071,0,0,0,0,0,0,-1048576,63,0,0,0,0,262143,0,0,0,0,0,0,-1048576,63,0,0,0,0,262143,0,0,0,0,0,0,-1048576,63,0,0,0,0,262143,0,0,0,0,0,0,-1048576,63,0,0,0,0,262143,0,0,0,0,0,0,-2097152,127,0,0,0,0,262143,0,0,0,0,0,0,-2097152,127,0,0,0,0,262143,0,0,0,0,0,0,-1048576,127,0,0,0,0,262143,0,0,0,0,0,0,-1048576,127,0,0,0,0,262143,0,0,0,0,0,0,-2097152,255,0,0,0,0,262143,0,0,0,0,0,0,-2097152,255,0,0,0,0,262142,0,0,0,0,0,0,-2097152,255,0,0,0,0,262142,0,0,0,0,0,0,-2097152,255,0,0,0,0,262142,0,0,0,0,0,0,-2097152,255,0,0,0,0,262140,0,0,0,0,0,0,-2097152,255,0,0,0,0,131068,0,0,0,0,0,0,-4194304,255,0,0,0,0,131068,0,0,0,0,0,0,-4194304,255,0,0,0,0,65528,0,0,0,0,0,0,-8388608,255,0,0,0,0,65528,0,0,0,0,0,0,-8388608,255,0,0,0,0,65528,0,0,0,0,0,0,-8388608,255,0,0,0,0,32760,0,0,0,0,0,0,-8388608,255,0,0,0,0,32760,0,0,0,0,0,0,-16777216,255,0,0,-2147483648,255,16368,0,0,0,0,0,0,-16777216,255,0,0,-536870912,1023,16368,0,0,0,0,0,0,-33554432,255,0,0,-16777216,4095,16352,0,0,0,0,0,0,-33554432,255,0,0,-8388608,262143,16352,0,0,0,0,0,0,-33554432,255,0,0,-1048576,2097151,16352,0,0,0,0,0,0,-67108864,255,0,0,-524288,8388607,16352,0,0,0,0,0,0,-67108864,255,0,0,-262144,16777215,16320,0,0,0,0,0,0,-67108864,255,0,0,-131072,16777215,100679648,0,0,0,0,0,0,-67108864,255,0,0,-16384,16776959,125861824,0,0,0,0,0,0,-134217728,255,0,0,-4096,16773121,62930880,0,0,0,0,0,0,-134217728,127,0,0,2147482624,16252928,32704,0,0,0,0,0,0,-134217728,127,0,0,268435200,14680064,16320,0,0,0,0,0,0,-134217728,127,0,0,134217600,0,32704,0,0,0,0,0,0,-33554432,127,0,1056964608,67108736,0,32704,0,0,0,0,0,0,-33554432,127,0,2130706432,33554368,0,65408,0,0,0,0,0,0,-33554432,127,0,-16777216,8388576,0,32640,0,0,0,0,0,0,-134217728,127,0,-16777216,2097136,0,32640,0,0,0,0,0,0,-134217728,63,0,-16776960,1048573,0,32640,0,0,0,0,0,0,-536870912,63,0,-16776448,1048575,0,32640,0,0,0,0,0,0,-536870912,63,0,-33553664,6291455,66752,32640,0,0,0,0,0,0,-536870912,63,0,2013266688,2097148,229376,32640,0,0,0,0,0,0,-536870912,63,0,256,4194300,229376,32640,0,0,0,0,0,0,-536870912,63,0,0,524280,196608,32512,0,0,8,0,0,0,-1073741824,63,0,0,-200,15,65280,0,0,24,0,0,0,-1073741824,63,0,0,-1867768,127,32512,0,0,56,0,0,0,-1073741824,63,0,0,-1056768,4095,32512,0,0,124,0,0,0,-1073741824,63,0,0,-1050624,8191,32512,0,0,508,0,0,0,-2147483648,31,0,0,-7866368,8191,32512,0,0,1020,0,0,0,-2147483648,31,0,0,-33030656,8095,32512,0,0,2046,0,0,0,-2147483648,63,0,0,-66586624,771,32512,0,0,4094,0,0,0,0,63,0,0,-134184960,1,32256,0,0,8190,0,0,0,0,63,0,0,1610612736,0,32256,0,0,16382,0,0,0,-2147483648,63,0,0,0,0,15872,0,0,32767,0,0,0,-2147483648,31,0,0,0,0,15872,0,-2147483648,65535,0,0,0,-2147483648,31,0,0,0,0,7680,0,0,65535,0,0,0,-2147483648,31,0,0,134217728,0,7680,0,-2147483648,65535,0,0,0,-2147483648,31,0,0,0,0,7680,0,-2147483648,65535,0,0,0,-2147483648,31,0,0,0,0,7680,0,-1073741824,65535,0,0,0,-2147483648,31,0,0,0,0,3072,0,-1073741824,65535,0,0,0,-2147483648,31,0,0,0,0,3072,0,-1073741824,65535,0,0,0,-2147483648,31,0,0,0,0,0,0,-2147483648,65535,0,0,0,-2147483648,31,0,0,0,0,0,0,-1073741824,65535,0,0,0,0,31,0,0,0,0,0,0,-1073741824,65535,0,0,0,0,31,0,0,0,0,0,0,-2147483648,65535,0,0,0,0,30,0,0,0,0,0,0,-1073741824,65535,0,0,0,0,30,0,0,0,0,0,0,-2147483648,65535,0,0,0,0,30,0,0,0,0,0,0,0,65535,0,0,0,0,28,0,0,0,0,0,0,0,65535,0,0,0,0,28,0,0,0,0,0,0,0,65535,0,0,0,0,28,0,0,0,0,0,0,0,65535,0,0,0,0,24,0,0,0,0,0,0,-2147483648,65535,0,0,0,0,0,0,0,0,0,0,0,-536870912,65535);//[$offset] |= intval32bits(1 << ($x & 0x1f));
        $bob = $this->bits[$offset];
        $bob |= 1 << ($x & 0x1f);
        $this->bits[$offset] |= $bob;
        //$this->bits[$offset] = intval32bits($this->bits[$offset]);
        //}
        //16777216
    }
    public function _unset($x, $y) : void
    {
        //было unset, php не позволяет использовать unset
        $offset = (int) ($y * $this->rowSize + $x / 32);
        $this->bits[$offset] &= ~(1 << ($x & 0x1f));
    }
    /**1 << (249 & 0x1f)
     * <p>Flips the given bit.</p>
     *
     * @param $x ;  The horizontal component (i.e. which column)
     * @param $y ;  The vertical component (i.e. which row)
     */
    public function flip($x, $y) : void
    {
        $offset = $y * $this->rowSize + (int) ($x / 32);
        $this->bits[$offset] = $this->bits[$offset] ^ 1 << ($x & 0x1f);
    }
    /**
     * Exclusive-or (XOR): Flip the bit in this {@code BitMatrix} if the corresponding
     * mask bit is set.
     *
     * @param $mask ;  XOR mask
     */
    public function _xor($mask)
    {
        //было xor, php не позволяет использовать xor
        if ($this->width != $mask->getWidth() || $this->height != $mask->getHeight() || $this->rowSize != $mask->getRowSize()) {
            throw new \InvalidArgumentException("input matrix dimensions do not match");
        }
        $rowArray = new BitArray($this->width / 32 + 1);
        for ($y = 0; $y < $this->height; $y++) {
            $offset = $y * $this->rowSize;
            $row = $mask->getRow($y, $rowArray)->getBitArray();
            for ($x = 0; $x < $this->rowSize; $x++) {
                $this->bits[$offset + $x] ^= $row[$x];
            }
        }
    }
    /**
     * Clears all bits (sets to false).
     */
    public function clear() : void
    {
        $max = \is_countable($this->bits) ? \count($this->bits) : 0;
        for ($i = 0; $i < $max; $i++) {
            $this->bits[$i] = 0;
        }
    }
    /**
     * <p>Sets a square region of the bit matrix to true.</p>
     *
     * @param $left   ;  The horizontal position to begin at (inclusive)
     * @param $top    ;  The vertical position to begin at (inclusive)
     * @param $width  ;  The width of the region
     * @param $height ;  The height of the region
     */
    public function setRegion($left, $top, $width, $height)
    {
        if ($top < 0 || $left < 0) {
            throw new \InvalidArgumentException("Left and top must be nonnegative");
        }
        if ($height < 1 || $width < 1) {
            throw new \InvalidArgumentException("Height and width must be at least 1");
        }
        $right = $left + $width;
        $bottom = $top + $height;
        if ($bottom > $this->height || $right > $this->width) {
            //> this.height || right > this.width
            throw new \InvalidArgumentException("The region must fit inside the matrix");
        }
        for ($y = $top; $y < $bottom; $y++) {
            $offset = $y * $this->rowSize;
            for ($x = $left; $x < $right; $x++) {
                $this->bits[$offset + (int) ($x / 32)] = $this->bits[$offset + (int) ($x / 32)] |= 1 << ($x & 0x1f);
            }
        }
    }
    /**
     * Modifies this {@code BitMatrix} to represent the same but rotated 180 degrees
     */
    public function rotate180() : void
    {
        $width = $this->getWidth();
        $height = $this->getHeight();
        $topRow = new BitArray($width);
        $bottomRow = new BitArray($width);
        for ($i = 0; $i < ($height + 1) / 2; $i++) {
            $topRow = $this->getRow($i, $topRow);
            $bottomRow = $this->getRow($height - 1 - $i, $bottomRow);
            $topRow->reverse();
            $bottomRow->reverse();
            $this->setRow($i, $bottomRow);
            $this->setRow($height - 1 - $i, $topRow);
        }
    }
    /**
     * @return float The width of the matrix
     */
    public function getWidth()
    {
        return $this->width;
    }
    /**
     * A fast method to retrieve one row of data from the matrix as a BitArray.
     *
     * @param $y   ;  The row to retrieve
     * @param $row ;  An optional caller-allocated BitArray, will be allocated if null or too small
     *
     * @return BitArray The resulting BitArray - this reference should always be used even when passing
     *         your own row
     */
    public function getRow($y, $row)
    {
        if ($row == null || $row->getSize() < $this->width) {
            $row = new BitArray($this->width);
        } else {
            $row->clear();
        }
        $offset = $y * $this->rowSize;
        for ($x = 0; $x < $this->rowSize; $x++) {
            $row->setBulk($x * 32, $this->bits[$offset + $x]);
        }
        return $row;
    }
    /**
     * @param $y   ;  row to set
     * @param $row ;  {@link BitArray} to copy from
     */
    public function setRow($y, $row) : void
    {
        $this->bits = arraycopy($row->getBitArray(), 0, $this->bits, $y * $this->rowSize, $this->rowSize);
    }
    /**
     * This is useful in detecting the enclosing rectangle of a 'pure' barcode.
     *
     * @return {@code left,top,width,height} enclosing rectangle of all 1 bits, or null if it is all white
     */
    public function getEnclosingRectangle()
    {
        $left = $this->width;
        $top = $this->height;
        $right = -1;
        $bottom = -1;
        for ($y = 0; $y < $this->height; $y++) {
            for ($x32 = 0; $x32 < $this->rowSize; $x32++) {
                $theBits = $this->bits[$y * $this->rowSize + $x32];
                if ($theBits != 0) {
                    if ($y < $top) {
                        $top = $y;
                    }
                    if ($y > $bottom) {
                        $bottom = $y;
                    }
                    if ($x32 * 32 < $left) {
                        $bit = 0;
                        while ($theBits << 31 - $bit == 0) {
                            $bit++;
                        }
                        if ($x32 * 32 + $bit < $left) {
                            $left = $x32 * 32 + $bit;
                        }
                    }
                    if ($x32 * 32 + 31 > $right) {
                        $bit = 31;
                        while (sdvig3($theBits, $bit) == 0) {
                            //>>>
                            $bit--;
                        }
                        if ($x32 * 32 + $bit > $right) {
                            $right = $x32 * 32 + $bit;
                        }
                    }
                }
            }
        }
        $width = $right - $left;
        $height = $bottom - $top;
        if ($width < 0 || $height < 0) {
            return null;
        }
        return [$left, $top, $width, $height];
    }
    /**
     * This is useful in detecting a corner of a 'pure' barcode.
     *
     * @return {@code x,y} coordinate of top-left-most 1 bit, or null if it is all white
     */
    public function getTopLeftOnBit()
    {
        $bitsOffset = 0;
        while ($bitsOffset < (\is_countable($this->bits) ? \count($this->bits) : 0) && $this->bits[$bitsOffset] == 0) {
            $bitsOffset++;
        }
        if ($bitsOffset == (\is_countable($this->bits) ? \count($this->bits) : 0)) {
            return null;
        }
        $y = $bitsOffset / $this->rowSize;
        $x = $bitsOffset % $this->rowSize * 32;
        $theBits = $this->bits[$bitsOffset];
        $bit = 0;
        while ($theBits << 31 - $bit == 0) {
            $bit++;
        }
        $x += $bit;
        return [$x, $y];
    }
    public function getBottomRightOnBit()
    {
        $bitsOffset = (\is_countable($this->bits) ? \count($this->bits) : 0) - 1;
        while ($bitsOffset >= 0 && $this->bits[$bitsOffset] == 0) {
            $bitsOffset--;
        }
        if ($bitsOffset < 0) {
            return null;
        }
        $y = $bitsOffset / $this->rowSize;
        $x = $bitsOffset % $this->rowSize * 32;
        $theBits = $this->bits[$bitsOffset];
        $bit = 31;
        while (sdvig3($theBits, $bit) == 0) {
            //>>>
            $bit--;
        }
        $x += $bit;
        return [$x, $y];
    }
    /**
     * @return float The height of the matrix
     */
    public function getHeight()
    {
        return $this->height;
    }
    /**
     * @return int The row size of the matrix
     */
    public function getRowSize()
    {
        return $this->rowSize;
    }
    public function equals($o)
    {
        if (!$o instanceof BitMatrix) {
            return \false;
        }
        $other = $o;
        return $this->width == $other->width && $this->height == $other->height && $this->rowSize == $other->rowSize && $this->bits === $other->bits;
    }
    //@Override
    public function hashCode()
    {
        $hash = $this->width;
        $hash = 31 * $hash + $this->width;
        $hash = 31 * $hash + $this->height;
        $hash = 31 * $hash + $this->rowSize;
        $hash = 31 * $hash + hashCode($this->bits);
        return $hash;
    }
    //@Override
    public function toString($setString = '', $unsetString = '', $lineSeparator = '')
    {
        if (!$setString || !$unsetString) {
            return (string) 'X ' . '  ';
        }
        if ($lineSeparator && $lineSeparator !== "\n") {
            return $this->toString_($setString, $unsetString, $lineSeparator);
        }
        return (string) ($setString . $unsetString . "\n");
    }
    public function toString_($setString, $unsetString, $lineSeparator)
    {
        //$result = new StringBuilder(height * (width + 1));
        $result = '';
        for ($y = 0; $y < $this->height; $y++) {
            for ($x = 0; $x < $this->width; $x++) {
                $result .= $this->get($x, $y) ? $setString : $unsetString;
            }
            $result .= $lineSeparator;
        }
        return (string) $result;
    }
    /**
     * @deprecated call {@link #toString(String,String)} only, which uses \n line separator always
     */
    // @Deprecated
    /**
     * <p>Gets the requested bit, where true means black.</p>
     *
     * @param $x ;  The horizontal component (i.e. which column)
     * @param $y ;  The vertical component (i.e. which row)
     *
     * @return value of given bit in matrix
     */
    public function get($x, $y)
    {
        $offset = (int) ($y * $this->rowSize + $x / 32);
        if (!isset($this->bits[$offset])) {
            $this->bits[$offset] = 0;
        }
        // return (($this->bits[$offset] >> ($x & 0x1f)) & 1) != 0;
        return (uRShift($this->bits[$offset], $x & 0x1f) & 1) != 0;
        //было >>> вместо >>, не знаю как эмулировать беззнаковый сдвиг
    }
    //  @Override
    public function _clone() : \WP2FA_Vendor\Zxing\Common\BitMatrix
    {
        return new BitMatrix($this->width, $this->height, $this->rowSize, $this->bits);
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/Common/customFunctions.php000064400000004175150755130600022755 0ustar00<?php

namespace WP2FA_Vendor;

if (!\function_exists('WP2FA_Vendor\\arraycopy')) {
    function arraycopy($srcArray, $srcPos, $destArray, $destPos, $length)
    {
        $srcArrayToCopy = \array_slice($srcArray, $srcPos, $length);
        \array_splice($destArray, $destPos, $length, $srcArrayToCopy);
        return $destArray;
    }
}
if (!\function_exists('WP2FA_Vendor\\hashCode')) {
    function hashCode($s)
    {
        $h = 0;
        $len = \strlen((string) $s);
        for ($i = 0; $i < $len; $i++) {
            $h = 31 * $h + \ord($s[$i]);
        }
        return $h;
    }
}
if (!\function_exists('WP2FA_Vendor\\numberOfTrailingZeros')) {
    function numberOfTrailingZeros($i)
    {
        if ($i == 0) {
            return 32;
        }
        $num = 0;
        while (($i & 1) == 0) {
            $i >>= 1;
            $num++;
        }
        return $num;
    }
}
if (!\function_exists('WP2FA_Vendor\\uRShift')) {
    function uRShift($a, $b)
    {
        static $mask = 8 * \PHP_INT_SIZE - 1;
        if ($b === 0) {
            return $a;
        }
        return $a >> $b & ~(1 << $mask >> $b - 1);
    }
}
/*
function sdvig3($num,$count=1){//>>> 32 bit
	$s = decbin($num);

	$sarray  = str_split($s,1);
	$sarray = array_slice($sarray,-32);//32bit

	for($i=0;$i<=1;$i++) {
		array_pop($sarray);
		array_unshift($sarray, '0');
	}
	return bindec(implode($sarray));
}
*/
if (!\function_exists('WP2FA_Vendor\\sdvig3')) {
    function sdvig3($a, $b)
    {
        if ($a >= 0) {
            return \bindec(\decbin($a >> $b));
            //simply right shift for positive number
        }
        $bin = \decbin($a >> $b);
        $bin = \substr($bin, $b);
        // zero fill on the left side
        return \bindec($bin);
    }
}
if (!\function_exists('WP2FA_Vendor\\floatToIntBits')) {
    function floatToIntBits($float_val)
    {
        $int = \unpack('i', \pack('f', $float_val));
        return $int[1];
    }
}
if (!\function_exists('WP2FA_Vendor\\fill_array')) {
    function fill_array($index, $count, $value)
    {
        if ($count <= 0) {
            return [0];
        }
        return \array_fill($index, $count, $value);
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/Common/GlobalHistogramBinarizer.php000064400000016615150755130600024500 0ustar00<?php

/*
* Copyright 2009 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace WP2FA_Vendor\Zxing\Common;

use WP2FA_Vendor\Zxing\Binarizer;
use WP2FA_Vendor\Zxing\NotFoundException;
/**
 * This Binarizer implementation uses the old ZXing global histogram approach. It is suitable
 * for low-end mobile devices which don't have enough CPU or memory to use a local thresholding
 * algorithm. However, because it picks a global black point, it cannot handle difficult shadows
 * and gradients.
 *
 * Faster mobile devices and all desktop applications should probably use HybridBinarizer instead.
 *
 * @author dswitkin@google.com (Daniel Switkin)
 * @author Sean Owen
 */
class GlobalHistogramBinarizer extends Binarizer
{
    private static int $LUMINANCE_BITS = 5;
    private static int $LUMINANCE_SHIFT = 3;
    private static int $LUMINANCE_BUCKETS = 32;
    private static array $EMPTY = [];
    private array $luminances = [];
    private array $buckets = [];
    /**
     * @var mixed|\Zxing\LuminanceSource
     */
    private $source = [];
    public function __construct($source)
    {
        self::$LUMINANCE_SHIFT = 8 - self::$LUMINANCE_BITS;
        self::$LUMINANCE_BUCKETS = 1 << self::$LUMINANCE_BITS;
        parent::__construct($source);
        $this->luminances = self::$EMPTY;
        $this->buckets = fill_array(0, self::$LUMINANCE_BUCKETS, 0);
        $this->source = $source;
    }
    // Applies simple sharpening to the row data to improve performance of the 1D Readers.
    public function getBlackRow($y, $row = null)
    {
        $this->source = $this->getLuminanceSource();
        $width = $this->source->getWidth();
        if ($row == null || $row->getSize() < $width) {
            $row = new BitArray($width);
        } else {
            $row->clear();
        }
        $this->initArrays($width);
        $localLuminances = $this->source->getRow($y, $this->luminances);
        $localBuckets = $this->buckets;
        for ($x = 0; $x < $width; $x++) {
            $pixel = $localLuminances[$x] & 0xff;
            $localBuckets[$pixel >> self::$LUMINANCE_SHIFT]++;
        }
        $blackPoint = self::estimateBlackPoint($localBuckets);
        $left = $localLuminances[0] & 0xff;
        $center = $localLuminances[1] & 0xff;
        for ($x = 1; $x < $width - 1; $x++) {
            $right = $localLuminances[$x + 1] & 0xff;
            // A simple -1 4 -1 box filter with a weight of 2.
            $luminance = ($center * 4 - $left - $right) / 2;
            if ($luminance < $blackPoint) {
                $row->set($x);
            }
            $left = $center;
            $center = $right;
        }
        return $row;
    }
    // Does not sharpen the data, as this call is intended to only be used by 2D Readers.
    private function initArrays($luminanceSize) : void
    {
        if (\count($this->luminances) < $luminanceSize) {
            $this->luminances = [];
        }
        for ($x = 0; $x < self::$LUMINANCE_BUCKETS; $x++) {
            $this->buckets[$x] = 0;
        }
    }
    private static function estimateBlackPoint($buckets)
    {
        // Find the tallest peak in the histogram.
        $numBuckets = \is_countable($buckets) ? \count($buckets) : 0;
        $maxBucketCount = 0;
        $firstPeak = 0;
        $firstPeakSize = 0;
        for ($x = 0; $x < $numBuckets; $x++) {
            if ($buckets[$x] > $firstPeakSize) {
                $firstPeak = $x;
                $firstPeakSize = $buckets[$x];
            }
            if ($buckets[$x] > $maxBucketCount) {
                $maxBucketCount = $buckets[$x];
            }
        }
        // Find the second-tallest peak which is somewhat far from the tallest peak.
        $secondPeak = 0;
        $secondPeakScore = 0;
        for ($x = 0; $x < $numBuckets; $x++) {
            $distanceToBiggest = $x - $firstPeak;
            // Encourage more distant second peaks by multiplying by square of distance.
            $score = $buckets[$x] * $distanceToBiggest * $distanceToBiggest;
            if ($score > $secondPeakScore) {
                $secondPeak = $x;
                $secondPeakScore = $score;
            }
        }
        // Make sure firstPeak corresponds to the black peak.
        if ($firstPeak > $secondPeak) {
            $temp = $firstPeak;
            $firstPeak = $secondPeak;
            $secondPeak = $temp;
        }
        // If there is too little contrast in the image to pick a meaningful black point, throw rather
        // than waste time trying to decode the image, and risk false positives.
        if ($secondPeak - $firstPeak <= $numBuckets / 16) {
            throw NotFoundException::getNotFoundInstance();
        }
        // Find a valley between them that is low and closer to the white peak.
        $bestValley = $secondPeak - 1;
        $bestValleyScore = -1;
        for ($x = $secondPeak - 1; $x > $firstPeak; $x--) {
            $fromFirst = $x - $firstPeak;
            $score = $fromFirst * $fromFirst * ($secondPeak - $x) * ($maxBucketCount - $buckets[$x]);
            if ($score > $bestValleyScore) {
                $bestValley = $x;
                $bestValleyScore = $score;
            }
        }
        return $bestValley << self::$LUMINANCE_SHIFT;
    }
    public function getBlackMatrix()
    {
        $source = $this->getLuminanceSource();
        $width = $source->getWidth();
        $height = $source->getHeight();
        $matrix = new BitMatrix($width, $height);
        // Quickly calculates the histogram by sampling four rows from the image. This proved to be
        // more robust on the blackbox tests than sampling a diagonal as we used to do.
        $this->initArrays($width);
        $localBuckets = $this->buckets;
        for ($y = 1; $y < 5; $y++) {
            $row = (int) ($height * $y / 5);
            $localLuminances = $source->getRow($row, $this->luminances);
            $right = (int) ($width * 4 / 5);
            for ($x = (int) ($width / 5); $x < $right; $x++) {
                $pixel = $localLuminances[(int) $x] & 0xff;
                $localBuckets[$pixel >> self::$LUMINANCE_SHIFT]++;
            }
        }
        $blackPoint = self::estimateBlackPoint($localBuckets);
        // We delay reading the entire image luminance until the black point estimation succeeds.
        // Although we end up reading four rows twice, it is consistent with our motto of
        // "fail quickly" which is necessary for continuous scanning.
        $localLuminances = $source->getMatrix();
        for ($y = 0; $y < $height; $y++) {
            $offset = $y * $width;
            for ($x = 0; $x < $width; $x++) {
                $pixel = (int) ($localLuminances[$offset + $x] & 0xff);
                if ($pixel < $blackPoint) {
                    $matrix->set($x, $y);
                }
            }
        }
        return $matrix;
    }
    public function createBinarizer($source) : \WP2FA_Vendor\Zxing\Common\GlobalHistogramBinarizer
    {
        return new GlobalHistogramBinarizer($source);
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/Common/DecoderResult.php000064400000005010150755130600022303 0ustar00<?php

/*
 * Copyright 2007 ZXing authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
namespace WP2FA_Vendor\Zxing\Common;

/**
 * <p>Encapsulates the result of decoding a matrix of bits. This typically
 * applies to 2D barcode formats. For now it contains the raw bytes obtained,
 * as well as a String interpretation of those bytes, if applicable.</p>
 *
 * @author Sean Owen
 */
final class DecoderResult
{
    /**
     * @var mixed|null
     */
    private $errorsCorrected;
    /**
     * @var mixed|null
     */
    private $erasures;
    /**
     * @var mixed|null
     */
    private $other;
    public function __construct(private $rawBytes, private $text, private $byteSegments, private $ecLevel, private $structuredAppendSequenceNumber = -1, private $structuredAppendParity = -1)
    {
    }
    public function getRawBytes()
    {
        return $this->rawBytes;
    }
    public function getText()
    {
        return $this->text;
    }
    public function getByteSegments()
    {
        return $this->byteSegments;
    }
    public function getECLevel()
    {
        return $this->ecLevel;
    }
    public function getErrorsCorrected()
    {
        return $this->errorsCorrected;
    }
    public function setErrorsCorrected($errorsCorrected) : void
    {
        $this->errorsCorrected = $errorsCorrected;
    }
    public function getErasures()
    {
        return $this->erasures;
    }
    public function setErasures($erasures) : void
    {
        $this->erasures = $erasures;
    }
    public function getOther()
    {
        return $this->other;
    }
    public function setOther($other) : void
    {
        $this->other = $other;
    }
    public function hasStructuredAppend()
    {
        return $this->structuredAppendParity >= 0 && $this->structuredAppendSequenceNumber >= 0;
    }
    public function getStructuredAppendParity()
    {
        return $this->structuredAppendParity;
    }
    public function getStructuredAppendSequenceNumber()
    {
        return $this->structuredAppendSequenceNumber;
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/QrReader.php000064400000006477150755130600020036 0ustar00<?php

namespace WP2FA_Vendor\Zxing;

use WP2FA_Vendor\Zxing\Common\HybridBinarizer;
use WP2FA_Vendor\Zxing\Qrcode\QRCodeReader;
final class QrReader
{
    public const SOURCE_TYPE_FILE = 'file';
    public const SOURCE_TYPE_BLOB = 'blob';
    public const SOURCE_TYPE_RESOURCE = 'resource';
    private readonly \WP2FA_Vendor\Zxing\BinaryBitmap $bitmap;
    private readonly \WP2FA_Vendor\Zxing\Qrcode\QRCodeReader $reader;
    private \WP2FA_Vendor\Zxing\Result|bool|null $result = null;
    public function __construct($imgSource, $sourceType = QrReader::SOURCE_TYPE_FILE, $useImagickIfAvailable = \true)
    {
        if (!\in_array($sourceType, [self::SOURCE_TYPE_FILE, self::SOURCE_TYPE_BLOB, self::SOURCE_TYPE_RESOURCE], \true)) {
            throw new \InvalidArgumentException('Invalid image source.');
        }
        $im = null;
        switch ($sourceType) {
            case QrReader::SOURCE_TYPE_FILE:
                if ($useImagickIfAvailable && \extension_loaded('imagick')) {
                    $im = new \Imagick();
                    $im->readImage($imgSource);
                } else {
                    $image = \file_get_contents($imgSource);
                    $im = \imagecreatefromstring($image);
                }
                break;
            case QrReader::SOURCE_TYPE_BLOB:
                if ($useImagickIfAvailable && \extension_loaded('imagick')) {
                    $im = new \Imagick();
                    $im->readImageBlob($imgSource);
                } else {
                    $im = \imagecreatefromstring($imgSource);
                }
                break;
            case QrReader::SOURCE_TYPE_RESOURCE:
                $im = $imgSource;
                if ($useImagickIfAvailable && \extension_loaded('imagick')) {
                    $useImagickIfAvailable = \true;
                } else {
                    $useImagickIfAvailable = \false;
                }
                break;
        }
        if ($useImagickIfAvailable && \extension_loaded('imagick')) {
            if (!$im instanceof \Imagick) {
                throw new \InvalidArgumentException('Invalid image source.');
            }
            $width = $im->getImageWidth();
            $height = $im->getImageHeight();
            $source = new IMagickLuminanceSource($im, $width, $height);
        } else {
            if (!$im instanceof \GdImage && !\is_object($im)) {
                throw new \InvalidArgumentException('Invalid image source.');
            }
            $width = \imagesx($im);
            $height = \imagesy($im);
            $source = new GDLuminanceSource($im, $width, $height);
        }
        $histo = new HybridBinarizer($source);
        $this->bitmap = new BinaryBitmap($histo);
        $this->reader = new QRCodeReader();
    }
    public function decode($hints = null) : void
    {
        try {
            $this->result = $this->reader->decode($this->bitmap, $hints);
        } catch (NotFoundException|FormatException|ChecksumException) {
            $this->result = \false;
        }
    }
    public function text($hints = null)
    {
        $this->decode($hints);
        if ($this->result !== \false && \method_exists($this->result, 'toString')) {
            return $this->result->toString();
        }
        return $this->result;
    }
    public function getResult()
    {
        return $this->result;
    }
}
vendor/khanamiryan/qrcode-detector-decoder/lib/ReaderException.php000064400000003163150755130600021377 0ustar00<?php

/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace WP2FA_Vendor\Zxing;

/**
 * The general exception class throw when something goes wrong during decoding of a barcode.
 * This includes, but is not limited to, failing checksums / error correction algorithms, being
 * unable to locate finder timing patterns, and so on.
 *
 * @author Sean Owen
 */
abstract class ReaderException extends \Exception
{
    // disable stack traces when not running inside test units
    //protected static  $isStackTrace = System.getProperty("surefire.test.class.path") != null;
    protected static bool $isStackTrace = \false;
    public function ReaderException($cause = null) : void
    {
        if ($cause) {
            parent::__construct($cause);
        }
    }
    // Prevent stack traces from being taken
    // srowen says: huh, my IDE is saying this is not an override. native methods can't be overridden?
    // This, at least, does not hurt. Because we use a singleton pattern here, it doesn't matter anyhow.
    //@Override
    public final function fillInStackTrace()
    {
        return null;
    }
}
vendor/khanamiryan/qrcode-detector-decoder/rector.php000064400000005152150755130600017046 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor;

use WP2FA_Vendor\Rector\Config\RectorConfig;
use WP2FA_Vendor\Rector\Nette\Set\NetteSetList;
use WP2FA_Vendor\Rector\Set\ValueObject\SetList;
use WP2FA_Vendor\Rector\Core\Configuration\Option;
use WP2FA_Vendor\Rector\Symfony\Set\SymfonySetList;
use WP2FA_Vendor\Rector\Doctrine\Set\DoctrineSetList;
use WP2FA_Vendor\Rector\Set\ValueObject\LevelSetList;
use WP2FA_Vendor\Rector\Symfony\Set\SensiolabsSetList;
use WP2FA_Vendor\Rector\TypeDeclaration\Rector\Property\PropertyTypeDeclarationRector;
use WP2FA_Vendor\Rector\TypeDeclaration\Rector\Property\TypedPropertyFromAssignsRector;
use WP2FA_Vendor\Rector\TypeDeclaration\Rector\ClassMethod\ReturnTypeFromReturnNewRector;
use WP2FA_Vendor\Rector\TypeDeclaration\Rector\ClassMethod\AddReturnTypeDeclarationRector;
use WP2FA_Vendor\Rector\CodeQuality\Rector\Class_\InlineConstructorDefaultToPropertyRector;
use WP2FA_Vendor\Rector\TypeDeclaration\Rector\ClassMethod\ParamTypeByMethodCallTypeRector;
use WP2FA_Vendor\Rector\TypeDeclaration\Rector\ClassMethod\ParamTypeByParentCallTypeRector;
use WP2FA_Vendor\Rector\TypeDeclaration\Rector\ClassMethod\AddVoidReturnTypeWhereNoReturnRector;
use WP2FA_Vendor\Rector\TypeDeclaration\Rector\ClassMethod\ArrayShapeFromConstantArrayReturnRector;
return static function (RectorConfig $rectorConfig) : void {
    $rectorConfig->paths([__DIR__ . '/lib']);
    $parameters = $rectorConfig->parameters();
    $parameters->set(Option::SYMFONY_CONTAINER_XML_PATH_PARAMETER, __DIR__ . '/var/cache/dev/App_KernelDevDebugContainer.xml');
    $rectorConfig->sets([DoctrineSetList::ANNOTATIONS_TO_ATTRIBUTES, SymfonySetList::ANNOTATIONS_TO_ATTRIBUTES, NetteSetList::ANNOTATIONS_TO_ATTRIBUTES, SensiolabsSetList::FRAMEWORK_EXTRA_61, SymfonySetList::SYMFONY_60, LevelSetList::UP_TO_PHP_81]);
    // register a single rule
    $rectorConfig->rule(InlineConstructorDefaultToPropertyRector::class);
    $rectorConfig->rule(AddReturnTypeDeclarationRector::class);
    $rectorConfig->rules([
        AddVoidReturnTypeWhereNoReturnRector::class,
        ArrayShapeFromConstantArrayReturnRector::class,
        ParamTypeByMethodCallTypeRector::class,
        ParamTypeByParentCallTypeRector::class,
        PropertyTypeDeclarationRector::class,
        ReturnTypeFromReturnNewRector::class,
        // ReturnTypeFromStrictBoolReturnExprRector::class,
        // ReturnTypeFromStrictNativeFuncCallRector::class,
        // ReturnTypeFromStrictNewArrayRector::class,
        TypedPropertyFromAssignsRector::class,
    ]);
    // define sets of rules
    //    $rectorConfig->sets([
    //        LevelSetList::UP_TO_PHP_80
    //    ]);
};
vendor/myclabs/php-enum/stubs/Stringable.php000064400000000342150755130600015170 0ustar00<?php

namespace WP2FA_Vendor;

if (\PHP_VERSION_ID < 80000 && !\interface_exists('Stringable')) {
    interface Stringable
    {
        /**
         * @return string
         */
        public function __toString();
    }
}
vendor/myclabs/php-enum/src/PHPUnit/Comparator.php000064400000002437150755130600016132 0ustar00<?php

namespace WP2FA_Vendor\MyCLabs\Enum\PHPUnit;

use WP2FA_Vendor\MyCLabs\Enum\Enum;
use WP2FA_Vendor\SebastianBergmann\Comparator\ComparisonFailure;
/**
 * Use this Comparator to get nice output when using PHPUnit assertEquals() with Enums.
 *
 * Add this to your PHPUnit bootstrap PHP file:
 *
 * \SebastianBergmann\Comparator\Factory::getInstance()->register(new \MyCLabs\Enum\PHPUnit\Comparator());
 */
final class Comparator extends \WP2FA_Vendor\SebastianBergmann\Comparator\Comparator
{
    public function accepts($expected, $actual)
    {
        return $expected instanceof Enum && ($actual instanceof Enum || $actual === null);
    }
    /**
     * @param Enum $expected
     * @param Enum|null $actual
     *
     * @return void
     */
    public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = \false, $ignoreCase = \false)
    {
        if ($expected->equals($actual)) {
            return;
        }
        throw new ComparisonFailure($expected, $actual, $this->formatEnum($expected), $this->formatEnum($actual), \false, 'Failed asserting that two Enums are equal.');
    }
    private function formatEnum(Enum $enum = null)
    {
        if ($enum === null) {
            return "null";
        }
        return \get_class($enum) . "::{$enum->getKey()}()";
    }
}
vendor/myclabs/php-enum/src/Enum.php000064400000017725150755130600013446 0ustar00<?php

/**
 * @link    http://github.com/myclabs/php-enum
 * @license http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
 */
namespace WP2FA_Vendor\MyCLabs\Enum;

/**
 * Base Enum class
 *
 * Create an enum by implementing this class and adding class constants.
 *
 * @author Matthieu Napoli <matthieu@mnapoli.fr>
 * @author Daniel Costa <danielcosta@gmail.com>
 * @author Mirosław Filip <mirfilip@gmail.com>
 *
 * @psalm-template T
 * @psalm-immutable
 * @psalm-consistent-constructor
 */
abstract class Enum implements \JsonSerializable, \Stringable
{
    /**
     * Enum value
     *
     * @var mixed
     * @psalm-var T
     */
    protected $value;
    /**
     * Enum key, the constant name
     *
     * @var string
     */
    private $key;
    /**
     * Store existing constants in a static cache per object.
     *
     *
     * @var array
     * @psalm-var array<class-string, array<string, mixed>>
     */
    protected static $cache = [];
    /**
     * Cache of instances of the Enum class
     *
     * @var array
     * @psalm-var array<class-string, array<string, static>>
     */
    protected static $instances = [];
    /**
     * Creates a new value of some type
     *
     * @psalm-pure
     * @param mixed $value
     *
     * @psalm-param T $value
     * @throws \UnexpectedValueException if incompatible type is given.
     */
    public function __construct($value)
    {
        if ($value instanceof static) {
            /** @psalm-var T */
            $value = $value->getValue();
        }
        /** @psalm-suppress ImplicitToStringCast assertValidValueReturningKey returns always a string but psalm has currently an issue here */
        $this->key = static::assertValidValueReturningKey($value);
        /** @psalm-var T */
        $this->value = $value;
    }
    /**
     * This method exists only for the compatibility reason when deserializing a previously serialized version
     * that didn't had the key property
     */
    public function __wakeup()
    {
        /** @psalm-suppress DocblockTypeContradiction key can be null when deserializing an enum without the key */
        if ($this->key === null) {
            /**
             * @psalm-suppress InaccessibleProperty key is not readonly as marked by psalm
             * @psalm-suppress PossiblyFalsePropertyAssignmentValue deserializing a case that was removed
             */
            $this->key = static::search($this->value);
        }
    }
    /**
     * @param mixed $value
     * @return static
     */
    public static function from($value) : self
    {
        $key = static::assertValidValueReturningKey($value);
        return self::__callStatic($key, []);
    }
    /**
     * @psalm-pure
     * @return mixed
     * @psalm-return T
     */
    public function getValue()
    {
        return $this->value;
    }
    /**
     * Returns the enum key (i.e. the constant name).
     *
     * @psalm-pure
     * @return string
     */
    public function getKey()
    {
        return $this->key;
    }
    /**
     * @psalm-pure
     * @psalm-suppress InvalidCast
     * @return string
     */
    public function __toString()
    {
        return (string) $this->value;
    }
    /**
     * Determines if Enum should be considered equal with the variable passed as a parameter.
     * Returns false if an argument is an object of different class or not an object.
     *
     * This method is final, for more information read https://github.com/myclabs/php-enum/issues/4
     *
     * @psalm-pure
     * @psalm-param mixed $variable
     * @return bool
     */
    public final function equals($variable = null) : bool
    {
        return $variable instanceof self && $this->getValue() === $variable->getValue() && static::class === \get_class($variable);
    }
    /**
     * Returns the names (keys) of all constants in the Enum class
     *
     * @psalm-pure
     * @psalm-return list<string>
     * @return array
     */
    public static function keys()
    {
        return \array_keys(static::toArray());
    }
    /**
     * Returns instances of the Enum class of all Enum constants
     *
     * @psalm-pure
     * @psalm-return array<string, static>
     * @return static[] Constant name in key, Enum instance in value
     */
    public static function values()
    {
        $values = array();
        /** @psalm-var T $value */
        foreach (static::toArray() as $key => $value) {
            $values[$key] = new static($value);
        }
        return $values;
    }
    /**
     * Returns all possible values as an array
     *
     * @psalm-pure
     * @psalm-suppress ImpureStaticProperty
     *
     * @psalm-return array<string, mixed>
     * @return array Constant name in key, constant value in value
     */
    public static function toArray()
    {
        $class = static::class;
        if (!isset(static::$cache[$class])) {
            /** @psalm-suppress ImpureMethodCall this reflection API usage has no side-effects here */
            $reflection = new \ReflectionClass($class);
            /** @psalm-suppress ImpureMethodCall this reflection API usage has no side-effects here */
            static::$cache[$class] = $reflection->getConstants();
        }
        return static::$cache[$class];
    }
    /**
     * Check if is valid enum value
     *
     * @param $value
     * @psalm-param mixed $value
     * @psalm-pure
     * @psalm-assert-if-true T $value
     * @return bool
     */
    public static function isValid($value)
    {
        return \in_array($value, static::toArray(), \true);
    }
    /**
     * Asserts valid enum value
     *
     * @psalm-pure
     * @psalm-assert T $value
     * @param mixed $value
     */
    public static function assertValidValue($value) : void
    {
        self::assertValidValueReturningKey($value);
    }
    /**
     * Asserts valid enum value
     *
     * @psalm-pure
     * @psalm-assert T $value
     * @param mixed $value
     * @return string
     */
    private static function assertValidValueReturningKey($value) : string
    {
        if (\false === ($key = static::search($value))) {
            throw new \UnexpectedValueException("Value '{$value}' is not part of the enum " . static::class);
        }
        return $key;
    }
    /**
     * Check if is valid enum key
     *
     * @param $key
     * @psalm-param string $key
     * @psalm-pure
     * @return bool
     */
    public static function isValidKey($key)
    {
        $array = static::toArray();
        return isset($array[$key]) || \array_key_exists($key, $array);
    }
    /**
     * Return key for value
     *
     * @param mixed $value
     *
     * @psalm-param mixed $value
     * @psalm-pure
     * @return string|false
     */
    public static function search($value)
    {
        return \array_search($value, static::toArray(), \true);
    }
    /**
     * Returns a value when called statically like so: MyEnum::SOME_VALUE() given SOME_VALUE is a class constant
     *
     * @param string $name
     * @param array  $arguments
     *
     * @return static
     * @throws \BadMethodCallException
     *
     * @psalm-pure
     */
    public static function __callStatic($name, $arguments)
    {
        $class = static::class;
        if (!isset(self::$instances[$class][$name])) {
            $array = static::toArray();
            if (!isset($array[$name]) && !\array_key_exists($name, $array)) {
                $message = "No static method or enum constant '{$name}' in class " . static::class;
                throw new \BadMethodCallException($message);
            }
            return self::$instances[$class][$name] = new static($array[$name]);
        }
        return clone self::$instances[$class][$name];
    }
    /**
     * Specify data which should be serialized to JSON. This method returns data that can be serialized by json_encode()
     * natively.
     *
     * @return mixed
     * @link http://php.net/manual/en/jsonserializable.jsonserialize.php
     * @psalm-pure
     */
    #[\ReturnTypeWillChange]
    public function jsonSerialize()
    {
        return $this->getValue();
    }
}
vendor/scoper.inc.php000064400000022541150755130600010623 0ustar00<?php

namespace WP2FA_Vendor;

/**
 * PHP-Scoper configuration file.
 *
 * @package   wp2fa
 * @copyright %%YEAR%% Melapress
 * @license   https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 * @link      https://wordpress.org/plugins/wp-2fa/
 */
use WP2FA_Vendor\Isolated\Symfony\Component\Finder\Finder;
return array(
    'prefix' => 'WP2FA_Vendor',
    'finders' => array(
        // General dependencies.
        Finder::create()->files()->ignoreVCS(\true)->notName('/LICENSE|.*\\.md|.*\\.dist|Makefile|composer\\.(json|lock)/')->exclude(array('doc', 'test', 'test_old', 'tests', 'Tests', 'vendor-bin'))->in('../vendor'),
    ),
    'patchers' => array(static function (string $file_path, string $prefix, string $content) : string {
        $path = \dirname(__FILE__) . \DIRECTORY_SEPARATOR . \implode(\DIRECTORY_SEPARATOR, array('composer', 'autoload_real.php'));
        if (0 === \strcasecmp($file_path, $path)) {
            $content = \str_replace('spl_autoload_unregister(array(\'ComposerAutoloader', 'spl_autoload_unregister(array(\'' . $prefix . '\\\\ComposerAutoloader', $content);
        }
        return $content;
    }, function ($file_path, $prefix, $contents) {
        /*
         * There is currently no easy way to simply whitelist all global WordPress functions.
         *
         * This list here is a manual attempt after scanning through the AMP plugin, which means
         * it needs to be maintained and kept in sync with any changes to the dependency.
         *
         * As long as there's no built-in solution in PHP-Scoper for this, an alternative could be
         * to generate a list based on php-stubs/wordpress-stubs. devowlio/wp-react-starter/ seems
         * to be doing just this successfully.
         *
         * @see https://github.com/humbug/php-scoper/issues/303
         * @see https://github.com/php-stubs/wordpress-stubs
         * @see https://github.com/devowlio/wp-react-starter/
         */
        $contents = \str_replace("\\{$prefix}\\_doing_it_wrong", '\\_doing_it_wrong', $contents);
        $contents = \str_replace("\\{$prefix}\\__", '\\__', $contents);
        $contents = \str_replace("\\{$prefix}\\esc_html_e", '\\esc_html_e', $contents);
        $contents = \str_replace("\\{$prefix}\\esc_html", '\\esc_html', $contents);
        $contents = \str_replace("\\{$prefix}\\esc_attr", '\\esc_attr', $contents);
        $contents = \str_replace("\\{$prefix}\\esc_url", '\\esc_url', $contents);
        $contents = \str_replace("\\{$prefix}\\do_action", '\\do_action', $contents);
        $contents = \str_replace("\\{$prefix}\\site_url", '\\site_url', $contents);
        $contents = \str_replace("\\{$prefix}\\wp_guess_url", '\\wp_guess_url', $contents);
        $contents = \str_replace("\\{$prefix}\\untrailingslashit", '\\untrailingslashit', $contents);
        $contents = \str_replace("\\{$prefix}\\WP_CONTENT_URL", '\\WP_CONTENT_URL', $contents);
        $contents = \str_replace("\\{$prefix}\\wp_list_pluck", '\\wp_list_pluck', $contents);
        $contents = \str_replace("\\{$prefix}\\is_customize_preview", '\\is_customize_preview', $contents);
        $contents = \str_replace("\\{$prefix}\\do_action", '\\do_action', $contents);
        $contents = \str_replace("\\{$prefix}\\trailingslashit", '\\trailingslashit', $contents);
        $contents = \str_replace("\\{$prefix}\\get_template_directory_uri", '\\get_template_directory_uri', $contents);
        $contents = \str_replace("\\{$prefix}\\get_stylesheet_directory_uri", '\\get_stylesheet_directory_uri', $contents);
        $contents = \str_replace("\\{$prefix}\\includes_url", '\\includes_url', $contents);
        $contents = \str_replace("\\{$prefix}\\wp_styles", '\\wp_styles', $contents);
        $contents = \str_replace("\\{$prefix}\\get_stylesheet", '\\get_stylesheet', $contents);
        $contents = \str_replace("\\{$prefix}\\get_template", '\\get_template', $contents);
        $contents = \str_replace("\\{$prefix}\\wp_parse_url", '\\wp_parse_url', $contents);
        $contents = \str_replace("\\{$prefix}\\is_wp_error", '\\is_wp_error', $contents);
        $contents = \str_replace("\\{$prefix}\\content_url", '\\content_url', $contents);
        $contents = \str_replace("\\{$prefix}\\get_admin_url", '\\get_admin_url', $contents);
        $contents = \str_replace("\\{$prefix}\\WP_CONTENT_DIR", '\\WP_CONTENT_DIR', $contents);
        $contents = \str_replace("\\{$prefix}\\ABSPATH", '\\ABSPATH', $contents);
        $contents = \str_replace("\\{$prefix}\\wp_nonce_url", '\\wp_nonce_url', $contents);
        $contents = \str_replace("\\{$prefix}\\WPINC", '\\WPINC', $contents);
        $contents = \str_replace("\\{$prefix}\\home_url", '\\home_url', $contents);
        $contents = \str_replace("\\{$prefix}\\__", '\\__', $contents);
        $contents = \str_replace("\\{$prefix}\\wp_array_slice_assoc", '\\wp_array_slice_assoc', $contents);
        $contents = \str_replace("\\{$prefix}\\wp_json_encode", '\\wp_json_encode', $contents);
        $contents = \str_replace("\\{$prefix}\\get_transient", '\\get_transient', $contents);
        $contents = \str_replace("\\{$prefix}\\wp_cache_get", '\\wp_cache_get', $contents);
        $contents = \str_replace("\\{$prefix}\\set_transient", '\\set_transient', $contents);
        $contents = \str_replace("\\{$prefix}\\wp_cache_set", '\\wp_cache_set', $contents);
        $contents = \str_replace("\\{$prefix}\\wp_using_ext_object_cache", '\\wp_using_ext_object_cache', $contents);
        $contents = \str_replace("\\{$prefix}\\_doing_it_wrong", '\\_doing_it_wrong', $contents);
        $contents = \str_replace("\\{$prefix}\\plugin_dir_url", '\\plugin_dir_url', $contents);
        $contents = \str_replace("\\{$prefix}\\is_admin_bar_showing", '\\is_admin_bar_showing', $contents);
        $contents = \str_replace("\\{$prefix}\\get_bloginfo", '\\get_bloginfo', $contents);
        $contents = \str_replace("\\{$prefix}\\add_filter", '\\add_filter', $contents);
        $contents = \str_replace("\\{$prefix}\\add_action", '\\add_action', $contents);
        $contents = \str_replace("\\{$prefix}\\apply_filters", '\\apply_filters', $contents);
        $contents = \str_replace("\\{$prefix}\\add_query_arg", '\\add_query_arg', $contents);
        $contents = \str_replace("\\{$prefix}\\remove_query_arg", '\\remove_query_arg', $contents);
        $contents = \str_replace("\\{$prefix}\\get_post", '\\get_post', $contents);
        $contents = \str_replace("\\{$prefix}\\wp_scripts", '\\wp_scripts', $contents);
        $contents = \str_replace("\\{$prefix}\\wp_styles", '\\wp_styles', $contents);
        $contents = \str_replace("\\{$prefix}\\wp_style_is", '\\wp_style_is', $contents);
        $contents = \str_replace("\\{$prefix}\\WP_PLUGIN_URL", '\\WP_PLUGIN_URL', $contents);
        $contents = \str_replace("\\{$prefix}\\WPMU_PLUGIN_URL", '\\WPMU_PLUGIN_URL', $contents);
        $contents = \str_replace("\\{$prefix}\\wp_list_pluck", '\\wp_list_pluck', $contents);
        $contents = \str_replace("\\{$prefix}\\wp_array_slice_assoc", '\\wp_array_slice_assoc', $contents);
        $contents = \str_replace("\\{$prefix}\\wp_json_encode", '\\wp_json_encode', $contents);
        $contents = \str_replace("\\{$prefix}\\WP_Http", '\\WP_Http', $contents);
        $contents = \str_replace("\\{$prefix}\\WP_Error", '\\WP_Error', $contents);
        $contents = \str_replace("\\{$prefix}\\MINUTE_IN_SECONDS", '\\MINUTE_IN_SECONDS', $contents);
        $contents = \str_replace("\\{$prefix}\\wp_next_scheduled", '\\wp_next_scheduled', $contents);
        $contents = \str_replace("\\{$prefix}\\wp_remote_post", '\\wp_remote_post', $contents);
        $contents = \str_replace("\\{$prefix}\\wp_create_nonce", '\\wp_create_nonce', $contents);
        $contents = \str_replace("\\{$prefix}\\admin_url", '\\admin_url', $contents);
        $contents = \str_replace("\\{$prefix}\\check_ajax_referer", '\\check_ajax_referer', $contents);
        $contents = \str_replace("\\{$prefix}\\wp_die", '\\wp_die', $contents);
        $contents = \str_replace("\\{$prefix}\\wp_clear_scheduled_hook", '\\wp_clear_scheduled_hook', $contents);
        $contents = \str_replace("\\{$prefix}\\wp_unschedule_event", '\\wp_unschedule_event', $contents);
        $contents = \str_replace("\\{$prefix}\\wp_convert_hr_to_bytes", '\\wp_convert_hr_to_bytes', $contents);
        $contents = \str_replace("\\{$prefix}\\maybe_unserialize", '\\maybe_unserialize', $contents);
        $contents = \str_replace("\\{$prefix}\\delete_site_transient", '\\delete_site_transient', $contents);
        $contents = \str_replace("\\{$prefix}\\set_site_transient", '\\set_site_transient', $contents);
        $contents = \str_replace("\\{$prefix}\\get_site_transient", '\\get_site_transient', $contents);
        $contents = \str_replace("\\{$prefix}\\is_multisite", '\\is_multisite', $contents);
        $contents = \str_replace("\\{$prefix}\\update_site_option", '\\update_site_option', $contents);
        $contents = \str_replace("\\{$prefix}\\delete_site_option", '\\delete_site_option', $contents);
        $contents = \str_replace("\\{$prefix}\\wp_schedule_event", '\\wp_schedule_event', $contents);
        return $contents;
    }),
    'exclude-files' => array(),
    // list<string>
    'exclude-namespaces' => array('WP2FA', 'Composer'),
    // list<string|regex>
    'exclude-constants' => array(),
    // list<string|regex>
    'exclude-classes' => array(),
    // list<string|regex>
    'exclude-functions' => array(),
    // list<string|regex>
    'whitelist' => array('add_action'),
);
vendor/autoload.php000064400000001350150755130600010363 0ustar00<?php

// autoload.php @generated by Composer

if (PHP_VERSION_ID < 50600) {
    if (!headers_sent()) {
        header('HTTP/1.1 500 Internal Server Error');
    }
    $err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
    if (!ini_get('display_errors')) {
        if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
            fwrite(STDERR, $err);
        } elseif (!headers_sent()) {
            echo $err;
        }
    }
    trigger_error(
        $err,
        E_USER_ERROR
    );
}

require_once __DIR__ . '/composer/autoload_real.php';

return ComposerAutoloaderInit29692::getLoader();
vendor/symfony/property-access/Exception/NoSuchPropertyException.php000064400000000724150755130600022167 0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace WP2FA_Vendor\Symfony\Component\PropertyAccess\Exception;

/**
 * Thrown when a property cannot be found.
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class NoSuchPropertyException extends AccessException
{
}
vendor/symfony/property-access/Exception/UnexpectedTypeException.php000064400000002157150755130600022173 0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace WP2FA_Vendor\Symfony\Component\PropertyAccess\Exception;

use WP2FA_Vendor\Symfony\Component\PropertyAccess\PropertyPathInterface;
/**
 * Thrown when a value does not match an expected type.
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class UnexpectedTypeException extends RuntimeException
{
    /**
     * @param mixed $value     The unexpected value found while traversing property path
     * @param int   $pathIndex The property path index when the unexpected value was found
     */
    public function __construct($value, PropertyPathInterface $path, int $pathIndex)
    {
        $message = \sprintf('PropertyAccessor requires a graph of objects or arrays to operate on, ' . 'but it found type "%s" while trying to traverse path "%s" at property "%s".', \gettype($value), (string) $path, $path->getElement($pathIndex));
        parent::__construct($message);
    }
}
vendor/symfony/property-access/Exception/InvalidPropertyPathException.php000064400000000734150755130600023174 0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace WP2FA_Vendor\Symfony\Component\PropertyAccess\Exception;

/**
 * Thrown when a property path is malformed.
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class InvalidPropertyPathException extends RuntimeException
{
}
vendor/symfony/property-access/Exception/ExceptionInterface.php000064400000000731150755130600021121 0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace WP2FA_Vendor\Symfony\Component\PropertyAccess\Exception;

/**
 * Marker interface for the PropertyAccess component.
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
interface ExceptionInterface extends \Throwable
{
}
vendor/symfony/property-access/Exception/AccessException.php000064400000000736150755130600020427 0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace WP2FA_Vendor\Symfony\Component\PropertyAccess\Exception;

/**
 * Thrown when a property path is not available.
 *
 * @author Stéphane Escandell <stephane.escandell@gmail.com>
 */
class AccessException extends RuntimeException
{
}
vendor/symfony/property-access/Exception/InvalidArgumentException.php000064400000001025150755130600022307 0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace WP2FA_Vendor\Symfony\Component\PropertyAccess\Exception;

/**
 * Base InvalidArgumentException for the PropertyAccess component.
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
{
}
vendor/symfony/property-access/Exception/NoSuchIndexException.php000064400000000732150755130600021411 0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace WP2FA_Vendor\Symfony\Component\PropertyAccess\Exception;

/**
 * Thrown when an index cannot be found.
 *
 * @author Stéphane Escandell <stephane.escandell@gmail.com>
 */
class NoSuchIndexException extends AccessException
{
}
vendor/symfony/property-access/Exception/OutOfBoundsException.php000064400000001011150755130600021420 0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace WP2FA_Vendor\Symfony\Component\PropertyAccess\Exception;

/**
 * Base OutOfBoundsException for the PropertyAccess component.
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class OutOfBoundsException extends \OutOfBoundsException implements ExceptionInterface
{
}
vendor/symfony/property-access/Exception/RuntimeException.php000064400000000775150755130600020654 0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace WP2FA_Vendor\Symfony\Component\PropertyAccess\Exception;

/**
 * Base RuntimeException for the PropertyAccess component.
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class RuntimeException extends \RuntimeException implements ExceptionInterface
{
}
vendor/symfony/property-access/PropertyAccess.php000064400000001637150755130600016360 0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace WP2FA_Vendor\Symfony\Component\PropertyAccess;

/**
 * Entry point of the PropertyAccess component.
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
final class PropertyAccess
{
    /**
     * Creates a property accessor with the default configuration.
     */
    public static function createPropertyAccessor() : PropertyAccessor
    {
        return self::createPropertyAccessorBuilder()->getPropertyAccessor();
    }
    public static function createPropertyAccessorBuilder() : PropertyAccessorBuilder
    {
        return new PropertyAccessorBuilder();
    }
    /**
     * This class cannot be instantiated.
     */
    private function __construct()
    {
    }
}
vendor/symfony/property-access/PropertyPathInterface.php000064400000004410150755130600017664 0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace WP2FA_Vendor\Symfony\Component\PropertyAccess;

/**
 * A sequence of property names or array indices.
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
interface PropertyPathInterface extends \Traversable
{
    /**
     * Returns the string representation of the property path.
     *
     * @return string The path as string
     */
    public function __toString();
    /**
     * Returns the length of the property path, i.e. the number of elements.
     *
     * @return int The path length
     */
    public function getLength();
    /**
     * Returns the parent property path.
     *
     * The parent property path is the one that contains the same items as
     * this one except for the last one.
     *
     * If this property path only contains one item, null is returned.
     *
     * @return self|null The parent path or null
     */
    public function getParent();
    /**
     * Returns the elements of the property path as array.
     *
     * @return array An array of property/index names
     */
    public function getElements();
    /**
     * Returns the element at the given index in the property path.
     *
     * @param int $index The index key
     *
     * @return string A property or index name
     *
     * @throws Exception\OutOfBoundsException If the offset is invalid
     */
    public function getElement(int $index);
    /**
     * Returns whether the element at the given index is a property.
     *
     * @param int $index The index in the property path
     *
     * @return bool Whether the element at this index is a property
     *
     * @throws Exception\OutOfBoundsException If the offset is invalid
     */
    public function isProperty(int $index);
    /**
     * Returns whether the element at the given index is an array index.
     *
     * @param int $index The index in the property path
     *
     * @return bool Whether the element at this index is an array index
     *
     * @throws Exception\OutOfBoundsException If the offset is invalid
     */
    public function isIndex(int $index);
}
vendor/symfony/property-access/PropertyAccessorBuilder.php000064400000010170150755130600020220 0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace WP2FA_Vendor\Symfony\Component\PropertyAccess;

use WP2FA_Vendor\Psr\Cache\CacheItemPoolInterface;
/**
 * A configurable builder to create a PropertyAccessor.
 *
 * @author Jérémie Augustin <jeremie.augustin@pixel-cookers.com>
 */
class PropertyAccessorBuilder
{
    private $magicCall = \false;
    private $throwExceptionOnInvalidIndex = \false;
    private $throwExceptionOnInvalidPropertyPath = \true;
    /**
     * @var CacheItemPoolInterface|null
     */
    private $cacheItemPool;
    /**
     * Enables the use of "__call" by the PropertyAccessor.
     *
     * @return $this
     */
    public function enableMagicCall()
    {
        $this->magicCall = \true;
        return $this;
    }
    /**
     * Disables the use of "__call" by the PropertyAccessor.
     *
     * @return $this
     */
    public function disableMagicCall()
    {
        $this->magicCall = \false;
        return $this;
    }
    /**
     * @return bool whether the use of "__call" by the PropertyAccessor is enabled
     */
    public function isMagicCallEnabled()
    {
        return $this->magicCall;
    }
    /**
     * Enables exceptions when reading a non-existing index.
     *
     * This has no influence on writing non-existing indices with PropertyAccessorInterface::setValue()
     * which are always created on-the-fly.
     *
     * @return $this
     */
    public function enableExceptionOnInvalidIndex()
    {
        $this->throwExceptionOnInvalidIndex = \true;
        return $this;
    }
    /**
     * Disables exceptions when reading a non-existing index.
     *
     * Instead, null is returned when calling PropertyAccessorInterface::getValue() on a non-existing index.
     *
     * @return $this
     */
    public function disableExceptionOnInvalidIndex()
    {
        $this->throwExceptionOnInvalidIndex = \false;
        return $this;
    }
    /**
     * @return bool whether an exception is thrown or null is returned when reading a non-existing index
     */
    public function isExceptionOnInvalidIndexEnabled()
    {
        return $this->throwExceptionOnInvalidIndex;
    }
    /**
     * Enables exceptions when reading a non-existing property.
     *
     * This has no influence on writing non-existing indices with PropertyAccessorInterface::setValue()
     * which are always created on-the-fly.
     *
     * @return $this
     */
    public function enableExceptionOnInvalidPropertyPath()
    {
        $this->throwExceptionOnInvalidPropertyPath = \true;
        return $this;
    }
    /**
     * Disables exceptions when reading a non-existing index.
     *
     * Instead, null is returned when calling PropertyAccessorInterface::getValue() on a non-existing index.
     *
     * @return $this
     */
    public function disableExceptionOnInvalidPropertyPath()
    {
        $this->throwExceptionOnInvalidPropertyPath = \false;
        return $this;
    }
    /**
     * @return bool whether an exception is thrown or null is returned when reading a non-existing property
     */
    public function isExceptionOnInvalidPropertyPath()
    {
        return $this->throwExceptionOnInvalidPropertyPath;
    }
    /**
     * Sets a cache system.
     *
     * @return PropertyAccessorBuilder The builder object
     */
    public function setCacheItemPool(CacheItemPoolInterface $cacheItemPool = null)
    {
        $this->cacheItemPool = $cacheItemPool;
        return $this;
    }
    /**
     * Gets the used cache system.
     *
     * @return CacheItemPoolInterface|null
     */
    public function getCacheItemPool()
    {
        return $this->cacheItemPool;
    }
    /**
     * Builds and returns a new PropertyAccessor object.
     *
     * @return PropertyAccessorInterface The built PropertyAccessor
     */
    public function getPropertyAccessor()
    {
        return new PropertyAccessor($this->magicCall, $this->throwExceptionOnInvalidIndex, $this->cacheItemPool, $this->throwExceptionOnInvalidPropertyPath);
    }
}
vendor/symfony/property-access/PropertyAccessorInterface.php000064400000011045150755130600020534 0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace WP2FA_Vendor\Symfony\Component\PropertyAccess;

/**
 * Writes and reads values to/from an object/array graph.
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
interface PropertyAccessorInterface
{
    /**
     * Sets the value at the end of the property path of the object graph.
     *
     * Example:
     *
     *     use Symfony\Component\PropertyAccess\PropertyAccess;
     *
     *     $propertyAccessor = PropertyAccess::createPropertyAccessor();
     *
     *     echo $propertyAccessor->setValue($object, 'child.name', 'Fabien');
     *     // equals echo $object->getChild()->setName('Fabien');
     *
     * This method first tries to find a public setter for each property in the
     * path. The name of the setter must be the camel-cased property name
     * prefixed with "set".
     *
     * If the setter does not exist, this method tries to find a public
     * property. The value of the property is then changed.
     *
     * If neither is found, an exception is thrown.
     *
     * @param object|array                 $objectOrArray The object or array to modify
     * @param string|PropertyPathInterface $propertyPath  The property path to modify
     * @param mixed                        $value         The value to set at the end of the property path
     *
     * @throws Exception\InvalidArgumentException If the property path is invalid
     * @throws Exception\AccessException          If a property/index does not exist or is not public
     * @throws Exception\UnexpectedTypeException  If a value within the path is neither object nor array
     */
    public function setValue(&$objectOrArray, $propertyPath, $value);
    /**
     * Returns the value at the end of the property path of the object graph.
     *
     * Example:
     *
     *     use Symfony\Component\PropertyAccess\PropertyAccess;
     *
     *     $propertyAccessor = PropertyAccess::createPropertyAccessor();
     *
     *     echo $propertyAccessor->getValue($object, 'child.name');
     *     // equals echo $object->getChild()->getName();
     *
     * This method first tries to find a public getter for each property in the
     * path. The name of the getter must be the camel-cased property name
     * prefixed with "get", "is", or "has".
     *
     * If the getter does not exist, this method tries to find a public
     * property. The value of the property is then returned.
     *
     * If none of them are found, an exception is thrown.
     *
     * @param object|array                 $objectOrArray The object or array to traverse
     * @param string|PropertyPathInterface $propertyPath  The property path to read
     *
     * @return mixed The value at the end of the property path
     *
     * @throws Exception\InvalidArgumentException If the property path is invalid
     * @throws Exception\AccessException          If a property/index does not exist or is not public
     * @throws Exception\UnexpectedTypeException  If a value within the path is neither object
     *                                            nor array
     */
    public function getValue($objectOrArray, $propertyPath);
    /**
     * Returns whether a value can be written at a given property path.
     *
     * Whenever this method returns true, {@link setValue()} is guaranteed not
     * to throw an exception when called with the same arguments.
     *
     * @param object|array                 $objectOrArray The object or array to check
     * @param string|PropertyPathInterface $propertyPath  The property path to check
     *
     * @return bool Whether the value can be set
     *
     * @throws Exception\InvalidArgumentException If the property path is invalid
     */
    public function isWritable($objectOrArray, $propertyPath);
    /**
     * Returns whether a property path can be read from an object graph.
     *
     * Whenever this method returns true, {@link getValue()} is guaranteed not
     * to throw an exception when called with the same arguments.
     *
     * @param object|array                 $objectOrArray The object or array to check
     * @param string|PropertyPathInterface $propertyPath  The property path to check
     *
     * @return bool Whether the property path can be read
     *
     * @throws Exception\InvalidArgumentException If the property path is invalid
     */
    public function isReadable($objectOrArray, $propertyPath);
}
vendor/symfony/property-access/PropertyPathIterator.php000064400000001757150755130600017570 0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace WP2FA_Vendor\Symfony\Component\PropertyAccess;

/**
 * Traverses a property path and provides additional methods to find out
 * information about the current element.
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class PropertyPathIterator extends \ArrayIterator implements PropertyPathIteratorInterface
{
    protected $path;
    public function __construct(PropertyPathInterface $path)
    {
        parent::__construct($path->getElements());
        $this->path = $path;
    }
    /**
     * {@inheritdoc}
     */
    public function isIndex()
    {
        return $this->path->isIndex($this->key());
    }
    /**
     * {@inheritdoc}
     */
    public function isProperty()
    {
        return $this->path->isProperty($this->key());
    }
}
vendor/symfony/property-access/PropertyPath.php000064400000012742150755130600016052 0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace WP2FA_Vendor\Symfony\Component\PropertyAccess;

use WP2FA_Vendor\Symfony\Component\PropertyAccess\Exception\InvalidArgumentException;
use WP2FA_Vendor\Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException;
use WP2FA_Vendor\Symfony\Component\PropertyAccess\Exception\OutOfBoundsException;
/**
 * Default implementation of {@link PropertyPathInterface}.
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class PropertyPath implements \IteratorAggregate, PropertyPathInterface
{
    /**
     * Character used for separating between plural and singular of an element.
     */
    const SINGULAR_SEPARATOR = '|';
    /**
     * The elements of the property path.
     *
     * @var array
     */
    private $elements = [];
    /**
     * The number of elements in the property path.
     *
     * @var int
     */
    private $length;
    /**
     * Contains a Boolean for each property in $elements denoting whether this
     * element is an index. It is a property otherwise.
     *
     * @var array
     */
    private $isIndex = [];
    /**
     * String representation of the path.
     *
     * @var string
     */
    private $pathAsString;
    /**
     * Constructs a property path from a string.
     *
     * @param PropertyPath|string $propertyPath The property path as string or instance
     *
     * @throws InvalidArgumentException     If the given path is not a string
     * @throws InvalidPropertyPathException If the syntax of the property path is not valid
     */
    public function __construct($propertyPath)
    {
        // Can be used as copy constructor
        if ($propertyPath instanceof self) {
            /* @var PropertyPath $propertyPath */
            $this->elements = $propertyPath->elements;
            $this->length = $propertyPath->length;
            $this->isIndex = $propertyPath->isIndex;
            $this->pathAsString = $propertyPath->pathAsString;
            return;
        }
        if (!\is_string($propertyPath)) {
            throw new InvalidArgumentException(\sprintf('The property path constructor needs a string or an instance of "Symfony\\Component\\PropertyAccess\\PropertyPath". Got: "%s".', \is_object($propertyPath) ? \get_class($propertyPath) : \gettype($propertyPath)));
        }
        if ('' === $propertyPath) {
            throw new InvalidPropertyPathException('The property path should not be empty.');
        }
        $this->pathAsString = $propertyPath;
        $position = 0;
        $remaining = $propertyPath;
        // first element is evaluated differently - no leading dot for properties
        $pattern = '/^(([^\\.\\[]++)|\\[([^\\]]++)\\])(.*)/';
        while (\preg_match($pattern, $remaining, $matches)) {
            if ('' !== $matches[2]) {
                $element = $matches[2];
                $this->isIndex[] = \false;
            } else {
                $element = $matches[3];
                $this->isIndex[] = \true;
            }
            $this->elements[] = $element;
            $position += \strlen($matches[1]);
            $remaining = $matches[4];
            $pattern = '/^(\\.([^\\.|\\[]++)|\\[([^\\]]++)\\])(.*)/';
        }
        if ('' !== $remaining) {
            throw new InvalidPropertyPathException(\sprintf('Could not parse property path "%s". Unexpected token "%s" at position %d.', $propertyPath, $remaining[0], $position));
        }
        $this->length = \count($this->elements);
    }
    /**
     * {@inheritdoc}
     */
    public function __toString()
    {
        return $this->pathAsString;
    }
    /**
     * {@inheritdoc}
     */
    public function getLength()
    {
        return $this->length;
    }
    /**
     * {@inheritdoc}
     */
    public function getParent()
    {
        if ($this->length <= 1) {
            return null;
        }
        $parent = clone $this;
        --$parent->length;
        $parent->pathAsString = \substr($parent->pathAsString, 0, \max(\strrpos($parent->pathAsString, '.'), \strrpos($parent->pathAsString, '[')));
        \array_pop($parent->elements);
        \array_pop($parent->isIndex);
        return $parent;
    }
    /**
     * Returns a new iterator for this path.
     *
     * @return PropertyPathIteratorInterface
     */
    public function getIterator()
    {
        return new PropertyPathIterator($this);
    }
    /**
     * {@inheritdoc}
     */
    public function getElements()
    {
        return $this->elements;
    }
    /**
     * {@inheritdoc}
     */
    public function getElement(int $index)
    {
        if (!isset($this->elements[$index])) {
            throw new OutOfBoundsException(\sprintf('The index "%s" is not within the property path.', $index));
        }
        return $this->elements[$index];
    }
    /**
     * {@inheritdoc}
     */
    public function isProperty(int $index)
    {
        if (!isset($this->isIndex[$index])) {
            throw new OutOfBoundsException(\sprintf('The index "%s" is not within the property path.', $index));
        }
        return !$this->isIndex[$index];
    }
    /**
     * {@inheritdoc}
     */
    public function isIndex(int $index)
    {
        if (!isset($this->isIndex[$index])) {
            throw new OutOfBoundsException(\sprintf('The index "%s" is not within the property path.', $index));
        }
        return $this->isIndex[$index];
    }
}
vendor/symfony/property-access/PropertyAccessor.php000064400000112051150755130600016712 0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace WP2FA_Vendor\Symfony\Component\PropertyAccess;

use WP2FA_Vendor\Psr\Cache\CacheItemPoolInterface;
use WP2FA_Vendor\Psr\Log\LoggerInterface;
use WP2FA_Vendor\Psr\Log\NullLogger;
use WP2FA_Vendor\Symfony\Component\Cache\Adapter\AdapterInterface;
use WP2FA_Vendor\Symfony\Component\Cache\Adapter\ApcuAdapter;
use WP2FA_Vendor\Symfony\Component\Cache\Adapter\NullAdapter;
use WP2FA_Vendor\Symfony\Component\Inflector\Inflector;
use WP2FA_Vendor\Symfony\Component\PropertyAccess\Exception\AccessException;
use WP2FA_Vendor\Symfony\Component\PropertyAccess\Exception\InvalidArgumentException;
use WP2FA_Vendor\Symfony\Component\PropertyAccess\Exception\NoSuchIndexException;
use WP2FA_Vendor\Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
use WP2FA_Vendor\Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException;
/**
 * Default implementation of {@link PropertyAccessorInterface}.
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 * @author Kévin Dunglas <dunglas@gmail.com>
 * @author Nicolas Grekas <p@tchwork.com>
 */
class PropertyAccessor implements PropertyAccessorInterface
{
    private const VALUE = 0;
    private const REF = 1;
    private const IS_REF_CHAINED = 2;
    private const ACCESS_HAS_PROPERTY = 0;
    private const ACCESS_TYPE = 1;
    private const ACCESS_NAME = 2;
    private const ACCESS_REF = 3;
    private const ACCESS_ADDER = 4;
    private const ACCESS_REMOVER = 5;
    private const ACCESS_TYPE_METHOD = 0;
    private const ACCESS_TYPE_PROPERTY = 1;
    private const ACCESS_TYPE_MAGIC = 2;
    private const ACCESS_TYPE_ADDER_AND_REMOVER = 3;
    private const ACCESS_TYPE_NOT_FOUND = 4;
    private const CACHE_PREFIX_READ = 'r';
    private const CACHE_PREFIX_WRITE = 'w';
    private const CACHE_PREFIX_PROPERTY_PATH = 'p';
    /**
     * @var bool
     */
    private $magicCall;
    private $ignoreInvalidIndices;
    private $ignoreInvalidProperty;
    /**
     * @var CacheItemPoolInterface
     */
    private $cacheItemPool;
    private $propertyPathCache = [];
    private $readPropertyCache = [];
    private $writePropertyCache = [];
    private static $resultProto = [self::VALUE => null];
    /**
     * Should not be used by application code. Use
     * {@link PropertyAccess::createPropertyAccessor()} instead.
     */
    public function __construct(bool $magicCall = \false, bool $throwExceptionOnInvalidIndex = \false, CacheItemPoolInterface $cacheItemPool = null, bool $throwExceptionOnInvalidPropertyPath = \true)
    {
        $this->magicCall = $magicCall;
        $this->ignoreInvalidIndices = !$throwExceptionOnInvalidIndex;
        $this->cacheItemPool = $cacheItemPool instanceof NullAdapter ? null : $cacheItemPool;
        // Replace the NullAdapter by the null value
        $this->ignoreInvalidProperty = !$throwExceptionOnInvalidPropertyPath;
    }
    /**
     * {@inheritdoc}
     */
    public function getValue($objectOrArray, $propertyPath)
    {
        $zval = [self::VALUE => $objectOrArray];
        if (\is_object($objectOrArray) && \false === \strpbrk((string) $propertyPath, '.[')) {
            return $this->readProperty($zval, $propertyPath, $this->ignoreInvalidProperty)[self::VALUE];
        }
        $propertyPath = $this->getPropertyPath($propertyPath);
        $propertyValues = $this->readPropertiesUntil($zval, $propertyPath, $propertyPath->getLength(), $this->ignoreInvalidIndices);
        return $propertyValues[\count($propertyValues) - 1][self::VALUE];
    }
    /**
     * {@inheritdoc}
     */
    public function setValue(&$objectOrArray, $propertyPath, $value)
    {
        if (\is_object($objectOrArray) && \false === \strpbrk((string) $propertyPath, '.[')) {
            $zval = [self::VALUE => $objectOrArray];
            try {
                $this->writeProperty($zval, $propertyPath, $value);
                return;
            } catch (\TypeError $e) {
                self::throwInvalidArgumentException($e->getMessage(), $e->getTrace(), 0, $propertyPath);
                // It wasn't thrown in this class so rethrow it
                throw $e;
            }
        }
        $propertyPath = $this->getPropertyPath($propertyPath);
        $zval = [self::VALUE => $objectOrArray, self::REF => &$objectOrArray];
        $propertyValues = $this->readPropertiesUntil($zval, $propertyPath, $propertyPath->getLength() - 1);
        $overwrite = \true;
        try {
            for ($i = \count($propertyValues) - 1; 0 <= $i; --$i) {
                $zval = $propertyValues[$i];
                unset($propertyValues[$i]);
                // You only need set value for current element if:
                // 1. it's the parent of the last index element
                // OR
                // 2. its child is not passed by reference
                //
                // This may avoid uncessary value setting process for array elements.
                // For example:
                // '[a][b][c]' => 'old-value'
                // If you want to change its value to 'new-value',
                // you only need set value for '[a][b][c]' and it's safe to ignore '[a][b]' and '[a]'
                if ($overwrite) {
                    $property = $propertyPath->getElement($i);
                    if ($propertyPath->isIndex($i)) {
                        if ($overwrite = !isset($zval[self::REF])) {
                            $ref =& $zval[self::REF];
                            $ref = $zval[self::VALUE];
                        }
                        $this->writeIndex($zval, $property, $value);
                        if ($overwrite) {
                            $zval[self::VALUE] = $zval[self::REF];
                        }
                    } else {
                        $this->writeProperty($zval, $property, $value);
                    }
                    // if current element is an object
                    // OR
                    // if current element's reference chain is not broken - current element
                    // as well as all its ancients in the property path are all passed by reference,
                    // then there is no need to continue the value setting process
                    if (\is_object($zval[self::VALUE]) || isset($zval[self::IS_REF_CHAINED])) {
                        break;
                    }
                }
                $value = $zval[self::VALUE];
            }
        } catch (\TypeError $e) {
            self::throwInvalidArgumentException($e->getMessage(), $e->getTrace(), 0, $propertyPath, $e);
            // It wasn't thrown in this class so rethrow it
            throw $e;
        }
    }
    private static function throwInvalidArgumentException(string $message, array $trace, int $i, string $propertyPath, \Throwable $previous = null) : void
    {
        if (!isset($trace[$i]['file']) || __FILE__ !== $trace[$i]['file']) {
            return;
        }
        if (\PHP_VERSION_ID < 80000) {
            if (0 !== \strpos($message, 'Argument ')) {
                return;
            }
            $pos = \strpos($message, $delim = 'must be of the type ') ?: (\strpos($message, $delim = 'must be an instance of ') ?: \strpos($message, $delim = 'must implement interface '));
            $pos += \strlen($delim);
            $j = \strpos($message, ',', $pos);
            $type = \substr($message, 2 + $j, \strpos($message, ' given', $j) - $j - 2);
            $message = \substr($message, $pos, $j - $pos);
            throw new InvalidArgumentException(\sprintf('Expected argument of type "%s", "%s" given at property path "%s".', $message, 'NULL' === $type ? 'null' : $type, $propertyPath), 0, $previous);
        }
        if (\preg_match('/^\\S+::\\S+\\(\\): Argument #\\d+ \\(\\$\\S+\\) must be of type (\\S+), (\\S+) given/', $message, $matches)) {
            list(, $expectedType, $actualType) = $matches;
            throw new InvalidArgumentException(\sprintf('Expected argument of type "%s", "%s" given at property path "%s".', $expectedType, 'NULL' === $actualType ? 'null' : $actualType, $propertyPath), 0, $previous);
        }
    }
    /**
     * {@inheritdoc}
     */
    public function isReadable($objectOrArray, $propertyPath)
    {
        if (!$propertyPath instanceof PropertyPathInterface) {
            $propertyPath = new PropertyPath($propertyPath);
        }
        try {
            $zval = [self::VALUE => $objectOrArray];
            $this->readPropertiesUntil($zval, $propertyPath, $propertyPath->getLength(), $this->ignoreInvalidIndices);
            return \true;
        } catch (AccessException $e) {
            return \false;
        } catch (UnexpectedTypeException $e) {
            return \false;
        }
    }
    /**
     * {@inheritdoc}
     */
    public function isWritable($objectOrArray, $propertyPath)
    {
        $propertyPath = $this->getPropertyPath($propertyPath);
        try {
            $zval = [self::VALUE => $objectOrArray];
            $propertyValues = $this->readPropertiesUntil($zval, $propertyPath, $propertyPath->getLength() - 1);
            for ($i = \count($propertyValues) - 1; 0 <= $i; --$i) {
                $zval = $propertyValues[$i];
                unset($propertyValues[$i]);
                if ($propertyPath->isIndex($i)) {
                    if (!$zval[self::VALUE] instanceof \ArrayAccess && !\is_array($zval[self::VALUE])) {
                        return \false;
                    }
                } else {
                    if (!$this->isPropertyWritable($zval[self::VALUE], $propertyPath->getElement($i))) {
                        return \false;
                    }
                }
                if (\is_object($zval[self::VALUE])) {
                    return \true;
                }
            }
            return \true;
        } catch (AccessException $e) {
            return \false;
        } catch (UnexpectedTypeException $e) {
            return \false;
        }
    }
    /**
     * Reads the path from an object up to a given path index.
     *
     * @throws UnexpectedTypeException if a value within the path is neither object nor array
     * @throws NoSuchIndexException    If a non-existing index is accessed
     */
    private function readPropertiesUntil(array $zval, PropertyPathInterface $propertyPath, int $lastIndex, bool $ignoreInvalidIndices = \true) : array
    {
        if (!\is_object($zval[self::VALUE]) && !\is_array($zval[self::VALUE])) {
            throw new UnexpectedTypeException($zval[self::VALUE], $propertyPath, 0);
        }
        // Add the root object to the list
        $propertyValues = [$zval];
        for ($i = 0; $i < $lastIndex; ++$i) {
            $property = $propertyPath->getElement($i);
            $isIndex = $propertyPath->isIndex($i);
            if ($isIndex) {
                // Create missing nested arrays on demand
                if ($zval[self::VALUE] instanceof \ArrayAccess && !$zval[self::VALUE]->offsetExists($property) || \is_array($zval[self::VALUE]) && !isset($zval[self::VALUE][$property]) && !\array_key_exists($property, $zval[self::VALUE])) {
                    if (!$ignoreInvalidIndices) {
                        if (!\is_array($zval[self::VALUE])) {
                            if (!$zval[self::VALUE] instanceof \Traversable) {
                                throw new NoSuchIndexException(\sprintf('Cannot read index "%s" while trying to traverse path "%s".', $property, (string) $propertyPath));
                            }
                            $zval[self::VALUE] = \iterator_to_array($zval[self::VALUE]);
                        }
                        throw new NoSuchIndexException(\sprintf('Cannot read index "%s" while trying to traverse path "%s". Available indices are "%s".', $property, (string) $propertyPath, \print_r(\array_keys($zval[self::VALUE]), \true)));
                    }
                    if ($i + 1 < $propertyPath->getLength()) {
                        if (isset($zval[self::REF])) {
                            $zval[self::VALUE][$property] = [];
                            $zval[self::REF] = $zval[self::VALUE];
                        } else {
                            $zval[self::VALUE] = [$property => []];
                        }
                    }
                }
                $zval = $this->readIndex($zval, $property);
            } else {
                $zval = $this->readProperty($zval, $property, $this->ignoreInvalidProperty);
            }
            // the final value of the path must not be validated
            if ($i + 1 < $propertyPath->getLength() && !\is_object($zval[self::VALUE]) && !\is_array($zval[self::VALUE])) {
                throw new UnexpectedTypeException($zval[self::VALUE], $propertyPath, $i + 1);
            }
            if (isset($zval[self::REF]) && (0 === $i || isset($propertyValues[$i - 1][self::IS_REF_CHAINED]))) {
                // Set the IS_REF_CHAINED flag to true if:
                // current property is passed by reference and
                // it is the first element in the property path or
                // the IS_REF_CHAINED flag of its parent element is true
                // Basically, this flag is true only when the reference chain from the top element to current element is not broken
                $zval[self::IS_REF_CHAINED] = \true;
            }
            $propertyValues[] = $zval;
        }
        return $propertyValues;
    }
    /**
     * Reads a key from an array-like structure.
     *
     * @param string|int $index The key to read
     *
     * @throws NoSuchIndexException If the array does not implement \ArrayAccess or it is not an array
     */
    private function readIndex(array $zval, $index) : array
    {
        if (!$zval[self::VALUE] instanceof \ArrayAccess && !\is_array($zval[self::VALUE])) {
            throw new NoSuchIndexException(\sprintf('Cannot read index "%s" from object of type "%s" because it doesn\'t implement \\ArrayAccess.', $index, \get_class($zval[self::VALUE])));
        }
        $result = self::$resultProto;
        if (isset($zval[self::VALUE][$index])) {
            $result[self::VALUE] = $zval[self::VALUE][$index];
            if (!isset($zval[self::REF])) {
                // Save creating references when doing read-only lookups
            } elseif (\is_array($zval[self::VALUE])) {
                $result[self::REF] =& $zval[self::REF][$index];
            } elseif (\is_object($result[self::VALUE])) {
                $result[self::REF] = $result[self::VALUE];
            }
        }
        return $result;
    }
    /**
     * Reads the a property from an object.
     *
     * @throws NoSuchPropertyException If $ignoreInvalidProperty is false and the property does not exist or is not public
     */
    private function readProperty(array $zval, string $property, bool $ignoreInvalidProperty = \false) : array
    {
        if (!\is_object($zval[self::VALUE])) {
            throw new NoSuchPropertyException(\sprintf('Cannot read property "%s" from an array. Maybe you intended to write the property path as "[%1$s]" instead.', $property));
        }
        $result = self::$resultProto;
        $object = $zval[self::VALUE];
        $access = $this->getReadAccessInfo(\get_class($object), $property);
        try {
            if (self::ACCESS_TYPE_METHOD === $access[self::ACCESS_TYPE]) {
                try {
                    $result[self::VALUE] = $object->{$access[self::ACCESS_NAME]}();
                } catch (\TypeError $e) {
                    list($trace) = $e->getTrace();
                    // handle uninitialized properties in PHP >= 7
                    if (__FILE__ === $trace['file'] && $access[self::ACCESS_NAME] === $trace['function'] && $object instanceof $trace['class'] && \preg_match(\sprintf('/Return value (?:of .*::\\w+\\(\\) )?must be of (?:the )?type (\\w+), null returned$/'), $e->getMessage(), $matches)) {
                        throw new AccessException(\sprintf('The method "%s::%s()" returned "null", but expected type "%3$s". Did you forget to initialize a property or to make the return type nullable using "?%3$s"?', \false === \strpos(\get_class($object), "@anonymous\x00") ? \get_class($object) : (\get_parent_class($object) ?: 'class') . '@anonymous', $access[self::ACCESS_NAME], $matches[1]), 0, $e);
                    }
                    throw $e;
                }
            } elseif (self::ACCESS_TYPE_PROPERTY === $access[self::ACCESS_TYPE]) {
                $result[self::VALUE] = $object->{$access[self::ACCESS_NAME]};
                if ($access[self::ACCESS_REF] && isset($zval[self::REF])) {
                    $result[self::REF] =& $object->{$access[self::ACCESS_NAME]};
                }
            } elseif (!$access[self::ACCESS_HAS_PROPERTY] && \property_exists($object, $property)) {
                // Needed to support \stdClass instances. We need to explicitly
                // exclude $access[self::ACCESS_HAS_PROPERTY], otherwise if
                // a *protected* property was found on the class, property_exists()
                // returns true, consequently the following line will result in a
                // fatal error.
                $result[self::VALUE] = $object->{$property};
                if (isset($zval[self::REF])) {
                    $result[self::REF] =& $object->{$property};
                }
            } elseif (self::ACCESS_TYPE_MAGIC === $access[self::ACCESS_TYPE]) {
                // we call the getter and hope the __call do the job
                $result[self::VALUE] = $object->{$access[self::ACCESS_NAME]}();
            } elseif (!$ignoreInvalidProperty) {
                throw new NoSuchPropertyException($access[self::ACCESS_NAME]);
            }
        } catch (\Error $e) {
            // handle uninitialized properties in PHP >= 7.4
            if (\PHP_VERSION_ID >= 70400 && \preg_match('/^Typed property ([\\w\\\\]+)::\\$(\\w+) must not be accessed before initialization$/', $e->getMessage(), $matches)) {
                $r = new \ReflectionProperty($matches[1], $matches[2]);
                $type = ($type = $r->getType()) instanceof \ReflectionNamedType ? $type->getName() : (string) $type;
                throw new AccessException(\sprintf('The property "%s::$%s" is not readable because it is typed "%s". You should initialize it or declare a default value instead.', $r->getDeclaringClass()->getName(), $r->getName(), $type), 0, $e);
            }
            throw $e;
        }
        // Objects are always passed around by reference
        if (isset($zval[self::REF]) && \is_object($result[self::VALUE])) {
            $result[self::REF] = $result[self::VALUE];
        }
        return $result;
    }
    /**
     * Guesses how to read the property value.
     */
    private function getReadAccessInfo(string $class, string $property) : array
    {
        $key = \str_replace('\\', '.', $class) . '..' . $property;
        if (isset($this->readPropertyCache[$key])) {
            return $this->readPropertyCache[$key];
        }
        if ($this->cacheItemPool) {
            $item = $this->cacheItemPool->getItem(self::CACHE_PREFIX_READ . \rawurlencode($key));
            if ($item->isHit()) {
                return $this->readPropertyCache[$key] = $item->get();
            }
        }
        $access = [];
        $reflClass = new \ReflectionClass($class);
        $access[self::ACCESS_HAS_PROPERTY] = $reflClass->hasProperty($property);
        $camelProp = $this->camelize($property);
        $getter = 'get' . $camelProp;
        $getsetter = \lcfirst($camelProp);
        // jQuery style, e.g. read: last(), write: last($item)
        $isser = 'is' . $camelProp;
        $hasser = 'has' . $camelProp;
        $canAccessor = 'can' . $camelProp;
        if ($reflClass->hasMethod($getter) && $reflClass->getMethod($getter)->isPublic()) {
            $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD;
            $access[self::ACCESS_NAME] = $getter;
        } elseif ($reflClass->hasMethod($getsetter) && $reflClass->getMethod($getsetter)->isPublic()) {
            $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD;
            $access[self::ACCESS_NAME] = $getsetter;
        } elseif ($reflClass->hasMethod($isser) && $reflClass->getMethod($isser)->isPublic()) {
            $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD;
            $access[self::ACCESS_NAME] = $isser;
        } elseif ($reflClass->hasMethod($hasser) && $reflClass->getMethod($hasser)->isPublic()) {
            $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD;
            $access[self::ACCESS_NAME] = $hasser;
        } elseif ($reflClass->hasMethod($canAccessor) && $reflClass->getMethod($canAccessor)->isPublic()) {
            $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD;
            $access[self::ACCESS_NAME] = $canAccessor;
        } elseif ($reflClass->hasMethod('__get') && $reflClass->getMethod('__get')->isPublic()) {
            $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_PROPERTY;
            $access[self::ACCESS_NAME] = $property;
            $access[self::ACCESS_REF] = \false;
        } elseif ($access[self::ACCESS_HAS_PROPERTY] && $reflClass->getProperty($property)->isPublic()) {
            $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_PROPERTY;
            $access[self::ACCESS_NAME] = $property;
            $access[self::ACCESS_REF] = \true;
        } elseif ($this->magicCall && $reflClass->hasMethod('__call') && $reflClass->getMethod('__call')->isPublic()) {
            // we call the getter and hope the __call do the job
            $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_MAGIC;
            $access[self::ACCESS_NAME] = $getter;
        } else {
            $methods = [$getter, $getsetter, $isser, $hasser, '__get'];
            if ($this->magicCall) {
                $methods[] = '__call';
            }
            $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_NOT_FOUND;
            $access[self::ACCESS_NAME] = \sprintf('Neither the property "%s" nor one of the methods "%s()" ' . 'exist and have public access in class "%s".', $property, \implode('()", "', $methods), $reflClass->name);
        }
        if (isset($item)) {
            $this->cacheItemPool->save($item->set($access));
        }
        return $this->readPropertyCache[$key] = $access;
    }
    /**
     * Sets the value of an index in a given array-accessible value.
     *
     * @param string|int $index The index to write at
     * @param mixed      $value The value to write
     *
     * @throws NoSuchIndexException If the array does not implement \ArrayAccess or it is not an array
     */
    private function writeIndex(array $zval, $index, $value)
    {
        if (!$zval[self::VALUE] instanceof \ArrayAccess && !\is_array($zval[self::VALUE])) {
            throw new NoSuchIndexException(\sprintf('Cannot modify index "%s" in object of type "%s" because it doesn\'t implement \\ArrayAccess.', $index, \get_class($zval[self::VALUE])));
        }
        $zval[self::REF][$index] = $value;
    }
    /**
     * Sets the value of a property in the given object.
     *
     * @param mixed $value The value to write
     *
     * @throws NoSuchPropertyException if the property does not exist or is not public
     */
    private function writeProperty(array $zval, string $property, $value)
    {
        if (!\is_object($zval[self::VALUE])) {
            throw new NoSuchPropertyException(\sprintf('Cannot write property "%s" to an array. Maybe you should write the property path as "[%1$s]" instead?', $property));
        }
        $object = $zval[self::VALUE];
        $access = $this->getWriteAccessInfo(\get_class($object), $property, $value);
        if (self::ACCESS_TYPE_METHOD === $access[self::ACCESS_TYPE]) {
            $object->{$access[self::ACCESS_NAME]}($value);
        } elseif (self::ACCESS_TYPE_PROPERTY === $access[self::ACCESS_TYPE]) {
            $object->{$access[self::ACCESS_NAME]} = $value;
        } elseif (self::ACCESS_TYPE_ADDER_AND_REMOVER === $access[self::ACCESS_TYPE]) {
            $this->writeCollection($zval, $property, $value, $access[self::ACCESS_ADDER], $access[self::ACCESS_REMOVER]);
        } elseif (!$access[self::ACCESS_HAS_PROPERTY] && \property_exists($object, $property)) {
            // Needed to support \stdClass instances. We need to explicitly
            // exclude $access[self::ACCESS_HAS_PROPERTY], otherwise if
            // a *protected* property was found on the class, property_exists()
            // returns true, consequently the following line will result in a
            // fatal error.
            $object->{$property} = $value;
        } elseif (self::ACCESS_TYPE_MAGIC === $access[self::ACCESS_TYPE]) {
            $object->{$access[self::ACCESS_NAME]}($value);
        } elseif (self::ACCESS_TYPE_NOT_FOUND === $access[self::ACCESS_TYPE]) {
            throw new NoSuchPropertyException(\sprintf('Could not determine access type for property "%s" in class "%s"%s.', $property, \get_class($object), isset($access[self::ACCESS_NAME]) ? ': ' . $access[self::ACCESS_NAME] : '.'));
        } else {
            throw new NoSuchPropertyException($access[self::ACCESS_NAME]);
        }
    }
    /**
     * Adjusts a collection-valued property by calling add*() and remove*() methods.
     */
    private function writeCollection(array $zval, string $property, iterable $collection, string $addMethod, string $removeMethod)
    {
        // At this point the add and remove methods have been found
        $previousValue = $this->readProperty($zval, $property);
        $previousValue = $previousValue[self::VALUE];
        if ($previousValue instanceof \Traversable) {
            $previousValue = \iterator_to_array($previousValue);
        }
        if ($previousValue && \is_array($previousValue)) {
            if (\is_object($collection)) {
                $collection = \iterator_to_array($collection);
            }
            foreach ($previousValue as $key => $item) {
                if (!\in_array($item, $collection, \true)) {
                    unset($previousValue[$key]);
                    $zval[self::VALUE]->{$removeMethod}($item);
                }
            }
        } else {
            $previousValue = \false;
        }
        foreach ($collection as $item) {
            if (!$previousValue || !\in_array($item, $previousValue, \true)) {
                $zval[self::VALUE]->{$addMethod}($item);
            }
        }
    }
    /**
     * Guesses how to write the property value.
     *
     * @param mixed $value
     */
    private function getWriteAccessInfo(string $class, string $property, $value) : array
    {
        $useAdderAndRemover = \is_array($value) || $value instanceof \Traversable;
        $key = \str_replace('\\', '.', $class) . '..' . $property . '..' . (int) $useAdderAndRemover;
        if (isset($this->writePropertyCache[$key])) {
            return $this->writePropertyCache[$key];
        }
        if ($this->cacheItemPool) {
            $item = $this->cacheItemPool->getItem(self::CACHE_PREFIX_WRITE . \rawurlencode($key));
            if ($item->isHit()) {
                return $this->writePropertyCache[$key] = $item->get();
            }
        }
        $access = [];
        $reflClass = new \ReflectionClass($class);
        $access[self::ACCESS_HAS_PROPERTY] = $reflClass->hasProperty($property);
        $camelized = $this->camelize($property);
        $singulars = (array) Inflector::singularize($camelized);
        $errors = [];
        if ($useAdderAndRemover) {
            foreach ($this->findAdderAndRemover($reflClass, $singulars) as $methods) {
                if (3 === \count($methods)) {
                    $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_ADDER_AND_REMOVER;
                    $access[self::ACCESS_ADDER] = $methods[self::ACCESS_ADDER];
                    $access[self::ACCESS_REMOVER] = $methods[self::ACCESS_REMOVER];
                    break;
                }
                if (isset($methods[self::ACCESS_ADDER])) {
                    $errors[] = \sprintf('The add method "%s" in class "%s" was found, but the corresponding remove method "%s" was not found', $methods['methods'][self::ACCESS_ADDER], $reflClass->name, $methods['methods'][self::ACCESS_REMOVER]);
                }
                if (isset($methods[self::ACCESS_REMOVER])) {
                    $errors[] = \sprintf('The remove method "%s" in class "%s" was found, but the corresponding add method "%s" was not found', $methods['methods'][self::ACCESS_REMOVER], $reflClass->name, $methods['methods'][self::ACCESS_ADDER]);
                }
            }
        }
        if (!isset($access[self::ACCESS_TYPE])) {
            $setter = 'set' . $camelized;
            $getsetter = \lcfirst($camelized);
            // jQuery style, e.g. read: last(), write: last($item)
            if ($this->isMethodAccessible($reflClass, $setter, 1)) {
                $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD;
                $access[self::ACCESS_NAME] = $setter;
            } elseif ($this->isMethodAccessible($reflClass, $getsetter, 1)) {
                $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD;
                $access[self::ACCESS_NAME] = $getsetter;
            } elseif ($this->isMethodAccessible($reflClass, '__set', 2)) {
                $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_PROPERTY;
                $access[self::ACCESS_NAME] = $property;
            } elseif ($access[self::ACCESS_HAS_PROPERTY] && $reflClass->getProperty($property)->isPublic()) {
                $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_PROPERTY;
                $access[self::ACCESS_NAME] = $property;
            } elseif ($this->magicCall && $this->isMethodAccessible($reflClass, '__call', 2)) {
                // we call the getter and hope the __call do the job
                $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_MAGIC;
                $access[self::ACCESS_NAME] = $setter;
            } else {
                foreach ($this->findAdderAndRemover($reflClass, $singulars) as $methods) {
                    if (3 === \count($methods)) {
                        $errors[] = \sprintf('The property "%s" in class "%s" can be defined with the methods "%s()" but ' . 'the new value must be an array or an instance of \\Traversable, ' . '"%s" given.', $property, $reflClass->name, \implode('()", "', [$methods[self::ACCESS_ADDER], $methods[self::ACCESS_REMOVER]]), \is_object($value) ? \get_class($value) : \gettype($value));
                    }
                }
                if (!isset($access[self::ACCESS_NAME])) {
                    $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_NOT_FOUND;
                    $triedMethods = [$setter => 1, $getsetter => 1, '__set' => 2, '__call' => 2];
                    foreach ($singulars as $singular) {
                        $triedMethods['add' . $singular] = 1;
                        $triedMethods['remove' . $singular] = 1;
                    }
                    foreach ($triedMethods as $methodName => $parameters) {
                        if (!$reflClass->hasMethod($methodName)) {
                            continue;
                        }
                        $method = $reflClass->getMethod($methodName);
                        if (!$method->isPublic()) {
                            $errors[] = \sprintf('The method "%s" in class "%s" was found but does not have public access', $methodName, $reflClass->name);
                            continue;
                        }
                        if ($method->getNumberOfRequiredParameters() > $parameters || $method->getNumberOfParameters() < $parameters) {
                            $errors[] = \sprintf('The method "%s" in class "%s" requires %d arguments, but should accept only %d', $methodName, $reflClass->name, $method->getNumberOfRequiredParameters(), $parameters);
                        }
                    }
                    if (\count($errors)) {
                        $access[self::ACCESS_NAME] = \implode('. ', $errors) . '.';
                    } else {
                        $access[self::ACCESS_NAME] = \sprintf('Neither the property "%s" nor one of the methods %s"%s()", "%s()", ' . '"__set()" or "__call()" exist and have public access in class "%s".', $property, \implode('', \array_map(function ($singular) {
                            return '"add' . $singular . '()"/"remove' . $singular . '()", ';
                        }, $singulars)), $setter, $getsetter, $reflClass->name);
                    }
                }
            }
        }
        if (isset($item)) {
            $this->cacheItemPool->save($item->set($access));
        }
        return $this->writePropertyCache[$key] = $access;
    }
    /**
     * Returns whether a property is writable in the given object.
     *
     * @param object $object The object to write to
     */
    private function isPropertyWritable($object, string $property) : bool
    {
        if (!\is_object($object)) {
            return \false;
        }
        $access = $this->getWriteAccessInfo(\get_class($object), $property, []);
        $isWritable = self::ACCESS_TYPE_METHOD === $access[self::ACCESS_TYPE] || self::ACCESS_TYPE_PROPERTY === $access[self::ACCESS_TYPE] || self::ACCESS_TYPE_ADDER_AND_REMOVER === $access[self::ACCESS_TYPE] || !$access[self::ACCESS_HAS_PROPERTY] && \property_exists($object, $property) || self::ACCESS_TYPE_MAGIC === $access[self::ACCESS_TYPE];
        if ($isWritable) {
            return \true;
        }
        $access = $this->getWriteAccessInfo(\get_class($object), $property, '');
        return self::ACCESS_TYPE_METHOD === $access[self::ACCESS_TYPE] || self::ACCESS_TYPE_PROPERTY === $access[self::ACCESS_TYPE] || self::ACCESS_TYPE_ADDER_AND_REMOVER === $access[self::ACCESS_TYPE] || !$access[self::ACCESS_HAS_PROPERTY] && \property_exists($object, $property) || self::ACCESS_TYPE_MAGIC === $access[self::ACCESS_TYPE];
    }
    /**
     * Camelizes a given string.
     */
    private function camelize(string $string) : string
    {
        return \str_replace(' ', '', \ucwords(\str_replace('_', ' ', $string)));
    }
    /**
     * Searches for add and remove methods.
     */
    private function findAdderAndRemover(\ReflectionClass $reflClass, array $singulars) : iterable
    {
        foreach ($singulars as $singular) {
            $addMethod = 'add' . $singular;
            $removeMethod = 'remove' . $singular;
            $result = ['methods' => [self::ACCESS_ADDER => $addMethod, self::ACCESS_REMOVER => $removeMethod]];
            $addMethodFound = $this->isMethodAccessible($reflClass, $addMethod, 1);
            if ($addMethodFound) {
                $result[self::ACCESS_ADDER] = $addMethod;
            }
            $removeMethodFound = $this->isMethodAccessible($reflClass, $removeMethod, 1);
            if ($removeMethodFound) {
                $result[self::ACCESS_REMOVER] = $removeMethod;
            }
            (yield $result);
        }
        return null;
    }
    /**
     * Returns whether a method is public and has the number of required parameters.
     */
    private function isMethodAccessible(\ReflectionClass $class, string $methodName, int $parameters) : bool
    {
        if ($class->hasMethod($methodName)) {
            $method = $class->getMethod($methodName);
            if ($method->isPublic() && $method->getNumberOfRequiredParameters() <= $parameters && $method->getNumberOfParameters() >= $parameters) {
                return \true;
            }
        }
        return \false;
    }
    /**
     * Gets a PropertyPath instance and caches it.
     *
     * @param string|PropertyPath $propertyPath
     */
    private function getPropertyPath($propertyPath) : PropertyPath
    {
        if ($propertyPath instanceof PropertyPathInterface) {
            // Don't call the copy constructor has it is not needed here
            return $propertyPath;
        }
        if (isset($this->propertyPathCache[$propertyPath])) {
            return $this->propertyPathCache[$propertyPath];
        }
        if ($this->cacheItemPool) {
            $item = $this->cacheItemPool->getItem(self::CACHE_PREFIX_PROPERTY_PATH . \rawurlencode($propertyPath));
            if ($item->isHit()) {
                return $this->propertyPathCache[$propertyPath] = $item->get();
            }
        }
        $propertyPathInstance = new PropertyPath($propertyPath);
        if (isset($item)) {
            $item->set($propertyPathInstance);
            $this->cacheItemPool->save($item);
        }
        return $this->propertyPathCache[$propertyPath] = $propertyPathInstance;
    }
    /**
     * Creates the APCu adapter if applicable.
     *
     * @return AdapterInterface
     *
     * @throws \LogicException When the Cache Component isn't available
     */
    public static function createCache(string $namespace, int $defaultLifetime, string $version, LoggerInterface $logger = null)
    {
        if (!\class_exists('WP2FA_Vendor\\Symfony\\Component\\Cache\\Adapter\\ApcuAdapter')) {
            throw new \LogicException(\sprintf('The Symfony Cache component must be installed to use "%s()".', __METHOD__));
        }
        if (!ApcuAdapter::isSupported()) {
            return new NullAdapter();
        }
        $apcu = new ApcuAdapter($namespace, $defaultLifetime / 5, $version);
        if ('cli' === \PHP_SAPI && !\filter_var(\ini_get('apc.enable_cli'), \FILTER_VALIDATE_BOOLEAN)) {
            $apcu->setLogger(new NullLogger());
        } elseif (null !== $logger) {
            $apcu->setLogger($logger);
        }
        return $apcu;
    }
}
vendor/symfony/property-access/PropertyPathIteratorInterface.php000064400000001377150755130600021407 0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace WP2FA_Vendor\Symfony\Component\PropertyAccess;

/**
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
interface PropertyPathIteratorInterface extends \Iterator, \SeekableIterator
{
    /**
     * Returns whether the current element in the property path is an array
     * index.
     *
     * @return bool
     */
    public function isIndex();
    /**
     * Returns whether the current element in the property path is a property
     * name.
     *
     * @return bool
     */
    public function isProperty();
}
vendor/symfony/property-access/PropertyPathBuilder.php000064400000023113150755130600017353 0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace WP2FA_Vendor\Symfony\Component\PropertyAccess;

use WP2FA_Vendor\Symfony\Component\PropertyAccess\Exception\OutOfBoundsException;
/**
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class PropertyPathBuilder
{
    private $elements = [];
    private $isIndex = [];
    /**
     * Creates a new property path builder.
     *
     * @param PropertyPathInterface|string|null $path The path to initially store
     *                                                in the builder. Optional.
     */
    public function __construct($path = null)
    {
        if (null !== $path) {
            $this->append($path);
        }
    }
    /**
     * Appends a (sub-) path to the current path.
     *
     * @param PropertyPathInterface|string $path   The path to append
     * @param int                          $offset The offset where the appended
     *                                             piece starts in $path
     * @param int                          $length The length of the appended piece
     *                                             If 0, the full path is appended
     */
    public function append($path, int $offset = 0, int $length = 0)
    {
        if (\is_string($path)) {
            $path = new PropertyPath($path);
        }
        if (0 === $length) {
            $end = $path->getLength();
        } else {
            $end = $offset + $length;
        }
        for (; $offset < $end; ++$offset) {
            $this->elements[] = $path->getElement($offset);
            $this->isIndex[] = $path->isIndex($offset);
        }
    }
    /**
     * Appends an index element to the current path.
     *
     * @param string $name The name of the appended index
     */
    public function appendIndex(string $name)
    {
        $this->elements[] = $name;
        $this->isIndex[] = \true;
    }
    /**
     * Appends a property element to the current path.
     *
     * @param string $name The name of the appended property
     */
    public function appendProperty(string $name)
    {
        $this->elements[] = $name;
        $this->isIndex[] = \false;
    }
    /**
     * Removes elements from the current path.
     *
     * @param int $offset The offset at which to remove
     * @param int $length The length of the removed piece
     *
     * @throws OutOfBoundsException if offset is invalid
     */
    public function remove(int $offset, int $length = 1)
    {
        if (!isset($this->elements[$offset])) {
            throw new OutOfBoundsException(\sprintf('The offset "%s" is not within the property path.', $offset));
        }
        $this->resize($offset, $length, 0);
    }
    /**
     * Replaces a sub-path by a different (sub-) path.
     *
     * @param int                          $offset     The offset at which to replace
     * @param int                          $length     The length of the piece to replace
     * @param PropertyPathInterface|string $path       The path to insert
     * @param int                          $pathOffset The offset where the inserted piece
     *                                                 starts in $path
     * @param int                          $pathLength The length of the inserted piece
     *                                                 If 0, the full path is inserted
     *
     * @throws OutOfBoundsException If the offset is invalid
     */
    public function replace(int $offset, int $length, $path, int $pathOffset = 0, int $pathLength = 0)
    {
        if (\is_string($path)) {
            $path = new PropertyPath($path);
        }
        if ($offset < 0 && \abs($offset) <= $this->getLength()) {
            $offset = $this->getLength() + $offset;
        } elseif (!isset($this->elements[$offset])) {
            throw new OutOfBoundsException('The offset ' . $offset . ' is not within the property path');
        }
        if (0 === $pathLength) {
            $pathLength = $path->getLength() - $pathOffset;
        }
        $this->resize($offset, $length, $pathLength);
        for ($i = 0; $i < $pathLength; ++$i) {
            $this->elements[$offset + $i] = $path->getElement($pathOffset + $i);
            $this->isIndex[$offset + $i] = $path->isIndex($pathOffset + $i);
        }
        \ksort($this->elements);
    }
    /**
     * Replaces a property element by an index element.
     *
     * @param int    $offset The offset at which to replace
     * @param string $name   The new name of the element. Optional
     *
     * @throws OutOfBoundsException If the offset is invalid
     */
    public function replaceByIndex(int $offset, string $name = null)
    {
        if (!isset($this->elements[$offset])) {
            throw new OutOfBoundsException(\sprintf('The offset "%s" is not within the property path.', $offset));
        }
        if (null !== $name) {
            $this->elements[$offset] = $name;
        }
        $this->isIndex[$offset] = \true;
    }
    /**
     * Replaces an index element by a property element.
     *
     * @param int    $offset The offset at which to replace
     * @param string $name   The new name of the element. Optional
     *
     * @throws OutOfBoundsException If the offset is invalid
     */
    public function replaceByProperty(int $offset, string $name = null)
    {
        if (!isset($this->elements[$offset])) {
            throw new OutOfBoundsException(\sprintf('The offset "%s" is not within the property path.', $offset));
        }
        if (null !== $name) {
            $this->elements[$offset] = $name;
        }
        $this->isIndex[$offset] = \false;
    }
    /**
     * Returns the length of the current path.
     *
     * @return int The path length
     */
    public function getLength()
    {
        return \count($this->elements);
    }
    /**
     * Returns the current property path.
     *
     * @return PropertyPathInterface|null The constructed property path
     */
    public function getPropertyPath()
    {
        $pathAsString = $this->__toString();
        return '' !== $pathAsString ? new PropertyPath($pathAsString) : null;
    }
    /**
     * Returns the current property path as string.
     *
     * @return string The property path as string
     */
    public function __toString()
    {
        $string = '';
        foreach ($this->elements as $offset => $element) {
            if ($this->isIndex[$offset]) {
                $element = '[' . $element . ']';
            } elseif ('' !== $string) {
                $string .= '.';
            }
            $string .= $element;
        }
        return $string;
    }
    /**
     * Resizes the path so that a chunk of length $cutLength is
     * removed at $offset and another chunk of length $insertionLength
     * can be inserted.
     */
    private function resize(int $offset, int $cutLength, int $insertionLength)
    {
        // Nothing else to do in this case
        if ($insertionLength === $cutLength) {
            return;
        }
        $length = \count($this->elements);
        if ($cutLength > $insertionLength) {
            // More elements should be removed than inserted
            $diff = $cutLength - $insertionLength;
            $newLength = $length - $diff;
            // Shift elements to the left (left-to-right until the new end)
            // Max allowed offset to be shifted is such that
            // $offset + $diff < $length (otherwise invalid index access)
            // i.e. $offset < $length - $diff = $newLength
            for ($i = $offset; $i < $newLength; ++$i) {
                $this->elements[$i] = $this->elements[$i + $diff];
                $this->isIndex[$i] = $this->isIndex[$i + $diff];
            }
            // All remaining elements should be removed
            $this->elements = \array_slice($this->elements, 0, $i);
            $this->isIndex = \array_slice($this->isIndex, 0, $i);
        } else {
            $diff = $insertionLength - $cutLength;
            $newLength = $length + $diff;
            $indexAfterInsertion = $offset + $insertionLength;
            // $diff <= $insertionLength
            // $indexAfterInsertion >= $insertionLength
            // => $diff <= $indexAfterInsertion
            // In each of the following loops, $i >= $diff must hold,
            // otherwise ($i - $diff) becomes negative.
            // Shift old elements to the right to make up space for the
            // inserted elements. This needs to be done left-to-right in
            // order to preserve an ascending array index order
            // Since $i = max($length, $indexAfterInsertion) and $indexAfterInsertion >= $diff,
            // $i >= $diff is guaranteed.
            for ($i = \max($length, $indexAfterInsertion); $i < $newLength; ++$i) {
                $this->elements[$i] = $this->elements[$i - $diff];
                $this->isIndex[$i] = $this->isIndex[$i - $diff];
            }
            // Shift remaining elements to the right. Do this right-to-left
            // so we don't overwrite elements before copying them
            // The last written index is the immediate index after the inserted
            // string, because the indices before that will be overwritten
            // anyway.
            // Since $i >= $indexAfterInsertion and $indexAfterInsertion >= $diff,
            // $i >= $diff is guaranteed.
            for ($i = $length - 1; $i >= $indexAfterInsertion; --$i) {
                $this->elements[$i] = $this->elements[$i - $diff];
                $this->isIndex[$i] = $this->isIndex[$i - $diff];
            }
        }
    }
}
vendor/symfony/inflector/Inflector.php000064400000036552150755130600014205 0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace WP2FA_Vendor\Symfony\Component\Inflector;

/**
 * Converts words between singular and plural forms.
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
final class Inflector
{
    /**
     * Map English plural to singular suffixes.
     *
     * @see http://english-zone.com/spelling/plurals.html
     */
    private static $pluralMap = [
        // First entry: plural suffix, reversed
        // Second entry: length of plural suffix
        // Third entry: Whether the suffix may succeed a vocal
        // Fourth entry: Whether the suffix may succeed a consonant
        // Fifth entry: singular suffix, normal
        // bacteria (bacterium), criteria (criterion), phenomena (phenomenon)
        ['a', 1, \true, \true, ['on', 'um']],
        // nebulae (nebula)
        ['ea', 2, \true, \true, 'a'],
        // services (service)
        ['secivres', 8, \true, \true, 'service'],
        // mice (mouse), lice (louse)
        ['eci', 3, \false, \true, 'ouse'],
        // geese (goose)
        ['esee', 4, \false, \true, 'oose'],
        // fungi (fungus), alumni (alumnus), syllabi (syllabus), radii (radius)
        ['i', 1, \true, \true, 'us'],
        // men (man), women (woman)
        ['nem', 3, \true, \true, 'man'],
        // children (child)
        ['nerdlihc', 8, \true, \true, 'child'],
        // oxen (ox)
        ['nexo', 4, \false, \false, 'ox'],
        // indices (index), appendices (appendix), prices (price)
        ['seci', 4, \false, \true, ['ex', 'ix', 'ice']],
        // selfies (selfie)
        ['seifles', 7, \true, \true, 'selfie'],
        // movies (movie)
        ['seivom', 6, \true, \true, 'movie'],
        // feet (foot)
        ['teef', 4, \true, \true, 'foot'],
        // geese (goose)
        ['eseeg', 5, \true, \true, 'goose'],
        // teeth (tooth)
        ['hteet', 5, \true, \true, 'tooth'],
        // news (news)
        ['swen', 4, \true, \true, 'news'],
        // series (series)
        ['seires', 6, \true, \true, 'series'],
        // babies (baby)
        ['sei', 3, \false, \true, 'y'],
        // accesses (access), addresses (address), kisses (kiss)
        ['sess', 4, \true, \false, 'ss'],
        // analyses (analysis), ellipses (ellipsis), fungi (fungus),
        // neuroses (neurosis), theses (thesis), emphases (emphasis),
        // oases (oasis), crises (crisis), houses (house), bases (base),
        // atlases (atlas)
        ['ses', 3, \true, \true, ['s', 'se', 'sis']],
        // objectives (objective), alternative (alternatives)
        ['sevit', 5, \true, \true, 'tive'],
        // drives (drive)
        ['sevird', 6, \false, \true, 'drive'],
        // lives (life), wives (wife)
        ['sevi', 4, \false, \true, 'ife'],
        // moves (move)
        ['sevom', 5, \true, \true, 'move'],
        // hooves (hoof), dwarves (dwarf), elves (elf), leaves (leaf), caves (cave), staves (staff)
        ['sev', 3, \true, \true, ['f', 've', 'ff']],
        // axes (axis), axes (ax), axes (axe)
        ['sexa', 4, \false, \false, ['ax', 'axe', 'axis']],
        // indexes (index), matrixes (matrix)
        ['sex', 3, \true, \false, 'x'],
        // quizzes (quiz)
        ['sezz', 4, \true, \false, 'z'],
        // bureaus (bureau)
        ['suae', 4, \false, \true, 'eau'],
        // fees (fee), trees (tree), employees (employee)
        ['see', 3, \true, \true, 'ee'],
        // roses (rose), garages (garage), cassettes (cassette),
        // waltzes (waltz), heroes (hero), bushes (bush), arches (arch),
        // shoes (shoe)
        ['se', 2, \true, \true, ['', 'e']],
        // tags (tag)
        ['s', 1, \true, \true, ''],
        // chateaux (chateau)
        ['xuae', 4, \false, \true, 'eau'],
        // people (person)
        ['elpoep', 6, \true, \true, 'person'],
    ];
    /**
     * Map English singular to plural suffixes.
     *
     * @see http://english-zone.com/spelling/plurals.html
     */
    private static $singularMap = [
        // First entry: singular suffix, reversed
        // Second entry: length of singular suffix
        // Third entry: Whether the suffix may succeed a vocal
        // Fourth entry: Whether the suffix may succeed a consonant
        // Fifth entry: plural suffix, normal
        // criterion (criteria)
        ['airetirc', 8, \false, \false, 'criterion'],
        // nebulae (nebula)
        ['aluben', 6, \false, \false, 'nebulae'],
        // children (child)
        ['dlihc', 5, \true, \true, 'children'],
        // prices (price)
        ['eci', 3, \false, \true, 'ices'],
        // services (service)
        ['ecivres', 7, \true, \true, 'services'],
        // lives (life), wives (wife)
        ['efi', 3, \false, \true, 'ives'],
        // selfies (selfie)
        ['eifles', 6, \true, \true, 'selfies'],
        // movies (movie)
        ['eivom', 5, \true, \true, 'movies'],
        // lice (louse)
        ['esuol', 5, \false, \true, 'lice'],
        // mice (mouse)
        ['esuom', 5, \false, \true, 'mice'],
        // geese (goose)
        ['esoo', 4, \false, \true, 'eese'],
        // houses (house), bases (base)
        ['es', 2, \true, \true, 'ses'],
        // geese (goose)
        ['esoog', 5, \true, \true, 'geese'],
        // caves (cave)
        ['ev', 2, \true, \true, 'ves'],
        // drives (drive)
        ['evird', 5, \false, \true, 'drives'],
        // objectives (objective), alternative (alternatives)
        ['evit', 4, \true, \true, 'tives'],
        // moves (move)
        ['evom', 4, \true, \true, 'moves'],
        // staves (staff)
        ['ffats', 5, \true, \true, 'staves'],
        // hooves (hoof), dwarves (dwarf), elves (elf), leaves (leaf)
        ['ff', 2, \true, \true, 'ffs'],
        // hooves (hoof), dwarves (dwarf), elves (elf), leaves (leaf)
        ['f', 1, \true, \true, ['fs', 'ves']],
        // arches (arch)
        ['hc', 2, \true, \true, 'ches'],
        // bushes (bush)
        ['hs', 2, \true, \true, 'shes'],
        // teeth (tooth)
        ['htoot', 5, \true, \true, 'teeth'],
        // bacteria (bacterium), criteria (criterion), phenomena (phenomenon)
        ['mu', 2, \true, \true, 'a'],
        // men (man), women (woman)
        ['nam', 3, \true, \true, 'men'],
        // people (person)
        ['nosrep', 6, \true, \true, ['persons', 'people']],
        // bacteria (bacterium), criteria (criterion), phenomena (phenomenon)
        ['noi', 3, \true, \true, 'ions'],
        // seasons (season), treasons (treason), poisons (poison), lessons (lesson)
        ['nos', 3, \true, \true, 'sons'],
        // bacteria (bacterium), criteria (criterion), phenomena (phenomenon)
        ['no', 2, \true, \true, 'a'],
        // echoes (echo)
        ['ohce', 4, \true, \true, 'echoes'],
        // heroes (hero)
        ['oreh', 4, \true, \true, 'heroes'],
        // atlases (atlas)
        ['salta', 5, \true, \true, 'atlases'],
        // irises (iris)
        ['siri', 4, \true, \true, 'irises'],
        // analyses (analysis), ellipses (ellipsis), neuroses (neurosis)
        // theses (thesis), emphases (emphasis), oases (oasis),
        // crises (crisis)
        ['sis', 3, \true, \true, 'ses'],
        // accesses (access), addresses (address), kisses (kiss)
        ['ss', 2, \true, \false, 'sses'],
        // syllabi (syllabus)
        ['suballys', 8, \true, \true, 'syllabi'],
        // buses (bus)
        ['sub', 3, \true, \true, 'buses'],
        // circuses (circus)
        ['suc', 3, \true, \true, 'cuses'],
        // fungi (fungus), alumni (alumnus), syllabi (syllabus), radii (radius)
        ['su', 2, \true, \true, 'i'],
        // news (news)
        ['swen', 4, \true, \true, 'news'],
        // feet (foot)
        ['toof', 4, \true, \true, 'feet'],
        // chateaux (chateau), bureaus (bureau)
        ['uae', 3, \false, \true, ['eaus', 'eaux']],
        // oxen (ox)
        ['xo', 2, \false, \false, 'oxen'],
        // hoaxes (hoax)
        ['xaoh', 4, \true, \false, 'hoaxes'],
        // indices (index)
        ['xedni', 5, \false, \true, ['indicies', 'indexes']],
        // boxes (box)
        ['xo', 2, \false, \true, 'oxes'],
        // indexes (index), matrixes (matrix)
        ['x', 1, \true, \false, ['cies', 'xes']],
        // appendices (appendix)
        ['xi', 2, \false, \true, 'ices'],
        // babies (baby)
        ['y', 1, \false, \true, 'ies'],
        // quizzes (quiz)
        ['ziuq', 4, \true, \false, 'quizzes'],
        // waltzes (waltz)
        ['z', 1, \true, \true, 'zes'],
    ];
    /**
     * A list of words which should not be inflected, reversed.
     */
    private static $uninflected = ['atad', 'reed', 'kcabdeef', 'hsif', 'ofni', 'esoom', 'seires', 'peehs', 'seiceps'];
    /**
     * This class should not be instantiated.
     */
    private function __construct()
    {
    }
    /**
     * Returns the singular form of a word.
     *
     * If the method can't determine the form with certainty, an array of the
     * possible singulars is returned.
     *
     * @param string $plural A word in plural form
     *
     * @return string|array The singular form or an array of possible singular forms
     */
    public static function singularize(string $plural)
    {
        $pluralRev = \strrev($plural);
        $lowerPluralRev = \strtolower($pluralRev);
        $pluralLength = \strlen($lowerPluralRev);
        // Check if the word is one which is not inflected, return early if so
        if (\in_array($lowerPluralRev, self::$uninflected, \true)) {
            return $plural;
        }
        // The outer loop iterates over the entries of the plural table
        // The inner loop $j iterates over the characters of the plural suffix
        // in the plural table to compare them with the characters of the actual
        // given plural suffix
        foreach (self::$pluralMap as $map) {
            $suffix = $map[0];
            $suffixLength = $map[1];
            $j = 0;
            // Compare characters in the plural table and of the suffix of the
            // given plural one by one
            while ($suffix[$j] === $lowerPluralRev[$j]) {
                // Let $j point to the next character
                ++$j;
                // Successfully compared the last character
                // Add an entry with the singular suffix to the singular array
                if ($j === $suffixLength) {
                    // Is there any character preceding the suffix in the plural string?
                    if ($j < $pluralLength) {
                        $nextIsVocal = \false !== \strpos('aeiou', $lowerPluralRev[$j]);
                        if (!$map[2] && $nextIsVocal) {
                            // suffix may not succeed a vocal but next char is one
                            break;
                        }
                        if (!$map[3] && !$nextIsVocal) {
                            // suffix may not succeed a consonant but next char is one
                            break;
                        }
                    }
                    $newBase = \substr($plural, 0, $pluralLength - $suffixLength);
                    $newSuffix = $map[4];
                    // Check whether the first character in the plural suffix
                    // is uppercased. If yes, uppercase the first character in
                    // the singular suffix too
                    $firstUpper = \ctype_upper($pluralRev[$j - 1]);
                    if (\is_array($newSuffix)) {
                        $singulars = [];
                        foreach ($newSuffix as $newSuffixEntry) {
                            $singulars[] = $newBase . ($firstUpper ? \ucfirst($newSuffixEntry) : $newSuffixEntry);
                        }
                        return $singulars;
                    }
                    return $newBase . ($firstUpper ? \ucfirst($newSuffix) : $newSuffix);
                }
                // Suffix is longer than word
                if ($j === $pluralLength) {
                    break;
                }
            }
        }
        // Assume that plural and singular is identical
        return $plural;
    }
    /**
     * Returns the plural form of a word.
     *
     * If the method can't determine the form with certainty, an array of the
     * possible plurals is returned.
     *
     * @param string $singular A word in singular form
     *
     * @return string|array The plural form or an array of possible plural forms
     */
    public static function pluralize(string $singular)
    {
        $singularRev = \strrev($singular);
        $lowerSingularRev = \strtolower($singularRev);
        $singularLength = \strlen($lowerSingularRev);
        // Check if the word is one which is not inflected, return early if so
        if (\in_array($lowerSingularRev, self::$uninflected, \true)) {
            return $singular;
        }
        // The outer loop iterates over the entries of the singular table
        // The inner loop $j iterates over the characters of the singular suffix
        // in the singular table to compare them with the characters of the actual
        // given singular suffix
        foreach (self::$singularMap as $map) {
            $suffix = $map[0];
            $suffixLength = $map[1];
            $j = 0;
            // Compare characters in the singular table and of the suffix of the
            // given plural one by one
            while ($suffix[$j] === $lowerSingularRev[$j]) {
                // Let $j point to the next character
                ++$j;
                // Successfully compared the last character
                // Add an entry with the plural suffix to the plural array
                if ($j === $suffixLength) {
                    // Is there any character preceding the suffix in the plural string?
                    if ($j < $singularLength) {
                        $nextIsVocal = \false !== \strpos('aeiou', $lowerSingularRev[$j]);
                        if (!$map[2] && $nextIsVocal) {
                            // suffix may not succeed a vocal but next char is one
                            break;
                        }
                        if (!$map[3] && !$nextIsVocal) {
                            // suffix may not succeed a consonant but next char is one
                            break;
                        }
                    }
                    $newBase = \substr($singular, 0, $singularLength - $suffixLength);
                    $newSuffix = $map[4];
                    // Check whether the first character in the singular suffix
                    // is uppercased. If yes, uppercase the first character in
                    // the singular suffix too
                    $firstUpper = \ctype_upper($singularRev[$j - 1]);
                    if (\is_array($newSuffix)) {
                        $plurals = [];
                        foreach ($newSuffix as $newSuffixEntry) {
                            $plurals[] = $newBase . ($firstUpper ? \ucfirst($newSuffixEntry) : $newSuffixEntry);
                        }
                        return $plurals;
                    }
                    return $newBase . ($firstUpper ? \ucfirst($newSuffix) : $newSuffix);
                }
                // Suffix is longer than word
                if ($j === $singularLength) {
                    break;
                }
            }
        }
        // Assume that plural is singular with a trailing `s`
        return $singular . 's';
    }
}
vendor/endroid/qr-code/assets/fonts/open_sans.ttf000064400000650420150755130600016167 0ustar000DSIG�D;�tGDEF&�7|GPOS777�8GSUB+=�7��OS/2�>���`cmap)�/h�cvt M���fpgm~a���gasp#7lglyft8�K%�/�head�v�<6hhea
�	st$hmtx�5<��kernT+	~U@�6loca)��4VmaxpC
� names���x�postC�l@&+prepC����	�!�__<�	�51���LL����	�b	���	����{	����V/\����3�3�f���@ [(1ASC@ �����X �H� ��#�5�+3���h�q��^R^=jV�h�?�T!���f���d�^�+���u�^�h�j!�!?�h�w�ho1y/�}��s�!��}���T#�`��'�9��;}��;}��djm���h�{�R����3V1�����s^���s�s}s�b'�����3��q����s���sD��j���91'�R=h�H�h#����?�{�h�!{�5�d�F�R�h�T�d��m�h�1�!����=q!��%�LB�P=K=.=o3���}s�s�s�s��<�T����<�/�;};};};};}��;}��������{����s^s^s^s^s^s^�^�s}s}s}s}s��������q���s�s�s�s�s�h�s����������s^s^s^}�s}�s}�s}�s���s�/�ss�}ss�}ss�}ss�}ss�}s�}b'�}b'�}b'�}b'������������*������T5�T��T�#�`����3�%�'��'�Y'��'���/�����������s���;}�s;}�s;}�sb}�q��D���D`��D�dj�jdj�jdj�jdj�jm�m�m�������������������������h9{{�R�R�R�R�R�R����s^����^;}�sdj�j���-�%��o�%������!���}�����������������/�)��'s��R��;}�T���9��mH;}�����Jm{bj�^mBP�<{�s�Z�������s�
�q�Z�s���s��%�F����V�q�s3���s�s����s^���/s�	���s��/ss��)�}dj�T�<#�`o����������/�)�ws���J�����9���;}����}m�bj�����B�D����%�
=f�3s^�w��m��)}s��D��'������s�����s�)�q1'����-��))����9��q%}s�m��s�j��������'���7�m�h9h9h9{RRRJ��\\�?\��={{�F�	�d��%�oRoP��
�y'm�b�D��?��)w'�5%BP�f=G= =G=j�f�'��L�hd%�w�b�h�h�h�o�������q��'��;�)�9�3�#�Vy!��TT��\�
��9�q�s^R���u3�uu=}�s%�R��S�
���;s��}s���fZ���m�
^�!��#�����?�^m�=}�s	�}}s�}Bs�}ws�^�}�s�ju��������)�))�%��/�����7/m#�3�=�J�DJ�\���D��/#��)��/���������;}s}�sm�){{�V'��)�������������=F3�=F3�T����d���������9�����;����Ts^s^����^s�}s�uyf�uyf���J�D�J�����;}�s=}�s=}�s
=�9�������7�m���)�7/m�R'�1'���s1�+s;NjPN/P���N�}s�-)�o�Z��s^s^s^s-s^s^s^s^s^s^s^s^s�}ss�}ss�}ss�}ss]}Js�}ss�}ss�}s�T{�T�;}�s;}�s;}�s;}�a;}�s;}�s;}�s=}�s=}�s=}�s=}�s=}�s��������%�R�%�R�%�R�%�R�%�R�{{{�s���q���q�h�y�y�y�h�1��-4�s�-)�^����u�^�h�jmZ\m��q�q�q�q�q;�;;�;��;;��;��;��;V;�^���;��������0HI~��'2a�����7�����	#�������
O_����?�����M    " & 0 3 : < D p y  � � �!!!! !"!&!.!^"""""""+"H"`"e%������� IJ���(3b�����7�����	#�������P`����>�����M      & 0 2 9 < D p t  � � �!!!! !"!&!.!["""""""+"H"`"d%��������������������a�I�������v�h�c�b�]g�D����������	�	�X��z�}�}�
�B������������������������v�t���	�n�������%�"������������iOS�������0L\pr`<������������������&'()*+,-./0123456789:;<=>?@AIJ$%TUVWXY�\]^_`abcdef�hijklmnopqrstuv�h������������ij������������k�����������������������������F�opqrstu�45]^@G[ZYXUTSRQPONMLKJIHGFEDCBA@?>=<;:9876510/.-,('&%$#"!

	, �`E�% Fa#E#aH-, EhD-,E#F`� a �F`�&#HH-,E#F#a� ` �&a� a�&#HH-,E#F`�@a �f`�&#HH-,E#F#a�@` �&a�@a�&#HH-, <<-, E# ��D# �ZQX# ��D#Y ��QX# �MD#Y �&QX# �
D#Y!!-,  EhD �` E�Fvh�E`D-,�
C#Ce
-,�
C#C-,�(#p�(>�(#p�(E:�
-, E�%Ead�PQXED!!Y-,I�#D-, E�C`D-,�C�Ce
-, i�@a�� �,����b`+d#da\X�aY-,�E����+�)#D�)z�-,Ee�,#DE�+#D-,KRXED!!Y-,KQXED!!Y-,�%# ���`#��-,�%# ���a#��-,�%���-,�C�RX!!!!!F#F`��F# F�`�a���b# #���pE` �PX�a�����F�Y�`h:Y-, E�%FRK�Q[X�%F ha�%�%?#!8!Y-, E�%FPX�%F ha�%�%?#!8!Y-,�C�C-,!!d#d��@b-,!��QXd#d�� b�@/+Y�`-,!��QXd#d��Ub��/+Y�`-,d#d��@b`#!-,KSX��%Id#Ei�@�a��b� aj�#D#��!#� 9/Y-,KSX �%Idi �&�%Id#a��b� aj�#D�&����#D���#D����& 9# 9//Y-,E#E`#E`#E`#vh��b -,�H+-, E�TX�@D E�@aD!!Y-,E�0/E#Ea`�`iD-,KQX�/#p�#B!!Y-,KQX �%EiSXD!!Y!!Y-,E�C�`c�`iD-,�/ED-,E# E�`D-,E#E`D-,K#QX�3��4 �34YDD-,�CX�&E�Xdf�`d� `f X!�@Y�aY#XeY�)#D#�)�!!!!!Y-,�CTXKS#KQZX8!!Y!!!!Y-,�CX�%Ed� `f X!�@Y�a#XeY�)#D�%�% XY�%�% F�%#B<�%�%�%�% F�%�`#B< XY�%�%�)�) EeD�%�%�)�%�% XY�%�%CH�%�%�%�%�`CH!Y!!!!!!!-,�%  F�%#B�%�%EH!!!!-,�% �%�%CH!!!-,E# E �P X#e#Y#h �@PX!�@Y#XeY�`D-,KS#KQZX E�`D!!Y-,KTX E�`D!!Y-,KS#KQZX8!!Y-,�!KTX8!!Y-,�CTX�F+!!!!Y-,�CTX�G+!!!Y-,�CTX�H+!!!!Y-,�CTX�I+!!!Y-, �#KS�KQZX#8!!Y-,�%I�SX �@8!Y-,F#F`#Fa#  F�a���b��@@�pE`h:-, �#Id�#SX<!Y-,KRX}zY-,�KKTB-,�B�#�Q�@�SZX� �TX�C`BY�$�QX� @�TX�C`B�$�TX� C`BKKRX�C`BY�@��TX�C`BY�@�c��TX�C`BY�@c��TX�C`BY�&�QX�@c��TX�@C`BY�@c��TX��C`BYYYYYY�CTX@
@@	@
�CTX�@�	�
��CRX�@���	@�@��	@Y�@��U�@c��UZX�
�
YYYBBBBB-,Eh#KQX# E d�@PX|Yh�`YD-,��%�%�#>�#>��
#eB�#B�#?�#?��#eB�#B�-,���CP��CT[X!#� ���Y-,�Y+-,��-@�	!H U UHU?�MK&LK3KF%&4U%3$U���JI3IF%3UU3U?�GF�F#3"U3U3UU3UO��U3Uo������TS++K��RK�	P[���%S���@QZ���UZ[X��Y���BK�2SX� YK�dSX��BYss++^stu+++++t+st+++++++++++++st+++^N�u��H�������������������������������Qw�{�j���A_t���[��F��4���Dd�A��		U	�	�

9
l
�
�
�V��,y��
$
K
�
�
�6Or���$y�T�(f��'��O��(h��G���K���S����G�������c���El���{������+7HYj|����*;L]n��  * ; L ^ o �!!(!8!H!X!i!z"""!"1"A"R"c"t"�"�"�###/#?#O#`#�$$$,$<$M$]$�$�$�$�$�%%%%0%@%Q%a%r%�%�%�%�%�%�&:&K&[&l&|&�&�&�&�&�&�&�&�'	''*';'G'W'h'y'�("(3(D(U(f(w(�(�(�(�(�(�(�(�))))L)])n)y)�)�)�)�)�)�*-*>*N*Z*e*v*�*�*�+'+8+H+Y+i+{+�+�,i,z,�,�,�,�,�,�,�,�----.->-I-T-e-u-�...%.6.F.W.g.y.�.�.�.�.�.�.�.�///+/;/L/]/n/~/�/�0w11'181I1Y1d1o1�1�1�1�22T2{2�2�33N3_3g3x3�3�3�3�3�3�3�3�444"4*424�4�4�4�4�4�55525:5q5y5�5�5�6<6�6�6�6�6�6�6�77k7�88g8�99L9�9�9�:,:4:_:�:�;;\;�;�<%<]<�==_=�=�=�=�=�>
>>o>�>�>�>�>�>�?S?�?�?�@@7@?@�@�@�@�@�A,A�A�A�BB<BDBLBTB\BdBlBtB�B�B�B�C+C[C�C�D#DaD�EEVE^E�FF4F|F�F�G#G[GkG�G�HHIHQHuH}H�H�H�IIILI�I�I�J4J}J�KKeK�K�L%L5L�L�L�L�L�MMXM`MpM�M�M�M�NNN/N@NRNdNuN�N�N�N�N�N�OOO:OiO�O�O�PZPzP�Q$Q,Q4QWQ{Q�Q�Q�RR�R�SnS�T,T�T�T�UKUbUyU�U�V
V>VcV�V�V�W2WbW�X,X>XPX}X�X�X�X�YY!Y@YuY�Y�ZMZnZ�['['['['['['['['['['['['['['\q\�\�\�]l]�^^^-^9^E^W^�^�^�^�_@_�_�`1`:`C`L`z`�`�`�`�`�aNa�a�b;b�b�c?c�c�d,d�d�eie�f�g0g8g@g�g�h/hghyh�ii
i�i�j�k;k�l:l}l�mm3m`m�m�n�oo�o�p1p�p�qCq{q�rrUr�r�sssPs�s�ttXt�t�uu]u�u�vvsv�wBw�w�w�xx4x<xox�x�y0yqy�y�z0zsz�{{C{z{�|K|�}-}5}F}W}�}�~D~�~�U�ڀ�o���ŀր��	���*�:���ځ���!�3�D���ڂ��
��0�A�I�Q�c�t���������ʃۃ���!�L�w�����������ʅ�V�������d�ɇ'���Ԉ+�y�ĉ�f����-�����������Šӊ����,�>�P�b�t���������Ӌ��	��-�B�V�b�n��������ÌՌ����/�A�V�j�{�����������͍ލ���&�8�J�\�n���������Ɏَ����(�4�@�L�]�n����������ҏ����#�4�E�V�f�r�����j�‘��2�{�͒���;�D������N���ޔ�	��n�z�����q���������ǖؖ����.�?�J�[�g�y�������������Η��
��/2/3/3/310!!7!!�I��hy����Jh������+@		OY??+9/933310#3432#"&Fi3��x:?@94D�#���FB@G?����@
	?3�2993310#!#?(i)+)h)�����3���@U		

!
 !
NY
NYO
O


/3?399//]]33+3333+339939939939223910!!#!#!5!!5!3!3!!!�B��T�T��R�P��D��+R�R1T�T��/B�������R��R��T��L��L��T��� &-f@5'%*
!	./%

MY$*LY*+MY*//99//92+33+33+3933333333310#5"&'53&&546753&'4&'6̷�p�CS�Yͥ˧���4����J�Y���Zocf�����#�%/�A������E�;�N2_{eHY,�{L\)�]h��-�	!-1E@$
("".(
023
+
+
+010%?3?3??99//33933331032#"#"&5463232654&#"#"&54632#�JS��SJʙ��������JTTPPTTJ˙����������Փ+��TR���������۫�������������� �J�q����5Q@0#*+.+-#&	673IY3'-0/&** / 	JY ?+?9/99?+93333106654&#"27%467.546326673#'#"&�HW�egVYo��Ko\,�����U=$į�����8C�D�+�v����E}XKSMa`����DYfAu����f_bj9����k�]�y>�c��ݲj\���?��?�9310#?(i)���R��!�
@
'??99331073#&R����������1	��2��6���=���
@


'??993310#654'3����������1���:��������1V0@
		


?�29333910%'%7�+����������+�uo���^j��^F�o�h�)�(@	
PY/]3+3933310!!#!5!3���d��f����V���?��m��	
/�9910%#67^b5}A
�d��rh2\T�?q�/399105!T�٘������@
	OY	?+931074632#"&�=9:AB93CjCEECAF?���??9910#�ߦ!��J�f��-�(@	KY	KY?+?+993310#"3232#"-�����ᖤ����������r~r�~������';;%�����
$@			??9/9993310!#47'3ˢ4�X���t.�r+d%�+@
KYLY?+?+93310!!5>54&#"'632!%�?��p8�~[�dX�����������Su�<Oq�Ӳ�����^���'C@$"
()KY
%%KY%
KY
?+?+9/+993310!"&'53 !#532654&#"'6632�����t�[_�`{�^���ȓ~`�mTZ���^������#,�/1)
���kz4FpGQ�+j�
<@	LY	??9/93+393333310##!533!47#jٟ�9����
0*�7P��P��)援`?�v����:@KYLYKY?+?+9/+933102#"'53265!"'!!6-�	����F�e���_�V7��%s}���O�-3��27���Iu��/�$D@#!!&%MYKYMY?+?+9/9+3933310!2&#"3632#"2654&#"uOHqAMc�n�����뎝��Z�YP�q�����Ƭ���Uȳ���J�Fg�h^+�@LY??+9310!!5!^��������h��)�".M@)&,		/0) ) KY))MY#MY?+?+9/+9993333102#"&54%&&54632654&'"6654&H�ꆓ�����2�x�w�����•�:}�v��w�˺�l�IU�{��ͼ��N�p����x��za�G@�gxd\�B<�\ewj��%�%A@""

&'MYKYMY?+?+9/9+933310!"'532##"&5432"326654&&%�htDPf�7�r���x�����[�XR�F���)3SW������0����J�Fi�f����d(@OY	OY	?+?+93331074632#"&432#"&�=9:AB93Cv{B93CjCEECAF?���AF??���d"@

		OY/�?+933310%#67432#"&^b5}A
w{B9:=�d��rh2\AFFh�)�@	//910%5)�?�����bߕ����w��*@	PYPY/]+/+9933105!5!w��^�Z���g��h�)�@	//9105h����?�Fu��!b�Z��9�&9@!'($$OY$
IY?+?+9/93331054676654&#"'632432#"&!Hb�G�{O�a;�ο�'L~eA�x:?@94D�6u�TstRfo%1�c��IocnVr_!�׈FB@G?y�F��5?E@"#.6;).@A88=+2&+/3?399//9233393333310#"&'##"&5463232654$#"!27# $!232&#"�X�hVv(�f���D�E�[r������B/���o���O�����HU��َ�hQWbͰ��*�׬��������V�T�f�ߵ�����9��9@IY?3?9/9+9933910!!#3&'`�����B�?�e�!#)��/��Dj�V}`s�;��� I@&

!"JYJYJY?+?+9/+99333310! #!!2654&##!2654&#��#��M���������1���������
9����Dq�{m���݉���}����&@	IYIY?+?+9310"327# 4$32&;��
�������?��H�3������7�9�i�T�T�N�X�(@	
JYJY?+?+993310!!! !#3 X�w���k�Uz�����02��������"�p+���:@


	IYIY
IY?+?+9/+93310!!!!!!!���/�{^������)������	2@
	IYIY??+9/+93310!#!!!!s�/�{^������}��=�:@IYIYIY?+?+9/+93310!# 4$32&# !27!L�t����X���BƷ����!������9%&�d�W�V�T�����#���3@	
IY
?3?39/+99333310!#!#3!3��������P���nTV�7@

	JY
JY?+3?+3933310!!57'5!V������b#�%bb%�V#�`�h�
@
	IY"?+?9310"'532653^6GMcg����xq��X�����*@

	?3?39993310!##33��뙪����ň����+�����@IY?+?9931033!ɪ�����q�2@


?33?3999333310!##!33#47#P��������^��J��J������?�.@	?3?39999333310!###33&73?������ش����:%?G}����(@	IY	IY?+?+993310! ! 32#"��������`D;b�s��������n�he��p�����2*'1���h�	4@

JY

JY??+9/+9933310!##! 32654&##h��欪{$���ʾɾ��������}����4@



IY
IY?�+?+993310# ! 32#"���\���7����`D;b�s�������B��J�he��p�����2*'1�����H@%
	

		

JY

IY?3?+9/+993333310#! #%32654&##s��
�������鴨���`������f�o`�����j���$4@%&IY	IY?+?+9993310# '532654&&'&&54632&#"���Z�h��=��̯��ڷ5����8������C�&,�sLaR4Iȡ��P�LtgLaQ1R�Z�$@	IY??+39310!#!5!!���1H�1������%@

IY?+?3993310! 533265������ߪ�¹���N��� ���F��Ÿ���
@??3999103#367����P:"$:��J��N����L�$@
	
	?3?339939910!#&&'#3673673Ũ��40��{��05�0!5��A����3��y����y��Î����#@

	?3?399910!##33���w�p��;�kn��;��}����C�L{� @	
??3993103#3=�����������/�R?�	+@
IYIY?+?+93310!!5!5!!?����������i���o� @	'?3?3993310!!!!o�7��!�����!���??9910#�#�����J�3���� @	'?3?3993310!!5!!3!���7�ߍ�1'#�@	//3999103#1�cݘ����'��f�������H�/33310!5!��b��Ń��!	�
�/�9910#&&'53nA�(� r,�4�?E�5^���Z$G@%"%&GYFYFY??+?+9/9+3933310!'##"&5%754&#"'6632%2655R!R�z���oz��3Q�aĽ����Ưm�gI��LD�{T,2���u��cmsZ^���uD@"
 !

FYFY?+?+993??993333102#"&'##336"32654&�����k�<#w�t̪������Z�����OR���e����������s���\&@	
FYFY?+?+9310"32&&# 327f�	�O�-37�2������n%,"��V��;�9s��7B@! !		FY	FY?+?+993??99333310%##"323''3#%26554&#"�	s������w
�������������&,�OM���w��#������s��\;@

FYFYFY?+?+9/+933310"32!327"!4&�����
����X����=�(	8���i��J�&!嬘��9@
FY
FY??3+3?+993333910!##575!2&#"!�����aWu+`D^Z�9�K<=�#�}�G'�1\*7An@>+8%=1*"%
BC55FY;GY
"***GY*(?GY(.GY?+?+?+99//99++993333310#"'332!"&5467&&5467&&5463232654&##"3254#"1�,�1+jJZ²������t*9@EUk��VE�����n��q~Z�t�u~Hi#qG��8U-+������d�P5<Z*#�l���Y\}kYEl<sv�~�D3@			FY
	?3??+999333310!4&#"#336632�z�����
1�t�����)U8O[��5�f�#@

HY
??�+933310!#34632#"&V���8*(::(*8H)9568877���f�,@
HY@	FY?+?�+933310"'5326534632#"&+_;ECNI��8*(::(*8��UW����]9568877�6@

??39/93?9933310673##3T+Xb�D��}}��1=cw�-��l�f��7s�V@	??9310!#3V����\#F@#	#	#$%

FY	?33??3+39/339333310!4&#"#4&#"#33663 36632%pv���pw����/�jO1�w��Ƀ�����Ƀ����H�PZ�Vd��5�D\1@			FY
	?3??+99933310!4&#"#336632�z�����3�q����H�QY��5s��b\(@

FY
FY?+?+993310#"&53232654&#"b����|������������%��ӊ�+������������u\!?@ "#FY	FY?+???+99993333310"&'##336632"32654&�k�<��@�n��������OR`V�=4�ZP�������%������s�7\D@"
 !
FY
FY
?+?+993??993333310%26754&#""32373#47#N�������}�����y	��
sw��%�����ً*
.�����dF��'\*@
		



FY?+?9?9933102&#"#3366�I:D4����=�\�ء��H�ktj��s\$6@%&FY	FY?+/?+9993310#"'532654&'.54632&#"s���zO�T��o���?ھ��;��vx-d�É+��E�(.SU@[>9UlK��H�DJA,>85G����F4@		
GY@FY?+?�+393310%267# #5773!!,Ri*��F`>��^u

O�PE��{cj���9H4@



FY??+?393993331032653#'##"&5Lz�����	3�t��H�9����@���QV���H@


	??399910!3363��`��Pu̲�`H�v�D5M0��#H,@	

	?3?339933339910!&'##3366733663/�4(��ծjo1ɴ�8#�����;ѯ_�H�c�PK9�5u���u$���'H"@
	?3?39991033##����! �������ʼ1�\������D�H$@	

FY
?2?+991033663#"'53277��O
S�)F��LJ7D�I=H���_3�|� �����RmH	+@
GYGY?+?+93310!!5!5!!m��V���]qV����=����,@'??933333310%&&54&#5665463�uq��~x�tض���f\���/hY�\`2�������''��{@	??93103#��H����,@
'??933333310&54'52"5665467
���v�z~;otnq?'�'������a[�Yh�љ��\f)rxhP)T$@PYPY/+�/�+9910"56323267#"&'&&R56d�DqYBb/6�6f�H~HKZ�C6�m&@9�n!  ����^+@		OY"??+9/9333103##"&54632�i3��y<<?93F���L�G@?H@�����>@





??99//333393333310%#5&5%53&#"327�i�����K�11�m�������6�� ��>��!�3����;?D�H@&	

NY	LYKY?+?+9/3+393333102&#"!!!!5655#5346���=��{}��ZAJ�������T�M|����d�,��/��<��{�' @
"()%/33/399331047'76327'#"''7&732654&#"�J�^�h�f�_�JJ�\�f�d�\�J��tt��rt��zk�\�II�\�qv�g�\�GI�\�k|p��qr��q�V@.		
??399//]9223333933333103!!!!#!5!5!5!3H{��`��=�ä��<���e���������{$@	??99//9333103#3#�����
��{���1=C@&2*8#>?;6-!	!'GY!	GY	?+?+99333310467&&54632&&#"#"'532654&&'.7654&'�VNJT��^�a5b�Ltt{���RJ���ڀN�R��0ls��B���1���DU)V�%(oUy�'�';@<T7D�kZ�)Q���A�%-LG.::+4ZrbMi=PoSp9d5h�@	/3�29933104632#"&%4632#"&55%&77&%5}5%%77%%5q4..421124..4211d��D�&6F@''/	78+#3?3?399//]]33933310"327#"&54632&4$32#"$732$54$#"}}��V}0eF��ݿ�v:l���^��^�������i�-��*���װ��֯#����-|���<v3���^��������Zƭ�ӭ�)��*����Fq�7@
 !


?3�]�39/39333310'#"&5467754#"'632%3255\�_o��u�dh+r����Pp�bpg!Tacffi'�3`8iy�<�d�19Ru��
)@

		
/3/39333310%RVw��!w���Xu��u��'�E����G��E����G�h)@PY//+99310#!5)���������T�?qd��D�&6]@3'	/
	78+#3?3?399//]]339/339333331032654&#####!24$32#"$732$54$#"�lPaV]j�UM�χ������^��^�������i�-��*���װ��֯�S@KA�P{�ub��{����^��������Zƭ�ӭ�)��*��������/33310!5!��\��!@

�?3�29933104632#"&732654&#"����R�T��suQPsqRSs�����T�T��RrqSTqr��h)�&+�t1J��#@
 ?3?393310!57>54&#"'632!����YR!P?4bEB����Y���Jh�VaL6DE&2Xo�pP���!9��#9@"
$%]mL!
!?3?39/]]]39310#"'53254##532654&#"'6632sRD����t�{��uwgcPCBp8E?�^���Pg/���8{D��kOD=D+#Z-6w��!	�	
�	/�99106673#�0o �,�@o�>�AA�4��DH5@


FY
	??+??399333331032653#'##"'##3V�����
o�X

��}����@����\T���4q��`'@/3?39/93310####"&563!`r�s>T����-����P3����L�Z@

	OY/+93104632#"&�>8:AB93C�BEEBAF?%��$@/�29/393310#"'532654&'73���3--;OQOmXn7���aj	j(6+5�s'LJ��
 @
		 ?2?9/9933103#47'R��6�C���C[Z-_`B��%@	?3�]2993310#"&5463232654&#"�����������[hi\\ig\o��������zzzz{vvPu��
#@	
	/3/393310'7'7���u��uX�u��u��uX�iG_^E�i�iG_^E�i��K��'�&{�<��	�?55��.��'?&{�tN���?5��!�&u�'�<m��	�+?553�wT^(A@"##)*&& OY&IY#?+?+9/_^]9333103267#"&54>76655#"&54632NKay=�zP�b;�ƾ�#@Y6eA�y;>B73F�3z�TjKM8dq&0�`��FiYR/Xt]+�EB@G@��s&$C��R�&+5��s&$v�R�&+5��s&$K#R�&+5��/&$RR�&+5��%&$j7R
�$&+55��&$P9�����N@,
	IYIY

IY

IY?+??99//+++33933310!!!#!!!!!!#����������D�T�v�/���)�������}���&&z����s&(C��R�
&+5����s&(v?R�&+5����s&(K��R�&+5����%&(jR
�!&+55��<Vs&,C��R�
&+5��Tss&,v�aR�&+5�����s&,K��R�&+5��<o%&,j�R
�!&+55/H�W@2
IY?���		JY	JY?+?+9/_^]3+39333310!!#53! !#!!3 H�w���{���Q|����{���b����������@����
���?/&1R�R�&+5��}���s&2CyR�&+5��}���s&2v
R�!&+5��}���s&2K�R�&&+5��}���/&2R�R�!&+5��}���%&2j�R
�-&+55��@			
/993310'7�`��^`����e^��da�c����c_��c``e��}����#N@,

$%!
!IY
IY?�+?�9+99933910!"''7&!27'32&#"������exl�`Dѝaxj��n�`s��'e�j�����nd�O��me�^�P�����LR2*����I�������s&8CFR�&+5�����s&8v�R�&+5�����s&8K}R� &+5�����%&8j�R
�'&+55��{s&<v1R�&+5�y�6@
	
JY	JY
	
	??99//++99333310!##33 32654&##y��Ḫ������ʾ�������ꏤ������0A@")*#*12*..&FY.*FY?+??+9/9333310#"'53254&'&&54676654&# #4632�X8GN�f³�k?�H�Sn`EGK@��������sFC! *93_�e��E�'/�KkFR{T?j59Z5PU�L������^���!&DC��&&+5��^���!&Dv+�.&+5��^���!&DK��3&+5��^����&DR��.&+5��^����&Dj�
�:&+55��^����&DP�
�(&+55^��s\)4;a@3*$08090<=-'-FY11GY8$'"'5FY?3+3?39/993+3+39333399310467754&#"'66326632!!267# '#"&732655"!4&^���tw��4J�b��)5�n��C:[�TV�e��}Q�kX������y��/��D�{T)5W_X`���k�u#'�&!�j��_Y��cm2������s��\&FzF��s��!&HC��&+5��s��!&HvN�$&+5��s��!&HK��)&+5��s���&Hj

�0&+55����c!&�C�Q�&+5���2!&�v� �
&+5����U!&�K���&+5�����&�j��
�&+55q��b!&J@+!	'(	FY		$FY?+?39/99+933310#"54327&''7&'774&# 326b�������d9��I�\^E�f�LϘ����������3���
��yֿ�l�>1uIK�kw��r�蓪��������D�&QR�&+5��s��b!&RC��&+5��s��b!&RvV�"&+5��s��b!&RK�'&+5��s��b�&RR��"&+5��s��b�&Rj
�.&+55h�)�3@



PY/+3/33/39333105!4632#"&4632#"&h���;64:;34=;64:;34=�����<=?:9@?�<=?:9@?s��b�#K@)

$%!
FY
!FY?�+?�9+999339910#"''7&327&#"4'326b���pTr^��tTua��5�Kr���3�/Gq��%���EuN��+LwL����f�5�Ԥd�}3������9!&XC��&+5�����9!&Xvq�&+5�����9!&XK�#&+5�����9�&Xj!
�*&+55���!&\v�&+5��u">@ $#		FY	FY?+?+99??993333106632#"'##3%"3 4&XB�j�����z��H����/��YO�����ӡ"M?�5�.4Z��)��������&\j�
�+&+55���&$M?R�&+5��^���b&DM��(&+5��7&$N+R�&+5��^����&DN��%&+5���B�&$Q���^�BZ&DQ���}���s&&vR� &+5��s���!&FvD� &+5��}���s&&K�R�%&+5��s���!&FK��%&+5��}���1&&OR� &+5��s����&FOP� &+5��}���s&&L�R�"&+5��s���!&FL��"&+5���Xs&'LXR�&+5��s���&G8�#?5��/H��s���'d@7%()GY/				"FY	FY?+?+99?9/_^]3+3?933333310%##"323&55!5!533##%26554&#"�	s������w
�@����������������&,�SI������%w��#����������&(MR�&+5��s��b&HM
�&+5����7&(NR�&+5��s���&HN��&+5����&(Oo5�&+5��s���&HOT�$&+5���B��&(Qs��s�a\&HQf����s&(LR�&+5��s��!&HL��&&+5��}��=s&*K�R�*&+5��'�1!&JK��P&+5��}��=7&*NR�&+5��'�1�&JN��B&+5��}��=1&*OdR�%&+5��'�1�&JO�K&+5��}�;=�&*9'��'�1!&J:D�F&+5���s&+K�R�&+5���D�&KK��%&+5��T@,IY
JY?3?399//33+33+9333333331053!533##!##55!ɪ��Ȫ����u������������P1�����DY@2
		 FYGY/		?3?9///]3+3+3933333310!4&#"##5353!!36632�z���������?
1�t������������T8O[��\�����/&,R��R�&+5����x�&�R���
&+5��*��&,M��R�&+5����2b&�M���&+5���7&,N��R�&+5����8�&�N���&+5��T�BV�&,Qh��5�B��&LQ��TV1&,OPR�&+5�VH@	??9310!#3V��H��T��&,-�����l�&LM���`�es&-K��R�&+5�����O!&7K���&+5���;��&.9�����;&N9+�F
/@

	?3?399333103##3/�b������F����q�yF��q����s&/v�cR�&+5���,�&Ov���
&+5���;��&/91��Y�;W&O9�������&/8���	?5����&O8+�?5�����&/O�g����&OOB�8��
=@!		
IY?+?99//99333103'73%!�iC��)C����;re�F�y�<���'7@	
	
??99//9339333107#'73V�HѦnF��`^p��?THqw ���?s&1vR�&+5���D!&Qvy�&+5���;?�&19�����;D\&Q9V���?s&1L�R�&+5���D!&QL� &+5����'Q���?5�?�8@


IY"?+??3999333310"'53265##33&53�b6GSij��������zo�������N��=�X����D\8@FYFY?+??9?+933310"'53254&#"#336632%V7<>�z�����
4�n�nj���y�����H�RX�������}����&2M�R�&+5��s��bb&RM�&+5��}���7&2N�R�&+5��s��b�&RN�&+5��}���s&2SR
�+&+55��s��b!&RSZ
�,&+55}����S@.
 !IYIY	IY	IYIY?+?+?+?+9/+933310!!# !2!!!!!"327&�f\����\@fZ��'��M�D����pWW�jh���)���������!uq��Z*1U@-%/%23+((FY.FY..""FY?3+3?39/99++393399310 '#"326632!!26732654&#"%"!4&���}>щ����>:�~��'J^�WX��!��������G� ��tw1	,wrpy���i�w#'�' 9�������ؤ�������s&5vyR�&+5���'!&Uv��&+5���;��&59}��`�;'\&U9������s&5LR�!&+5���'!&UL�v�&+5��j��s&6vPR�.&+5��j��s!&Vv��.&+5��j��s&6K��R�3&+5��j��s!&VK��3&+5��j��&6z'��j�s\&Vz���j��s&6L��R�0&+5��j��s!&VL��0&+5���;Z�&79���;�F&W9���Zs&7L��R�&+5�����&W8b�?5Z�?@!	
JYIY?+3?9/3+3933310!5!!!!#!5�1H�16�ʪ��/���^�����FL@)
GY

GY@FY?+?�9/3+3+39333310%27# 5#53#5773!!!!U< j*�ȍ���F`>��-��u\��PE����������/&8RoR�&+5�����9�&XR��&+5������&8M�R�&+5�����9b&XM�&+5�����7&8N�R�&+5�����9�&XN�&+5������&8P�R
�&+55�����9�&XP#
�&+55�����s&8S�R
�%&+55�����9!&XSh
�(&+55����B�&8Q!����BeH&XQ���Ls&:KTR�(&+5��#!&ZK��+&+5��{s&<K��R�&+5���!&\K��$&+5��{%&<j��R
�&+55��R?s&=vBR�&+5��Rm!&]v��&+5��R?1&=ODR�&+5��Rm�&]O��&+5��R?s&=L��R�&+5��Rm!&]L��&+5��@
	FY??+39310!#!2&#"V�g`d+WIaY��%�{z�� D@$
!"

FYFYFY?+?+9/3+3933310"'53265#5754632&#"!!HE@F=_M�ޢ�Uxf<bP���fq�K<�ò+@A i|���7���".a@4#)	"	0/&,	IY	""?3/9///99+33393333999910#!#&54632&'6673#4&#"326hh������jzcd}�/0	��1f� �Bo�B33B<95@��8�'��o�4�eru�6�:�0��T�;�*.�-��9<<97==^����	$/;Gg@7-B6<0)$$06HI		?9E3)GY  FY %FY
/??+?+9/9+3?3�29/9333333105667!'##"&5%754&#"'6632%2655#"&546324&#"326�.j��!R�z���w�`�G7T�e�����Ưm�{feyyee|mA33B<94@�*xiD�'�gI��LD�z4 +3���u��cmsZ^=bwtcbsw^8==88==�����s&�vLR�&+5��^��s!&�v��E&+5��}���s&�vR�-&+5��s��b!&�vV�-&+5��j�;�&69��j�;s\&V9���!@	�	/3�299106673#&'#f�m}wX��Ss�)*��7��4��!@	�/3�299103673#&&'sri�[wB�.�f!Js�;D�W)~�-��b�/39910!!-X��b�%���@	�/2�29910"&'332673V��	h)IUe`
h
�ى�18@C~��f��
	/�93104632#"&�8*(::(*8q9568877o�-�@	/3�2993310#"&546324&#"326-{fexyde|lB33B<94A�bwubbsw^8==88==%�Bq@
	
	/39310327#"54673�^*7A<�VHxDE�^
m�F�5Bm���$@	�	/�99//339910".#"#663232673+ROI"23b
s[.VNH 10c
q�%-%<=y�%-%;>y����!	@	
�	/3�29106673#%6673#�$n�%�:ae1e�%�:`�0�E?�0D�:?�0��s	�
�	/�99106673#�5�m1d�H�RJ�L�� +@		!"/3�99//393310673#'4632#"&%4632#"&A�!y3P�4&)17#&4�4&)17#&4���C�=4.4.21124.4.211��
&$T� ���?5���L�Zy����u
&(}T����?5�����
'+�T����?5����D
',�T���?5�����
&2DT���?5�����
'<
T����
?5����3
&v?T���#?5�������&�U���.&+555���$�����%���@IY??+99310!#��{��������'m�(�����(��R?�=����+}����?@ 

IY

IY
IY?+?+9/+99339910!!%! ! 32#"�u���������`D;b�s����3�?���n�he��p�����0,*.����TV�,�����.��
@	?3?99910!#&'#3Ӷ��W!G������Z��^����q�0���?�1H%�4@

IY

IY
IY?+?+9/+910!!!!!5��R��u��#H���y����}����2��#@	IY?3?+993310!#!#!���C������h�3J\�5@

	
IY
IY?+?+3933310355!!'!J�+�\`�T�o+��������Z�7��{�<j����"+P@)'

+,-**JY"$$JY??99//3+3+339333333310332###5#"$54663332654&+"33۬F�������)�-�������C���ι:�9����˴��������������E�ù�Է������;m��>@

IY

??339/3+393333310!##"&&5333332653##��-��������ϰ����-�z���!��d��ƻ���{P��9@ 


 !IY		IY?3+333?+93310"!5!&5! !!5654!����l��b:;b��k�����5�������v^�6`������x���N���<o%&,j�R
�!&+55��{%&<j��R
�&+55��s���s&~T�4&+5��Z���s&�T��/&+5����Ds&�T;�&+5������s&�T���&+5�����q�&�U;�4&+555s���\*G@$	'"+,'((FYFY$?3+3?+993?9333310%26554&# "323673327#"&'#P�����ѓ�����y�6)�T!.AQY
;�w����P�ԋ))TT\8B�t�Ir
wQVVQ���)L@('"*+#"FY##FYFY?+?+99//+?93333102#"&'#46"32654&##532654&����y���m�O��䞝]�V����p\���з��3*����&��4�����1%�������{�
�H!@
		??39/3910#4733>3�@+�?��^)+�k05�`&r<���g��m��|��q��`*;@ %	+,"(FY
FY?+?+9933310&&54632&&#"#"$544&'326!�t¤g�~Hp�QUak�ұ������a{�ο�����N�c��-?�>,OBGo[s���ұ�s��J5٠���Z���\%M@+#&'%%FY%%%%
!FY
FY
?+?+9/_^]+993310# 3267#"&54675&&54632&&#"!˔�ɓ�T�d����n�bk�a�d?^�O�=���Zb'/�K��b�)\��!-�*��s�o� 0@!"#FY?+33?9333105!#654&'&&54>7!����;}����}o�˼;p��(���������ߦbvI%m[���k8=$��r������D\/@		FY	
	???9?+99333104&#"#336632�z�����3�q��������H�QY��Is��J+I@'FY�		FY	FY?+?+9/_^]+99333310#"322!"!J�������y����
���j�v�����k��13���)�������H@FY?+?993103267#"&5NIW%ei2��H��he

�����F����F!"3@$#FYFY??+93?+3910#'.#"5632327#"&'&'#�:2C1:9D?[yX6k*#!0=JS�T	X�7�UF$
�<���13
yLS��`t������DHwH@	

	?2?9993103363#��S�����H��C�>��Q����q�o�1I@'-(%2300GY00&)%&%FY&#??+39/+99333310#"#6654&'&&54675&5467##5!#"33V���2_�T��6C�5Bs��Ǟ�ً��sD�3������Pb=$nZA�cG�47="Ȱ��'@�u�2��P�_sl��s��b\R���H6@

	

FYFY?+??+3393310%27#"5!##57!#}&0+T�#�ݏL�3u���F�JD��<J7��b\6@		

FY
FY?+??+99933310#"'##32%"32654&b��x�����!��z����%���^=��
�Ѣ���f����s�o�\ .@"!FY#??+9393310#6654&&'&&532&#";����6C�6C3na���O�65�r��
��P" kZB�_F�2(/&%��6!�3�s���H
0@	FY	FY?+?+393310#"5!!!3265'#"`{��P��������A����� �>������Ŷ�����H,@	FYFY?+?+39310!3267#"&5!57��P�/b#o0���הH����
}��JD���qH%@	FY?+?3993310"&332654&'3s�覞���"�$���
X������֌���s�L\"A@#
 #$FY FY??3+3??+93333310$746324&#"66�����σYQh���ڈ��y|fIN���#(�Zu�|�u#l���������&'��xr�����PN 9@!"!FYFY?+??+?9391023327#"&'#&&#"56�6N>,�>��T�0R?--<;s�;����Ь&F+%1N+[p��a���zJ�v���hD�cP����=@

FY
??3+3?3?933333106654&'3#$3Z��%�?���������i��x������&�	"����
�s���H'=@
&  ()&

FY#?2+3?39/99339310"54733265332654'3#"'#��7D�D9xk^i�j]kx7E�A9˶�D	A(�������؏}7�ɀ����������ֶ���	����&�j��
�%&+55�����q�&�j9
�+&+55��s��bs&RT!�"&+5�����qs&�T'�&+5��s���s&�T��1&+5����%&(j'R
�!&+55��B�F@&
IYIYIY?+??+39/+933310"'5326554&#!#!5!!!2�`67[eh���������C�����|p��q����^��������s&avZR�&+5}����8@IYIY	IY?+?+9/+93310"!!327# !2&B���)
��ɡ���yN�G�3����7�9�m_�X�R��j���6��TV�,��<o%&,j�R
�!&+55���`�h�-��#�#G@&
$%#IYIYJYJY?+?+?+9/+933310!!!#"'532>!3 32654&###������9TP�kE@2?0A+7DA�z:�L�Ʒ��f����H���y�>g����M���|�T�J@&IY
	JY?+??39/3+3933333310!!!#3!33 32654&##T����}����y9�N���f�����P���n���M���}B�:@

IYIY

?3?9/++3933310!2#4&#!#!5!!��٪}��}�����}�����~q�������s&�v�R�&+5�����^&�6DR�&+5���0@	
IY"??3+?3933310!!#!3!3�/��>���}������$�}�
=@ 		IY		IYJY?+?+9/+933310!!!!3232654&##}�����T^�L��t��ᆳ���������'Y��T���x�����%�����a��J�
C@$


IY
"IY?+33?3?+93333310#!#3!3!!J���q��������Ή��}���3����Y������(��<@


		?33?33933333933310333###V���9�:���R��������<�<�<���J��5�(C@$#)*JY
&&JY&
JY
?+?+9/+993310!"'532654&##532654&#"'6632�������`�g�������ᢉn�uTe���`������O�.2�������k�2JrKM��R�4@		

	?2?3993399333310333#47##˟4��	�˺�����J%��5���R^&�6�R�&+5���
-@	

?3?399393310!##33��\����y���<�:����-@
IY
JY??+?+93310!#!'"'53266!٪�%=]�~J;6;5O=]8�!�E��W�Y����q�0����+��}����2����n���h�3��}����&��Z�7����*@	

	IY?+?3993910"'5326733673%oT]`n�B�Ǽ�g��-T���+e�A��1/T5�껪O��j����s����;����2@	
IY"??+3?3933310%3#!3!3�����漢��}������-@IY	??39/+9933310!##"&5332673Ǫ��j�ߪ�a���\5'��E��yt7��y�1@	
IY?+3?33933310!!3!3!3y�P�X�X���������;@

"	IY?+33?33?933331033!3!33#ɪG�H�����������}�=@ 	

	IY		IY
JY?+?+9/+933310#!!5!3 32654&##����G����������������������~�
�
?@ IYJY?+?39/+?9333310333 #%32654&###3ɪ���������������������ܑ���{�R����
2@IYJY?+?9/+9933310#!3! ! 4&#!����#��+l���������� �=����:@		IYIYIY?+?+9/+93310"'632!"'53 !5!&Ӭ�H���9������S�c�1���3L�T������l9�"!������G@&	
	 IYIY	
	IY?+??9/+?+93333310! !#3!! 32#"���������dQ3V�������������qoU�P���7N�o�����2**.��3N�
=@ JY			JY	?3?+9/9+933310#&&54$!!##"!3{��������㷾{�b��3Ϟ��Jb�~����^���ZDw��T!";@ $#FYFY??+9/9+39333107$736632#" !"w��������>�k�����1��L�u ��h�2=&�:"!��T`�����b��s?h7�����LHI@& !FYFYFY?+?+9/+99333310#!! 4&#!! 4&#!!26){o�����������1{}���~5ko	~o��H�YQ���PC��L�DH@FY??+99310!#!D�����FH)��hH
C@$


GY
"FY?+33?3?+93333310#!#36!3!#h���V��+���
�l��{��
���G6�9���s��\H�F<@		





?33?3393333393331033###3��Ŷ�6�����7��F�����+��+��3��D��\"M@+

!#$"!"!FY""""
FY
FY
?+?+9/_^]+993310 54#"'632#"'532654!#5�7�M~f;�ɽ��~t��큷����ɘ���*�L���9%�g��G�Vc]���bH
4@

????999933333103#77#LQϛ���H�I�9�������\H���b&�6=�&+5�H
-@


?3?3993933103##3/��'���H���+��H�����H-@
FYGY??+?+93310!#!#"'532!��`�v6 s�#�����^�{���/F5@??3?399399333310%773##&'#3�+)ӓ:���5��+�]v���:��J��K�wF�In�bH9@		


FY/?

?3?39/]+99333310!3#!#Vf�����H�5���H��s��b\R�HH#@	FY?3?+993310!#!#!V�����H�������u\S��s���\F)�H$@	FY??+39310!#!5!������j��F�����H\q�FL@'		 
FYFY??3+3?3+3?9333333310#&5473%66F���������ٰ���{����%����$�.�&��D����T��'����'H[����H2@



FY"??+3?3933310#!3!33ݦ�y�F����{H�G��G�-H-@

		FY

??39/+993331032673##"&5B�[�i��i�q��H�p�8C���H;����oH1@		
FY?+3?33933310%!3!3!3��A�妏���H�G����
F;@	

	
	FY	"??+33?339333310%!33#!3!3�榝��N�妏��I��yF�I�)H=@ 

FY
FYFY?+?+9/+933310!2#!!5!4&#!! -9����%��L|���9���������]S���yH
?@ FY
	FY?+?39/+?9333310!2#!3#3! 54&#V+����9�#����z������H��H�����\T�LH	2@
FYFY?+?9/+9933310! #!3!2654&#VR����@������ˢ�H����\][U9��}\D@&		

FYFYFY?+?+9/_^]+93310"'53267!5!&&#"'663 V�v<�[��
��)��g�/7�P
��9�$�����6�#��������3\Q@-	
	 FYFY	
	FY?+??9/_^]+?+93333310#"'!#3!663232654&#"3�����ᦦ!����������%�����H�5����������%�H
=@ 


FY

FY?3?+9/9+9333103#&&5463!#!!!!"��;�ʵ�������z�����N�r��s���&Hj
�0&+55�D'f@:%%()!FYGY/	!!FY?+??9///_^]3+3+3933333310"'53254&#"##5353!!36632/O4:7�z���������o
1�t�ɉ���R���������T8O[��������D!&�v��&+5s���\D@&	FYFYFY?+?+9/_^]+93310"32&#"!!327y����R�91�m��)��	����t#* �3�����;�9��j��s\V���f�L�����&�j��
�&+55�����f�M��BHL@)	FY	FYGYFY?+?+?+9/+933331032!!!#"'532!4&##3 ����K�e��(��8 s�#P�}������������>{��[U����FJ@&

FY
FY?+??39/3+3933333310!2!!!#3!3 54&#��N�`�
�������F�;�����F�7�����\T��D����!&�v3�&+5���&\6��&+5���FF2@

"	FY?+3?3?933310!!3!3!#/���J����F�I�������#@	IY??�+9933103!#f��k��-�:����D�'@	GY??+�993310!#!3D����9HA��Ls&:CR�&+5��#!&ZCs�&+5��Ls&:v�R�#&+5��#!&Zv�&&+5��L%&:jdR
�/&+55��#�&Zj�
�2&+55��{s&<C��R�
&+5���!&\C�a�&+5R��q�/399105!R\٘�R��q�/399105!R\٘���R��q���1N��@	/3/3333210!5!5!5!N��R��R�1����D��	?�9910'673%b8{B%�Zy���D��	?�9910#75b5zF �d��r���?��m��F��	?�9910#&'7�%B{-m���^e���@
	?3�2910'63!'673�8z{;
��b8{B%��s��aZy�����@	

?3�2910#7!#675b5zF '`8}B
�d��r�[��zd4]�������8 �@

H����H����		H+++55{�C@!	


?.333?9/333933333310%#53%���1�1��L1�1`������_{�u@:
			


??99//9922333333333393333333333333310%%#553%%9a��1�1��Z++��Z1�1a��+����{�+�|�������^��
	/�93104632#"&�qlitsjkr�y~|{w���������&'%d��	;�	$/;F[@0
0B6<+%%+<B
GH33(?
"99-D
D
D?3??99//3333?33393333331032#"#"&5!2%#32654&#"#"&5!232654&#"#"&5!2�S]��]S���8��i�Ք+�S][YY[]S���7���8Q][YY[]Q뢛��8����TR���������J����������������ݫ�����������������?�
������Ru�@
//993310RVw��!w��'�E����G�Pu�@
//993310'7��u��uX�iG_^E�i�����J�&��y���??3310#��y����J�m!��&@			
?�2?399333104&#"#3363 LNPr[t`
K�!�TGiz���Xe��Tb#�K@(	NYLY


LY
??+99//+3+39333310!!##53!!!!�4�̦����D������
�+���DH�%p@@
	" &'NY
 ! NY	!!!?!O!	!!LYKY?+?+99//_^]3+33+39333333102&#"!!!!!!5655#535#53546�ɞ<��z~��\��\AJ���������P�G������!d�,��0�#���ϲ������!*`@7"&
		+,"KYNY*KYMY?+??+9/////++933333310%267#"&5#57733#!##! 32654&##N"V<nm���>b��4����@����4ȹ��Ru}���PE�Ӂ�GMR�������?����&q@?$
'(NYNY/	""LY"LY?+?+99//_^]3+33+3933333310 !!!!327#"#53'57#5332&��O����A%˪������.����'$�ɥG�5�m�9@-���B�A
�*,P�$a�V���
�+E@$% *

*,-#
'

??99//33?3?39333310##"&546323254#"%"&54632&#"327�Ք+��������������ʦ���hX!QP��bZN��J��������������۱���#g��!e%w����$=@#		%&#

/3/399//99333310%273#"&5556746324#"$}�_����``Nr��u�ίR�C>oզ����#q&򊟡����J��h{+�Vl�K����'+_@1	

"+(,-%((()JY(?3?3?+99//9933933333310!###33&53#"&5463232654&#"5!ǻ�L��������������"Q][OO[\RV��l����:��G����������rvusspp� ��%���O@'

	


?�229/33333333393333310##5!###33#7#q{��X�w��˴��gjj��/��R��/�/������P��vf���H4@ !
/?/2/39/]93933310"&546632!3267&&#"y�����1�R��QHbٓ2�X�z#�����������5Fi�)�|�5Bu��G����'\&{�@`���?555�� ���'�'@u��u��?555��G���'�&=@q���,?555��j���'F'@m��?1�?555f��5�(A@"&)*"GYFYFY?+?+9/9+933310#"&546327!"56632267&&#"5��쭬���a�+��>�0/�J���_�x�Pe�ee����5���3�]KZ�,!�%��Ɛ�al���v�'m�(@	

	IY??+999331073!!&'Ϧ��!=(����DhN��f��y�����!�#@	IY?3?+993310!#!w���X�
����ZL���1@		

IY	IY?+?+933331055!!!Lw��@��C����k�3l������h�)@	PY/+99105!h����%����@
	//9/933310##5!3o��!����T�w�-!-3@+%./"	(	/333/3993393310#"&'#"&54632632267&&#""32654&-��]�A<�X�����z|����}Bm62mHLda�Bm73nGLdeσ�jthq�����ׯ��[da]iWSjy\ba^kTUi��@
/2/393102&#"#"'5325}O,1>���J;=:���᰻��jb�-/p@@(10'PY/	*@*$PY*@PY/	@PY /]�+�_^]+���+�_^]+�993310"56323267#"&'&&"56323267#"&'&&P69l�CpXM[-5�6e�CoXI[19�5j�EtRE_17�3d�EvOTU@9�n%!B9�m%�D5�m "B7�n !"h�)F@&	

PY
	PY/3�+3/_^]�3+393310!5!!5!!!!!'}��T�-�}m�����}���9���7��h)�&+�t	�?55��h)�&!+�t	�?55o=�	 @

	//999933103#	o�H�<Hb���=���!������&IL���&IO����
@	
�/2�29910"&'332673H��
�	[qgc��ُ�hRXb�����VH@

	FY?+?9310"'532653+_;ECNI���UW������u	�	
�	/�99106673#�'
�X/Z�7�Q3�Fq�;o��	�	
	�/�99106673#q3�b7Z�T@�53�B��!	�	
	�/�9910#566735�c1\=�1=�9'9�� @	!?3?399331032654&#"!"&5!2�R^^VV^^R��;�����������7����J��
<@		 ??39/]33393333310##5!533!547�}��n��}�����eC��ÆK'--�;7��+@	
!?3?39/3933102#"&'532654&#"'!!6H����J�)8�6_nmf9L;!�>h�{���"&SYNX)�h�)9��#6@!%$!?3?39/]93933310632&#"36632#"&2654&#")��J14S��
qU}�����DQcXVUpj��r��+;�~���c]cO[Z;Y|9J��@
 ??39310!5!�^�9V��J�t^��39��"-?@"
&+
./  ))))
!#?2?39/]39993333102#"&5467&&54632654&''"654&d|�������IUJ9�5TVZT]QHF�DKDQ�N�vh�LJ�q��tEt..]Df~�f<II<?O
"T�<9/G!6a9<#9��"<@ 
#$
!?3?39/]933933310#"'53 ##"&54632%"32654&���S11]
#tA��������Q_UWTsgF��tF34�����[_WQ_U>arT����#'+/37;?CGS[kt|��@�A@=<10TNXHvkp`zg��ED)(%$
	�;g`87/k4,H# N��
*BZQ�\t\)AF>duulE=�}VKkvk&2%1
BA>\=l
12k\lkkl\-,9854! /333333333/3333333339///9999993333�2�2339333��233933333333333333333333333310!#%5!#533!5353!5!!5!5!#3#35!#35!35!#35#3#3#"&546323254#"%32##32654&##32654#"'53253T/��0m�o��m�I�����mmmm���0oo�w��oooo�mm�����~��s�����mp.,=.m^�{B.$*/;J1%Z^4+V}i�0o��o���/�mm��mmmm�oo���;mm�Joooo�/y�hI�����������aCS1BD5QYb" "�+%J��
fV��r_cT���*.@%	+,(""//9///33910	54676654&#"63232654&#"���T�V�,AgI��O�GR�Z?>1HT;GFBIHCHE�V�W��/2A1R~X��8*�P:/5K6DpJ;��?HI>@IH�����W!&7L���&+5���D�
���+-6f@94%.+-%78GY!.!GY+...	..((1FY(FY?+?+99//_^]3+3+933333310! 47654&#"'6323 4'&$&546323%&#"V���w$ 6!S_X]�w�ɠ���*����{]aN.A���nX9{z/#	v']]#��:�p?,i������ׁ��_K��{�(@
JY?+??993106632&#"#39z�M\:0((;V|e��#��#7l0�8���U��/���wH)L@'!!'

*+FY$FY?2+3?+339/99339310"&54!57!##"'#32655332654')�LJ���uȹ�DD��?Blu]l�k]umo���JD����綶΄��g���}��z��������qu&0v�T�&+5����!&Pv��-&+5�����&$[5��^���Z&D[��������&2\�G	�?55u��5��@	/3�2993310#"&546324&#"3265}fexxee~nB33B<95@��axubbuva9<<98==�h��@		/���9310673#%47#"&�F�)w1N���y%]7C��zN�9v�=H)5JD���'I�&ILm���'I�&IOm}��d!<@"#		IY	IY?+?�+999333310! ! >5332#"��������aCE�2:��h`�u��������q�jh��Cfi��'������1+'1��s���"<@#$

 FY
FY?+?�+999333310#"&532>5332654&#"b����|�ى3:�yfG����������%��ӊ�+�Acn��&������������{3@
IY?+?�39999333310>53! 533265:F�!������Ԫ�Ƹ���>pn�����������F���������D@"



FY??+?3�939933333310326536653#'##"&5Lz����RJ� ���	4�o��F�;����>y�������RU������S��!C�����
��!v�������R����s�@

/�23339310#'6654&#"563 �s�
iVNCI> &E׌"q�2++)d
�;����}�
	/�33104632#"&�;;*(::(*;�9669777����s&(C��R�
&+5���Rs&�ChR�&+5��s��!&HC��&+5���b!&�C��&+5�����1E@$"*'/		'23IY((,%%IY?3+3?39/9+3933310"'632#"&'## 32&&#"327332�<^-E~����l�SP�k����|F-]<��ϻ�f�f��Υ/)�P�������a-32.�wSxP�)������L�7LK0(H(@


??339?910#33663363#&'
���� .J���	-
����۶}!�3��H�I]�5�$���,��R����Z\�L@(IYIYJY?+?99//+3+393333310!3!!3 !!!32654&##?���^�1�����h������ڶ����d��f�+���z�'G@&


FYFYFY?+?3�9/++393333310!!! #!#5353! 54&#�X��?���!��1��H���ͦ�������\T��!� J@)!"IYIY	IY?+??9/3+3?+933310"!!327# !#3!%2&&����=	��˜��������dq0նHd�3����7�9pT�P���3N\�0&����\!Y@2
	 "#
FY
	FY				FY?+??9/_^]3+3?+93333310"'!#3!6$32&#"!!3267w���ᦦ!
�Q�62�e����	��=wbn
��H�3� �3�����%�9m�4@
IY?33?9/9+39310####3#!'&'�����߲h�g��\LR8@��V��V��J?ϐdb�
yH5@

FY

?33?9/9+39310#####!&&'#�Ѭ�q�sͬ�!+8"	H����H�-l�j\�^�F@%

		IY?333?39/93+33933310####!#3!3#!&'������"�_����f��f>v#��P��P��P���n�JH5V/Ch�HM@+


FY/?
?333??9/]93+33933310#####!#3!#!FΪ�q�nѬ�ߦ�^�h
 YH�����H�3�s"_���"K@( !$#!!IYJY"?33?9/33+3+99933310#.####"#>75!)�Zv�d2���#DeY�[cA ���/c�v�e��
{���H���;�o`&�B�'_o�7ş�I�9H #N@*!"%$"  "FYGY# ?33?9/33+33+99933310#.####"#>75!���WoI1���":TL
�KR8'���0InW�� ��%Hi��0Pi�qPWG��
@^��P=iO2`i������$'a@5!&#'%'"#	)(#$&$&IY!IY'!!$?333??9/33+33+99933333310#.####"#67!#3!5!=�]x�e-���Fi_�^dB!���78�R���h��
{���H���;�hc(�D�(_l�7��:�P���酙�7��H$'g@:!&#'%'"#	)(#$&$&FY!FY'/!?!!!$?333??9/]33+33+99933333310#.####"#67!#3!5!1��XoI0���":VJ
�
KT7&���/%�ͦ�5��!��%Hi��1Ni�rPVF��?\��Px(�H�5bi���?�N5�K�@M!?FF
?7C<*-(LMIJYI941../.	.*@CJY<**$JY*
	IY
IY#IY"?+?+�+?+39/+9�_^]9�2?+933310327632&#"#"&5467665!#532654&#"'67&''536632&#"�WYaxxF�GP�Diii����̵�@���ᢉj�nV��9u1{\�\�@20+,o0�������抆�72'�3}�~�	�����k�7ErrBy4;�sVq
RG��������	7�{NF�@N)6. >2@<)GHD>AGYAA/A	A>&FY#FY3232FY&#33#& >>8FY> ",GY?+??3+9///+9++�_^]�+99333102&#"32772&&##"&5467$54&##53 54#"'67&'5366�3-)/g-z����]m0KYVz�}'T7��\����N���w7�J�X;|~\g{K�X�Np
O>�k�9Gʔ�*,1+'�wpt}�aZ���"$�7ub4�nU��m��u������}����G@%IY		IY	IY?+?+9/_^]+99333310! ! 2!"!&��������`D;b�a�
�+
��������n�he��p�D���������s��b\I@'FY

FY

FY?+?+9/_^]+99333310#"&532267!"!&&b����|�����
�i	����
��%��ӊ�+��M����X����H� @JY
??9?+3910"#367>32&�;N9����RH# F�;TnY*O87g�����VǏ����A�=R@
GY
??9?+3910!336>32&#"��j��dR`%G[T-&/:�H����dv5z{4
T\���Hs&�v�R
�!&+55��=!&�vd
�"&+55}�	��.D@&!.'/0%*JY% 	IY	IY?+?+?393?+93310! ! 32#"%33663#"'532677T������C,#E����������o��NS�+E��LJ7B^u#=���o�hf��p�����1+)/��A���f,��� ���gY���s�{\&R\u}��-(Q@*
&
""
)*$"&
&IY
IY?33+33?33+339333333310#"'$%6326632654'#"'���w|���+|y-�!ʽI6n��ʽnq����s,oo)�61�,ll,�s����)0&V)1��/'XV'��s����-P@*	+%# 	./(%++FY 	FY	?33+33?33+33933333310#"&'&54766326632665%#"&'���	@89=	����>98@	��P}�<5g�|��
=35<�}%��%6-+8$&�� $8*+9&����*"Jү`>*  ,�}��;ETU@.C7++&FKPH<7
UVR@H:"@:@IY(:4IY.4?3+3?3+3����29/393310#".#"#54632326732#"'632!"&'# 32&&#"5654.5432�T�xf+/<}tp:pw�N�(X�=7�]�ҥ�<_+Fy����h�LK�n�����yF+^<�����x$\8C�y$+$43gn$,$��B?9HN-(+�R�������b(0-+�uUvR�+�����h�=H)5IDs��*?N\@3((,"@E
JB6
OP2:?--6LB
@
FYFY%
FY?3+3?39++3���23�293310"'#"32&#"!273 4&#"'6632#".#"#5463235654.5432+�^\��Ϻ>w(9YGtm1{p>oC-nsGY9(w>��QT�xe+k}sp:qv�N���w$\8CAA#( �3��^P*&���3� ������x$*$fdo%+%ݡ>H(8JD^��
@_@40$96>6)$AB-'-IY77!'		
@	H
@';3!3IY!?3+3?3�22�+239/9+3933310#'##'##'5"'632!"&'## 32&&#"3267332�P 2�1!1�/!PC<]-F|���t�L	N�p�����~F-]<��ҾA�3�f��ԥ�gggg��+)�P�������c001/�rUvP�)������&&�7LJ1(�
*?@$$+,(		
@	H
#??33�22�+239?910#'##'##'5#33663363#&�R2�11�2P�'����')#���	-
����۶}!��gggg��%_��H�Io�#Q����,��R����Z\}���-@	

IYIY
??+?+93310"!27## 4$32&H���
o9������H��G�3�������t��m�V�T�Ns��\/@	FYFY?+?+?93310"32&#"3267#u����O�01�h����5P9�+"�3�����n�j��u/@!


	?�9910'%7%7%�y���B!��C!�v�!D���A9��CB�s�d�u�=C���s����s����@

	/332993310#"&5463!6632#�*03)*6�+/3-,6�-2255).0138(����@�	/�233991027632#54#"##5x��Qot}j+fy�Tb;:odf$+$y���5@


/�93104632&�C8\$w��8EL6(J@���5@

/�93105654.5432��w$\8C��@J(6LE)����(6DR_m�@I_(DZ">R6mL0gno:HHAOED>LVcc\jf_Zm,,%3/"(6OLjm3663mjLO	
	/3/39////////333333333333333333910&&#"#632&&#"#6632&&#"#6632!&&#"#6632&&#"#6632!&&#"#6632&&#"#632!&&#"#6632o<EN2K�]qO<EN2Kdg\s�<DN2Leg\s�/<DN2Leg\s1<DN2Leg\s�/<DN2Leg\s�<DN3K�\s��<DN2Leg\s�,,)/�e]��,,)/Yif\-+'1Zif]-+'1Zif]�-+'1Zif]-+'1Zif]�,,(0�hZ-+'1Zhf\)�}�'.5>4@%% >:),25	
?@;+.6/'$3//9910#67'66737&'&&'57667'67'&'7&&'7F$a5;Ia4#G�A݁�hB�O݁�C�CE�x����E�x+REC{Lj'ZC�&#B�O݁�G�A܂�Ia5;F$a5�'XDnX��Y?DnX���F�c��E�<F2�4��^"Y@/
	 	
$#	IY"?2�2�]2??+9933?9333333103333##47##"&'332673ɡ
4���Ŝ�	�ɺC��
�
]nic	�����v�S���}%���5+��lN]]�����O@*
	! FY	"?�2�]2??3+?9993333331033##47#%"&'332673L
Qϰ��}����칪
�Ztgd
��H�j����G��y��h�ZH���fTZ`��/}�M@)IYIYJY??+99//3+3+93333310353!!3 !!#3 4&##/��V���J���o�D�{���������d��f�+��LK@(FYGYFY?+?99//3+3+93333310!!!2#!#5353! 54&#V'��@����!���1����嚛��������\T�y�H@)


JY	JY		??9/99++99933310'###! 327'7654&##yslxd�f�������WLll�����9�T������
�R�H�����u\)U@1'%"$#
*+%" FY	 FY?+???93+9993333310"&'##336632'"327'7654&�k�<��@�m��sd�Gm����/)yj�e�OR�"�=4�ZP����P���%���P�g���/�
<@


IY

IY
??+9/3+3933310!!!##53�k��X��������w���BH
<@		
	GY

FY??+9/3+3933310!!!##53!B�Z���������������A@#	IYIYIY??+9/+?+933310"#!!63 !"&'53 41dZ�I�aZy@U��S}F{����}����
�������1����
�HA@#FYFYFY?+??+9/+933310"'532654&#"#!!63 F�et{����EJ���R;�
<�?�����%H�������������M@)
		
IY
"?33??+933333?393333103333####V���9�:���ڴ�^��������<�<�<����}����HK@(

	FY	"??+?3?339333339333310333####3��Ŷ�6p��^�����7��H����Z��y-��-��5����J�B5�&�X��D�B\&���+�;@ 		
	IY"??+??39933310%3###33��f�陪�������}ň����+�����=H:@

	FY"??+??3993331033###3/��'��T���H��X��{+��H�����8@

?3?3993333310#3733##�}��}}���b�L}�k����%�]�����\���;H:@

?3?39933333310'#3733##�w��w���<��Ձ�y��H��yJ��%k��;�/��G@&
IY
	?3?39/93+3933333103533#3###/�������b�񙪚����n����ņ��mM@+

GY/??9/]3+3?39993333310353!!37663###��}��7(p�D��}}��Z����[7J0��-��j�f���
5@


IY?+??399933310!3##!����b�욪����%����ň��)�H5@		



FY?+??399933310!3##!)۶�'�
���H�����+�������D@$	
	IY
	IY"??+??39/+93333310%3##!#3!3�����������}��P���n����HN@+



FY

FY
"??+??39/_^]+93333310!33##!#Vf�������H�5�G��y�H�o�
?@!

IY

IY?3??+9/+9333310!#!#3!!o�����������P���n��H
I@'

FYFY?+?39/_^]+?9333310!!!#!#Vf������H�5ˌ�D�H��G@&

IY
IY

IY?3?+9/+?+9333310!#!#!63 !"'53 4#"٪�D�D}2Q���{�z��*����a������1�1����
�HG@&


FYFYFY?+?3?+9/+9333310"'5324&#"#!#!632�aml�CH��ߦoKB��
<�?����)��HH�'�����}����(4P@,/#) #56&,JY2&&IY
IY
IY?+�+?+9/99+933310327#"'# !2&# 327&54324&#"66��tBZN=8[��f����I:\/TZ�3��6.V\Ư���g]^g]Sfs����V�d$�Vx�#������
g��
������ɰ��UC�s���\
5P@,&,4,/$&67)GY
))FY1FY!FY?+�+?+9/99+9333106654#""'#"&&532&#"3267&54632327�D?DS�HKf��`{��z��[M%6O����%5�����k^4CB1'�^�5,�n�}�cM(���0�����	����}�@���}�B��&&%��s�B�\&F���Z�2@		
IY	"IY?+3??+93310!5!!3##�1J�1������}��})���H4@		
	"FYFY?+?+3?93310!5!!3##���h�����������y��{�<�H
)@
???39939310#33673T��R��S!F�R��L���a��e��{�:@
		IY		??39/93+39333103!!#!5!53=���+�լ��-�����;���d�3��H<@GY??3+3?393933310#!5!33673!T����T��S!F�T���k�H���a��e�������7@ 	
	
IY"??+??399310%3###333��^�w�p��;�kn��;���}��}����C�L'��7H9@!
		

FY
"?3??+9?9310333###����! ���+��E��ʼ1�\���^��{��D����@@"

IYIY"??+3?+3?933310%3#!!5!!!3������V/�%��}���})���F?@"	


FYFY	"???+3?+3933310!!33#!!5!y��F����x��P�����I��y�����h�;@	IY
IY"??+?39/+9333310%3###"&5332673ǡ����j�ߪ�a������}\5'��E��yt7�����H;@	FY	FY"??+?39/+9333310326733###"&5B�[�i����i�q��H�p�8C�G��{�H;������J@&
IY		?3?9///3+3933333310 333673##u�5���}������}qE��wv\��
<�JXA���HJ@&FY	??39///3+39333333103673##5#"&5B�wq����vw��H�p�-��Y���[�ꪕ����/@	
IY	??39/9+99333103$32#4&#"#ɪ��ߪ�k������\����1xv"2�5�BH/@FY	?3?9/9+9933310!4#"#36632��X�w��_�r����1J�-H�E>���f=��?� 'Q@*$%()IY$IY!IY?+?+99//3+3933333310473337! !3267# "&"!&=�q"M)(���eʍr݂��������n��I62<g+*G����E����+�'dLv#���	�3���Z&L@(

$
'(FY# FYFY?+?+99//3+39333310"'$54733376632!3267"!4&J�����j"������e�bX����=���E2/;g#���i�� *�&!㤞��=��?�")]@1	&!"'"*+"" 
IY&

#IYJY?+?+99//3+3??9333333310$"&5473337! !3267#"!&��������q"M)(���eʍ��L��n�Z1vuI62<g+*G����E����+�>������	�3���Z!(X@/

 !&!
)*!"FY%"FYFY?+?+99//3+3??93333310&'$54733376632!3267#"!4&տ����j"������e�b���D��=�
��E2/;g#���i�� *�A��H������TV�,���`&�6T�&+5���&�6��&+5��B@%	
IYIYJY??39/9++?+933310"#337 !"&'53254$^�_�����Ob���R|Fz����{����<�T�������1
����
!HB@%

FYGYFY??39/9++?+933310!#33#"'532654&#"T���7�n̅�_.lG����R\H��������<�&��������9@IYJYIY"??+?+?+933310%3##!'"'53266!ٸ�Ŝ��%=]�~J;6;5O=]8���}�!�E��W�Y����F9@
FYGYFY"??+?+?+933310%3##!#"'532!߰��}���^�v:q�"����y����d�
����=@ 
IY	IY?+??39/+9333310%!"&'53 !#3!3��RzM{������������1�#�P���n��
bHG@'
FY
FY?+??39/_^]+9333310"'53265!#3!3ӄ]of}v����d��
:�=����H�5��������D@$	
	IY
	IY"??+??39/+933333310%3##!#3!3�����������}��P���n���FD@$


	
FY

FY
"??+??39/+933333310!33##!#Vf����}����F�7�I��y�F�����=@ 	IY
"IY?+??39/+9333310!##3#"&5332673Ǫ����j�ߪ�a������5'��E��yt7����-H=@ 		FY
"

FY
?+??39/+933331032673##3#"&5B�[�i����i�q��H�p�8C����
aH;�����)�H@%	IY"?3?3??+9933933333310!##!333##47#P������Ǟ���/�^��J����}��������F?@ 
	

FY
"??3+??39939333310%7733###&'#3�+)Ӱ��}�:���5��)-�]v�I��y�:��J��K�wF�-n��TV�,��^&$69R�&+5��^���&D6��%&+5��%&$j=R
�$&+55��^����&Dj�
�:&+55���������^��s\�����^&(6R�&+5��s��&H6�&+5u��X�=@ 		IYIYIY?+?+9/+933310"5663 ! 5!27!���s҆Ko�����/�������5L�& �q�����q�F
�N
����f��\;@		
FY

FYFY?+?+9/+9333102#"55!&&#"566267!����������b�_Y�����Í\��������i̻!)�("�������u��X%&�j�R
�/&+55��f���&�j�
�1&+55���%&�jR
�'&+55����&�j�
�'&+55��J��5%&�j��R
�>&+55��D���&�j�
�8&+55J��7�@@#IYJYJY?+?9/++3933310! '532654&##5!5!�$����`�j���ߌ�N�?	���O�.2�����ޙ���H@@#	FYGYFY?+?9/++3933310#"'532654&##5!5!������ꊷȡ���y��8�rʈ��F�V����r��{���R�&�M�R�&+5���bb&�M1�&+5���R%&�j�R
�%&+55���b�&�j=
�#&+55��}���%&2j�R
�-&+55��s��b�&Rj
�.&+55��}����~��s��b\��}���%&~j�R
�/&+55��s��b�&j
�0&+55��=���%&�j��R
�0&+55��9��}�&�j�
�0&+55������&�M/R�&+5���b&\M��&+5�����%&�j;R
�,&+55����&\j�
�+&+55�����s&�S�R
�*&+55���!&\S
�)&+55����%&�jjR
�)&+55���-�&�j
�(&+55���	-@		
	IY	"IY?+??+93310!!3##�?�k������}��}���BF	-@		
	FY	"FY?+??+93310!!3##������F����y���
%&�jR
�-&+55���y�&�j�
�,&+55��/�u�&������uBH&��u���u��&;�X��'�u4H&[����;@"

	
IY
?3?39/993+3910!33!!##!3�w�kl��p<�����w�p����Tb��E����D��}�'H;@"

	
GY
?3?39/993+3910!33!!##!u���! �����h���ʼf��w�\��/��
��D��7�
4@IYJY??+9/+99333104$!33! $#"33�$ ƪ�c����
��¶�����p�J��|�����s��7G���w�#F@$

##$%IY  JY?2+3?99//9+93339310"&54$!3332653#"&'#"!265N��*"���dy�ϸv�3q)���!�����p���{n���RZ������wps���".Q@),  &&/0
*FY##FY	?3+3?+9/99?933339310%2653#"&'##"323&&53!26554&# �vk�Ƚ��+K������j�?�m��������w��9����[qq[)/MUp�������#��N��N����*K@((""
+,JY  %%IY%	JY?+?+99//+9933310#532654&#"'663232653#"&'&&���՚�g�gT]�����bl|wp�ҽ�������l7ErHPħ��3�іy��)���Ȗ�P���\%K@(
$$ &'!FYFYFY?+?+99//+9933310%23# &&##53 54#"'6632Bݦ������o!�K�M9U�h��c{	w9����McX���$"�($���9zj�N����#J@(#!  #$%JY##IY#!"JY?+??+9/+99333104&##532654&#"'66323##������ᤇi�iTa������ì��������k�:BrJNħ��������}P��ZJ@(
 FYFY"
FY?+??+9/+99333104!#53 54&#"'6323##�˖u9�w��=�˿��~p���-Ǎ�RPF�J���9%�f���y��!�#:@##	$%IYJY ?3+3?+9/93310!#"'53266!32653#"&5�H+LS�dE@2?1@,8J7�ospq�ͼ���D�f�>h���ωyy��)������)F:@FYGY?3+3?+9/93310323#"&5!#"'532!�hwզ�����^�v:q�"q���
;������=���d�
����^�C@#		IYIY?+??399//+9333331032653#"&5!#3!3�nspq�ȿ��'��٪��yy��)����3�P���n����HM@*


FYFY?+??399//_^]+93333310!3323#"&55!#VP�jwզ������H�5�=��9������s�H}����:@IYIYIY?+?+9/+93310!! 4$32&&# 3 !f4�������U�x�SBZ�W��������V�����`�T�1'�&.������s���\:@FY

FY
FY?+?+9/+93310!! !2&#"3265!�������C!ԯ;�����ũ���?C��'+P�J���ߠ�����9@


IYIY?+3?+9/933105!!323#"&5<�/wr�ӽ�����h�{�)�����)���F6@

FY

FY
?+?9/+393310!323#"&5!5!���mvצ�����X��ɉ�A������?�o��X�&G@& $## '(#JYJY	JY?+?+9/+99333104$3 &&#"33#"327! $54675&&���^i�e��������ʷ�ǯ�����ϼ��\�ƐxD4{r�������\�M�ŗ����Z���\����uk�&������usH&�������&$g���^���Z&Dgy���&$f�R�&+5��^����&Df��)&+5���&$w�R
�&+55��^��A&Dw�
�+&+55���&$x�R
�&+55��-���&Dx�
�+&+55��J&$y�R
�&+55��^���&Dy�
�+&+55��b&$z�R
�-&+55��^���&Dz�
�C&+55����s&$'g�K+R�)&+5��^���!&D'gyK��>&+5��&${�R
�&+55��^����&D{�
�-&+55��&$|�R
�&+55��^����&D|�
�-&+55��X&$}�R
�!&+55��^���&D}�
�7&+55��^&$~�R
�'&+55��^���&D~�
�=&+55����I&$'N-dg��&+5��^����&D&N�gy�%&+5������&(g���s��\&Hg������&(f�R�&+5��s���&Hf��&+5����/&(R��R�&+5��s���&HR��$&+5���o�&(w�R
�&+55��s��\&Hw�
�!&+55��]��&(x�R
�&+55��J��&Hx�
�!&+55���9J&(y�R
�&+55��s���&Hy�
�!&+55����b&(z�R
�*&+55��s��&Hz�
�9&+55�����s&('g�KR�%&+5��s��!&H'g�K��4&+5��TV�&,f�R�&+5��{��&�fs�&+5��T��V�&,g������f�&Lgb��}����&2g��s��b\&Rg���}����&2f�R�&+5��s��b�&Rf��&+5��}����&2w}R
�&+55��s��u&Rw�
�&+55��}����&2x}R
�&+55��a��b&Rx�
�&+55��}���J&2y{R
�&+55��s��b�&Ry�
�&+55��}���b&2zyR
�6&+55��s��b&Rz�
�7&+55��}���s&2'gK�R�1&+5��s��b!&R'g�K�2&+5��}��ds&_v+R�+&+5��s��!&`vm�+&+5��}��ds&_C�R�#&+5��s��!&`C��$&+5��}��d�&_f�R�&&+5��s���&`f��'&+5��}��d/&_R�R�+&+5��s���&`R��#&+5��}��d&_g{��s���&`g�������&8gJ�����9H&Xg�������&8fTR�&+5�����9�&Xf��&+5�����{s&av�R�%&+5������!&bvy�&&+5�����{s&aCZR�&+5������!&bC��&+5�����{�&af`R� &+5�������&bf��"&+5�����{/&aRR�%&+5�������&bR��&+5�����{&agL�������&bg�����{�&<g����H&\g�����{�&<f�R�
&+5����&\fj�&+5��{/&<R��R�&+5����&\R��&+5��s���&�B�����!	@
�/3�2339910#&&'53#&&'53��`4�%�c1��`8�%�c1�*�?=�D,�?=�D�q��
(@


�/3�99//9339910#&'#57673'673#��^pcra^5p4�B�PI6�Sx`�K[eA<{M^��[pn`����
*@


�/3�99//9339910#&'#57673%#&'53��^arji^5p4�B���_xT�4K�Ae`F<{M^��^pla�q�{�
4@!


�/3�299//93339310#&'#57673#'6654&#"5632��^pcra^5p4�B��P
9?9+.7��K[eA<{M^�{gQ�	 &%P�h��%:@		'"	""�/�9///3�339339910".#"#663232673#&'#57673�-%GC?(*[
eK%IC>(*Zc^^arji^5p4�B�5%12jq$11hs��Ae`F<{M^��y���$@

@
�/3�2��339910673# 332673�^P1�Vw`>��f	LjbVi��her]��H9A@x��y���$@

@
�/3�2��339910#&'53 332673��^wV�4K5��f	LjbVi��]rla��H9A@x��y��.@
 �/�239/�2339310#'6654&#"5632 332673�1R
9B9,%$>����f	LjbVi�yd)Z	 %%N��H9A@x��h��$0@"		&@	!�/�2��3�39/3329910".#"#663232673 332673�-%GC?(*[
dL%IC>(*Zc��f	LjbVi�3$02hq$11gr��H9A@x�1�Bm@

/�293104'3#"'5326ߋ{�fcA2 6%3�g�x�[gl
0�uq�@	

/�299310%#"'5325q�8<)=^�����d0�uq�@	

/�299310%#"'5325q�8<)=^����d%��4C��xs��s(@	KY	&MY?+?+993310#"3232654&#"���������������/����55����������-7^
&@		??99//993310!#47'37�C>�Z�1�C0pr#)�s,@
KY&LY?+3?+9310!!5>54&#"'632!�R��q,�wX�\Z���ڂ�����/whSAWg=Jm���s���^��t'G@&"
()KY
%%KY%&
KY
%?+?+9/+9933310!"&'53 !#532654&#"'6632�����t�[_�`{�^���ȓ~`�mTZ���������#,�/1)
���kz4FpGQ���f^
B@!	MY	$??9/933+393333310%##!533!47#f٨�2����
)D�9��s}�D\��V\�����_:@KYLYKY%?+?+9/+933102#"'53265!"'!!6-�	����F�e���^�V7��%s&���O�-3��27���I��u��/�^��+_@LY$??+9310!5!^�����������h��)�j��%t%A@""

&'MY
KY&MY%?+?+9/9+933310!"'532##"&5432"326654&&%�htDPf�7�r���x�����[�XR���)3SW������0����J�Fi�f���'I�I\���"3Z@.,00.*&&(
(.54+1$-/-)/##(
())?3/3�2/3993339933333310#"'53254&&'&&54632&#"##33#7#H�|�Jjw�6UxQ�n}\"dS<K+_�P��w��˴��bm!l(d!(!,[LVi'c%.($$2Z��/��R��/�/������Z�&7z?����F&Wz�q�7\*G@&
**$+,!'FY$!FYFY?+?+99??3+9333310%26754&#"47##"32373#"'53265L��������	p������{���K�v��w��+������k$c�-
1������F�*.����q�7!&�K�9&+5��q�7�&�N�+&+5��q�7�&�OV�4&+5��q�7!&�:w�/&+5�s��??91033ɪ��J���s&�C�|R�&+5���<s&�v�*R�
&+5����is&�K��R�&+5��8%&�j��R
�&+55�����/&�R��R�
&+5����K�&�M��R�&+5����S7&�N��R�&+5��V�B��&�Q1���1&�OR�
&+5�����&�-;����
'��T���?5���s����8%&�j��R
�&+55���s����8%&�j��R
�&+55���s�����s������&�f�R�&+5������&�g}�2I�6$$�q7)9):)<D��F��G��H��J��P��Q��R��S��T��U��V��X����q��q��q��q��q��q��������������������������������������������������������������������q���q���q���������������������������������������
����������������!��$)&)+��-��/��1��3��5��6)8:C�qD��F��H��J��V�q_�qb�qi�qy��z��{��~�������������������������������������������q���������q���q�����������q�)�)�)W��X�qY��`��b��j��r�qs�q}�������������������������q�q���q���q����������	�q
���q�����q�������q�q���q ��!�q"��#�q%�q&��'�q(��)�q*��+�q,��-�q.��/�q0��1�q2��3�q4��6��8��:��<��@��B��D��J��L��N��R��T��V��X��Z��\��^��`��b��d��f��h��j��l��n��oqs�)
$�q
7)
9)
:)
<
D��
F��
G��
H��
J��
P��
Q��
R��
S��
T��
U��
V��
X��
��q
��q
��q
��q
��q
��q
�
���
���
���
���
���
���
���
���
���
���
���
���
���
���
���
���
���
���
���
���
���
���
�q
��
�q
��
�q
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
���
��
��

��
��
��
��
��
��
��
��
!��
$)
&)
+��
-��
/��
1��
3��
5��
6)
8
:
C�q
D��
F��
H��
J��
V�q
_�q
b�q
i�q
y��
z��
{��
~��
���
���
���
���
���
���
���
���
���
���
���
���
���
��q
���
���
��
�q
��
�q
��
��
��
�
�
��
�q
�)
�)
�)

W��
X�q
Y��
`��
b��
j��
r�q
s�q
}��
��
���
���
���
���
���
���
��
�q
�q
��
�q
��
�q
��
�
��
��
�
�
�
	�q

��
�q
��
��
�q
��
��
��
�q
�q
��
�q
 ��
!�q
"��
#�q
%�q
&��
'�q
(��
)�q
*��
+�q
,��
-�q
.��
/�q
0��
1�q
2��
3�q
4��
6��
8��
:��
<��
@��
B��
D��
J��
L��
N��
R��
T��
V��
X��
Z��
\��
^��
`��
b��
d��
f��
h��
j��
l��
n��
o
q
s
�)-�&��*��2��4��7�q8��9��:��<�����������������������������������������������������������$�q&�q*��,��.��0��2��4��6��8��:��G��f��m��q�qr��s��u��x�������q�����q��������q�����\�q���������������T��_��a��l��|�\~�������������������������q�����������q�����������q�����\�����\�������\�������\���
�����������q��I��K��M��O��Q��S��U��W��Y��[��]��_��a��c��e��g��i��k��m��o��q��s����q7��$��&��q������������������������������������������&��*��2��4��7�q8��9��:��<�����������������������������������������������������������$�q&�q*��,��.��0��2��4��6��8��:��G��f��m��q�qr��s��u��x�������q�����q��������q�����\�q���������������T��_��a��l��|�\~�������������������������q�����������q�����������q�����\�����\�������\�������\���
�����������q��I��K��M��O��Q��S��U��W��Y��[��]��_��a��c��e��g��i��k��m��o��q��s����q$�q$
�q$&��$*��$-
$2��$4��$7�q$9��$:��$<��$���$���$���$���$���$���$���$���$��$��$��$��$��$��$��$��$��$��$��$��$$�q$&�q$6��$8��$:��$G��$���$���$���$��$�q$�q$_��$I��$K��$M��$O��$Q��$S��$U��$W��$Y��$[��$]��$_��$o��$q��$s��$��q%��%��%$��%7��%9��%:��%;��%<��%=��%���%���%���%���%���%���%���%��%��%��%$��%&��%6��%8��%:��%;��%=��%?��%C��%���%���%���%���%��%��%��%X��%��%��%!��%#��%%��%'��%)��%+��%-��%/��%1��%3��%o��%q��%s��%���&&��&*��&2��&4��&���&���&���&���&���&���&���&��&��&��&��&��&��&��&��&��&��&��&��&G��&_��&I��&K��&M��&O��&Q��&S��&U��&W��&Y��&[��&]��&_��'��'��'$��'7��'9��':��';��'<��'=��'���'���'���'���'���'���'���'��'��'��'$��'&��'6��'8��':��';��'=��'?��'C��'���'���'���'���'��'��'��'X��'��'��'!��'#��'%��''��')��'+��'-��'/��'1��'3��'o��'q��'s��'���(-{)��)��)"))$��)���)���)���)���)���)���)��)��)��)C��)��)��)X��)��)��)!��)#��)%��)'��))��)+��)-��)/��)1��)3��.&��.*��.2��.4��.���.���.���.���.���.���.���.��.��.��.��.��.��.��.��.��.��.��.��.G��._��.I��.K��.M��.O��.Q��.S��.U��.W��.Y��.[��.]��._��/�\/
�\/&��/*��/2��/4��/7��/8��/9��/:��/<��/���/���/���/���/���/���/���/���/���/���/���/���/��/��/��/��/��/��/��/��/��/��/��/��/$��/&��/*��/,��/.��/0��/2��/4��/6��/8��/:��/G��/���/���/���/��/�\/�\/_��/a��/I��/K��/M��/O��/Q��/S��/U��/W��/Y��/[��/]��/_��/a��/c��/e��/g��/i��/k��/m��/o��/q��/s��/���2��2��2$��27��29��2:��2;��2<��2=��2���2���2���2���2���2���2���2��2��2��2$��2&��26��28��2:��2;��2=��2?��2C��2���2���2���2���2��2��2��2X��2��2��2!��2#��2%��2'��2)��2+��2-��2/��21��23��2o��2q��2s��2���3��3��3$��3;��3=��3���3���3���3���3���3���3��3��3��3;��3=��3?��3C��3��3��3X��3��3��3!��3#��3%��3'��3)��3+��3-��3/��31��33��4��4��4$��47��49��4:��4;��4<��4=��4���4���4���4���4���4���4���4��4��4��4$��4&��46��48��4:��4;��4=��4?��4C��4���4���4���4���4��4��4��4X��4��4��4!��4#��4%��4'��4)��4+��4-��4/��41��43��4o��4q��4s��4���7��7��7��7")7$�q7&��7*��72��74��77)7D�\7F�q7G�q7H�q7J�q7P��7Q��7R�q7S��7T�q7U��7V��7X��7Y��7Z��7[��7\��7]��7��q7��q7��q7��q7��q7��q7���7���7���7���7���7���7���7��q7��\7��\7��\7��\7��\7��\7��q7��q7��q7��q7��q7��q7��q7��q7��q7��q7��q7���7���7���7���7���7�q7�\7�q7�\7�q7�\7��7�q7��7�q7��7�q7��7�q7�q7�q7�q7�q7�q7�q7�q7��7�q7��7�q7��7�q7��7�q7���7��7��7
��7��7�q7��7�q7��7�q7��7�q7��7��7��7!��7$)7&)7+��7-��7/��71��73��75��77��7<��7>��7@��7C�q7D�\7F�\7G��7H�q7J��7���7���7��7��7��7��7��7W��7X�q7Y�\7_��7`�q7b��7�q7�\7�q7 �\7!�q7"�\7#�q7%�q7&�\7'�q7(�\7)�q7*�\7+�q7,�\7-�q7.�\7/�q70�\71�q72�\73�q74�\76�q78�q7:�q7<�q7@�q7B�q7D�q7I��7J�q7K��7L�q7M��7N�q7O��7Q��7R�q7S��7T�q7U��7V�q7W��7X�q7Y��7Z�q7[��7\�q7]��7^�q7_��7`�q7b��7d��7f��7h��7j��7l��7n��7p��7�)8��8��8$��8���8���8���8���8���8���8��8��8��8C��8��8��8X��8��8��8!��8#��8%��8'��8)��8+��8-��8/��81��83��9��9��9")9$��9&��9*��92��94��9D��9F��9G��9H��9J��9P��9Q��9R��9S��9T��9U��9V��9X��9���9���9���9���9���9���9���9���9���9���9���9���9���9���9���9���9���9���9���9���9���9���9���9���9���9���9���9���9���9���9���9���9���9���9���9��9��9��9��9��9��9��9��9��9��9��9��9��9��9��9��9��9��9��9��9��9��9��9��9��9��9��9��9��9���9��9��9
��9��9��9��9��9��9��9��9��9��9��9��9!��9+��9-��9/��91��93��95��9C��9D��9F��9G��9H��9J��9��9��9W��9X��9Y��9_��9`��9b��9��9��9��9 ��9!��9"��9#��9%��9&��9'��9(��9)��9*��9+��9,��9-��9.��9/��90��91��92��93��94��96��98��9:��9<��9@��9B��9D��9I��9J��9K��9L��9M��9N��9O��9Q��9R��9S��9T��9U��9V��9W��9X��9Y��9Z��9[��9\��9]��9^��9_��9`��9b��9d��9f��9h��9j��9l��9n��:��:��:"):$��:&��:*��:2��:4��:D��:F��:G��:H��:J��:P��:Q��:R��:S��:T��:U��:V��:X��:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:��:��:��:��:��:��:��:��:��:��:��:��:��:��:��:��:��:��:��:��:��:��:��:��:��:��:��:��:��:���:��:��:
��:��:��:��:��:��:��:��:��:��:��:��:!��:+��:-��:/��:1��:3��:5��:C��:D��:F��:G��:H��:J��:��:��:W��:X��:Y��:_��:`��:b��:��:��:��: ��:!��:"��:#��:%��:&��:'��:(��:)��:*��:+��:,��:-��:.��:/��:0��:1��:2��:3��:4��:6��:8��::��:<��:@��:B��:D��:I��:J��:K��:L��:M��:N��:O��:Q��:R��:S��:T��:U��:V��:W��:X��:Y��:Z��:[��:\��:]��:^��:_��:`��:b��:d��:f��:h��:j��:l��:n��;&��;*��;2��;4��;���;���;���;���;���;���;���;��;��;��;��;��;��;��;��;��;��;��;��;G��;_��;I��;K��;M��;O��;Q��;S��;U��;W��;Y��;[��;]��;_��<��<��<")<$��<&��<*��<2��<4��<D��<F��<G��<H��<J��<P��<Q��<R��<S��<T��<U��<V��<X��<]��<���<���<���<���<���<���<���<���<���<���<���<���<���<���<���<���<���<���<���<���<���<���<���<���<���<���<���<���<���<���<���<���<���<���<���<��<��<��<��<��<��<��<��<��<��<��<��<��<��<��<��<��<��<��<��<��<��<��<��<��<��<��<��<��<���<��<��<
��<��<��<��<��<��<��<��<��<��<��<��<!��<+��<-��</��<1��<3��<5��<<��<>��<@��<C��<D��<F��<G��<H��<J��<��<��<W��<X��<Y��<_��<`��<b��<��<��<��< ��<!��<"��<#��<%��<&��<'��<(��<)��<*��<+��<,��<-��<.��</��<0��<1��<2��<3��<4��<6��<8��<:��<<��<@��<B��<D��<I��<J��<K��<L��<M��<N��<O��<Q��<R��<S��<T��<U��<V��<W��<X��<Y��<Z��<[��<\��<]��<^��<_��<`��<b��<d��<f��<h��<j��<l��<n��=&��=*��=2��=4��=���=���=���=���=���=���=���=��=��=��=��=��=��=��=��=��=��=��=��=G��=_��=I��=K��=M��=O��=Q��=S��=U��=W��=Y��=[��=]��=_��>-�D��D
��D��D��E��E
��EY��EZ��E[��E\��E]��E���E7��E<��E>��E@��E���E���E��E��Ep��F)F
)F)F)H��H
��HY��HZ��H[��H\��H]��H���H7��H<��H>��H@��H���H���H��H��Hp��I{I
{I{I{K��K
��K��K��NF��NG��NH��NR��NT��N���N���N���N���N���N���N���N���N���N���N���N���N��N��N��N��N��N��N��N��N��N��N��N��N��N��N��NH��N`��N6��N8��N:��N<��N@��NB��ND��NJ��NL��NN��NR��NT��NV��NX��NZ��N\��N^��N`��P��P
��P��P��Q��Q
��Q��Q��R��R
��RY��RZ��R[��R\��R]��R���R7��R<��R>��R@��R���R���R��R��Rp��S��S
��SY��SZ��S[��S\��S]��S���S7��S<��S>��S@��S���S���S��S��Sp��URU
RUD��UF��UG��UH��UJ��UR��UT��U���U���U���U���U���U���U���U���U���U���U���U���U���U���U���U���U���U���U��U��U��U��U��U��U��U��U��U��U��U��U��U��U��U��U��U��U��U��U��U��UD��UF��UH��URURUY��U`��U��U ��U"��U&��U(��U*��U,��U.��U0��U2��U4��U6��U8��U:��U<��U@��UB��UD��UJ��UL��UN��UR��UT��UV��UX��UZ��U\��U^��U`��W)W
)W)W)YRY
RY��Y��Y")YRY��YRY��ZRZ
RZ��Z��Z")ZRZ��ZRZ��[F��[G��[H��[R��[T��[���[���[���[���[���[���[���[���[���[���[���[���[��[��[��[��[��[��[��[��[��[��[��[��[��[��[��[H��[`��[6��[8��[:��[<��[@��[B��[D��[J��[L��[N��[R��[T��[V��[X��[Z��[\��[^��[`��\R\
R\��\��\")\R\��\R\��^-���q�
�q�&���*���-
�2���4���7�q�9���:���<�����������������������������������������������������������������������$�q�&�q�6���8���:���G�������������������q��q�_���I���K���M���O���Q���S���U���W���Y���[���]���_���o���q���s�����q��q�
�q�&���*���-
�2���4���7�q�9���:���<�����������������������������������������������������������������������$�q�&�q�6���8���:���G�������������������q��q�_���I���K���M���O���Q���S���U���W���Y���[���]���_���o���q���s�����q��q�
�q�&���*���-
�2���4���7�q�9���:���<�����������������������������������������������������������������������$�q�&�q�6���8���:���G�������������������q��q�_���I���K���M���O���Q���S���U���W���Y���[���]���_���o���q���s�����q��q�
�q�&���*���-
�2���4���7�q�9���:���<�����������������������������������������������������������������������$�q�&�q�6���8���:���G�������������������q��q�_���I���K���M���O���Q���S���U���W���Y���[���]���_���o���q���s�����q��q�
�q�&���*���-
�2���4���7�q�9���:���<�����������������������������������������������������������������������$�q�&�q�6���8���:���G�������������������q��q�_���I���K���M���O���Q���S���U���W���Y���[���]���_���o���q���s�����q��q�
�q�&���*���-
�2���4���7�q�9���:���<�����������������������������������������������������������������������$�q�&�q�6���8���:���G�������������������q��q�_���I���K���M���O���Q���S���U���W���Y���[���]���_���o���q���s�����q�-{�&���*���2���4�������������������������������������������������������������������G���_���I���K���M���O���Q���S���U���W���Y���[���]���_���-{�-{�-{�-{�������$���7���9���:���;���<���=����������������������������������������$���&���6���8���:���;���=���?���C����������������������������X���������!���#���%���'���)���+���-���/���1���3���o���q���s�������������$���7���9���:���;���<���=����������������������������������������$���&���6���8���:���;���=���?���C����������������������������X���������!���#���%���'���)���+���-���/���1���3���o���q���s�������������$���7���9���:���;���<���=����������������������������������������$���&���6���8���:���;���=���?���C����������������������������X���������!���#���%���'���)���+���-���/���1���3���o���q���s�������������$���7���9���:���;���<���=����������������������������������������$���&���6���8���:���;���=���?���C����������������������������X���������!���#���%���'���)���+���-���/���1���3���o���q���s�������������$���7���9���:���;���<���=����������������������������������������$���&���6���8���:���;���=���?���C����������������������������X���������!���#���%���'���)���+���-���/���1���3���o���q���s�������������$���7���9���:���;���<���=����������������������������������������$���&���6���8���:���;���=���?���C����������������������������X���������!���#���%���'���)���+���-���/���1���3���o���q���s�������������$���7���9���:���;���<���=����������������������������������������$���&���6���8���:���;���=���?���C����������������������������X���������!���#���%���'���)���+���-���/���1���3���o���q���s�������������$������������������������������������C���������X���������!���#���%���'���)���+���-���/���1���3���������$������������������������������������C���������X���������!���#���%���'���)���+���-���/���1���3���������$������������������������������������C���������X���������!���#���%���'���)���+���-���/���1���3���������$������������������������������������C���������X���������!���#���%���'���)���+���-���/���1���3���������")�$���&���*���2���4���D���F���G���H���J���P���Q���R���S���T���U���V���X���]������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
������������������������������������!���+���-���/���1���3���5���<���>���@���C���D���F���G���H���J���������W���X���Y���_���`���b������������ ���!���"���#���%���&���'���(���)���*���+���,���-���.���/���0���1���2���3���4���6���8���:���<���@���B���D���I���J���K���L���M���N���O���Q���R���S���T���U���V���W���X���Y���Z���[���\���]���^���_���`���b���d���f���h���j���l���n���������$���;���=������������������������������������;���=���?���C���������X���������!���#���%���'���)���+���-���/���1���3������
������������
������������
������������
������������
������������
������������
���Y���Z���[���\���]�������7���<���>���@�����������������p������
���Y���Z���[���\���]�������7���<���>���@�����������������p������
���Y���Z���[���\���]�������7���<���>���@�����������������p������
���Y���Z���[���\���]�������7���<���>���@�����������������p������
���Y���Z���[���\���]�������7���<���>���@�����������������p������
���Y���Z���[���\���]�������7���<���>���@�����������������p������
���Y���Z���[���\���]�������7���<���>���@�����������������p������
���Y���Z���[���\���]�������7���<���>���@�����������������p������
������������
���Y���Z���[���\���]�������7���<���>���@�����������������p���R�
R�������")�R����R�������
���Y���Z���[���\���]�������7���<���>���@�����������������p���R�
R�������")�R����R�����q�
�q�&���*���-
�2���4���7�q�9���:���<�����������������������������������������������������������������������$�q�&�q�6���8���:���G�������������������q��q�_���I���K���M���O���Q���S���U���W���Y���[���]���_���o���q���s�����q����
����������q�
�q�&���*���-
�2���4���7�q�9���:���<�����������������������������������������������������������������������$�q�&�q�6���8���:���G�������������������q��q�_���I���K���M���O���Q���S���U���W���Y���[���]���_���o���q���s�����q����
����������q�
�q�&���*���-
�2���4���7�q�9���:���<�����������������������������������������������������������������������$�q�&�q�6���8���:���G�������������������q��q�_���I���K���M���O���Q���S���U���W���Y���[���]���_���o���q���s�����q����
���������&���*���2���4�������������������������������������������������������������������G���_���I���K���M���O���Q���S���U���W���Y���[���]���_���&���*���2���4�������������������������������������������������������������������G���_���I���K���M���O���Q���S���U���W���Y���[���]���_���&���*���2���4�������������������������������������������������������������������G���_���I���K���M���O���Q���S���U���W���Y���[���]���_���&���*���2���4�������������������������������������������������������������������G���_���I���K���M���O���Q���S���U���W���Y���[���]���_���������$���7���9���:���;���<���=����������������������������������������$���&���6���8���:���;���=���?���C����������������������������X���������!���#���%���'���)���+���-���/���1���3���o���q���s�������R�
R���"��@��E=�K=�N=�O=�`���=��{�R�R�������$���7���9���:���;���<���=����������������������������������������$���&���6���8���:���;���=���?���C����������������������������X���������!���#���%���'���)���+���-���/���1���3���o���q���s�������-{����
���Y���Z���[���\���]�������7���<���>���@�����������������p���-{����
���Y���Z���[���\���]�������7���<���>���@�����������������p���-{����
���Y���Z���[���\���]�������7���<���>���@�����������������p���-{����
���Y���Z���[���\���]�������7���<���>���@�����������������p���-{����
���Y���Z���[���\���]�������7���<���>���@�����������������p������
���������&���*���2���4�������������������������������������������������������������������G���_���I���K���M���O���Q���S���U���W���Y���[���]���_���F���G���H���R���T������������������������������������������������������������������������������������������������H���`���6���8���:���<���@���B���D���J���L���N���R���T���V���X���Z���\���^���`���F���G���H���R���T������������������������������������������������������������������������������������������������H���`���6���8���:���<���@���B���D���J���L���N���R���T���V���X���Z���\���^���`����\�
�\�&���*���2���4���7���8���9���:���<���������������������������������������������������������������������������������������$���&���*���,���.���0���2���4���6���8���:���G�������������������\��\�_���a���I���K���M���O���Q���S���U���W���Y���[���]���_���a���c���e���g���i���k���m���o���q���s��������\�
�\�&���*���2���4���7���8���9���:���<���������������������������������������������������������������������������������������$���&���*���,���.���0���2���4���6���8���:���G�������������������\��\�_���a���I���K���M���O���Q���S���U���W���Y���[���]���_���a���c���e���g���i���k���m���o���q���s��������\�
�\�&���*���2���4���7���8���9���:���<���������������������������������������������������������������������������������������$���&���*���,���.���0���2���4���6���8���:���G�������������������\��\�_���a���I���K���M���O���Q���S���U���W���Y���[���]���_���a���c���e���g���i���k���m���o���q���s������R
R�"�@�E=K=N=O=`��=��RR�\
�\&��*��2��4��7��8��9��:��<��������������������������������������������������������������$��&��*��,��.��0��2��4��6��8��:��G��������������\�\_��a��I��K��M��O��Q��S��U��W��Y��[��]��_��a��c��e��g��i��k��m��o��q��s������\
�\&��*��2��4��7��8��9��:��<��������������������������������������������������������������$��&��*��,��.��0��2��4��6��8��:��G��������������\�\_��a��I��K��M��O��Q��S��U��W��Y��[��]��_��a��c��e��g��i��k��m��o��q��s�������
����������$��7��9��:��;��<��=�����������������������������$��&��6��8��:��;��=��?��C��������������������X������!��#��%��'��)��+��-��/��1��3��o��q��s���������$��7��9��:��;��<��=�����������������������������$��&��6��8��:��;��=��?��C��������������������X������!��#��%��'��)��+��-��/��1��3��o��q��s���������$��7��9��:��;��<��=�����������������������������$��&��6��8��:��;��=��?��C��������������������X������!��#��%��'��)��+��-��/��1��3��o��q��s�����-{R
RD��F��G��H��J��R��T����������������������������������������������������������������������������������������������������D��F��H��RRY��`���� ��"��&��(��*��,��.��0��2��4��6��8��:��<��@��B��D��J��L��N��R��T��V��X��Z��\��^��`��R
RD��F��G��H��J��R��T����������������������������������������������������������������������������������������������������D��F��H��RRY��`���� ��"��&��(��*��,��.��0��2��4��6��8��:��<��@��B��D��J��L��N��R��T��V��X��Z��\��^��`��R
RD��F��G��H��J��R��T����������������������������������������������������������������������������������������������������D��F��H��RRY��`���� ��"��&��(��*��,��.��0��2��4��6��8��:��<��@��B��D��J��L��N��R��T��V��X��Z��\��^��`��$��$��$��$")$$�q$&��$*��$2��$4��$7)$D�\$F�q$G�q$H�q$J�q$P��$Q��$R�q$S��$T�q$U��$V��$X��$Y��$Z��$[��$\��$]��$��q$��q$��q$��q$��q$��q$���$���$���$���$���$���$���$��q$��\$��\$��\$��\$��\$��\$��q$��q$��q$��q$��q$��q$��q$��q$��q$��q$��q$���$���$���$���$���$�q$�\$�q$�\$�q$�\$��$�q$��$�q$��$�q$��$�q$�q$�q$�q$�q$�q$�q$�q$��$�q$��$�q$��$�q$��$�q$���$��$��$
��$��$�q$��$�q$��$�q$��$�q$��$��$��$!��$$)$&)$+��$-��$/��$1��$3��$5��$7��$<��$>��$@��$C�q$D�\$F�\$G��$H�q$J��$���$���$��$��$��$��$��$W��$X�q$Y�\$_��$`�q$b��$�q$�\$�q$ �\$!�q$"�\$#�q$%�q$&�\$'�q$(�\$)�q$*�\$+�q$,�\$-�q$.�\$/�q$0�\$1�q$2�\$3�q$4�\$6�q$8�q$:�q$<�q$@�q$B�q$D�q$I��$J�q$K��$L�q$M��$N�q$O��$Q��$R�q$S��$T�q$U��$V�q$W��$X�q$Y��$Z�q$[��$\�q$]��$^�q$_��$`�q$b��$d��$f��$h��$j��$l��$n��$p��$�)%)%
)%)%)&��&��&��&")&$�q&&��&*��&2��&4��&7)&D�\&F�q&G�q&H�q&J�q&P��&Q��&R�q&S��&T�q&U��&V��&X��&Y��&Z��&[��&\��&]��&��q&��q&��q&��q&��q&��q&���&���&���&���&���&���&���&��q&��\&��\&��\&��\&��\&��\&��q&��q&��q&��q&��q&��q&��q&��q&��q&��q&��q&���&���&���&���&���&�q&�\&�q&�\&�q&�\&��&�q&��&�q&��&�q&��&�q&�q&�q&�q&�q&�q&�q&�q&��&�q&��&�q&��&�q&��&�q&���&��&��&
��&��&�q&��&�q&��&�q&��&�q&��&��&��&!��&$)&&)&+��&-��&/��&1��&3��&5��&7��&<��&>��&@��&C�q&D�\&F�\&G��&H�q&J��&���&���&��&��&��&��&��&W��&X�q&Y�\&_��&`�q&b��&�q&�\&�q& �\&!�q&"�\&#�q&%�q&&�\&'�q&(�\&)�q&*�\&+�q&,�\&-�q&.�\&/�q&0�\&1�q&2�\&3�q&4�\&6�q&8�q&:�q&<�q&@�q&B�q&D�q&I��&J�q&K��&L�q&M��&N�q&O��&Q��&R�q&S��&T�q&U��&V�q&W��&X�q&Y��&Z�q&[��&\�q&]��&^�q&_��&`�q&b��&d��&f��&h��&j��&l��&n��&p��&�)')'
)')')(��(��(��(")($�q(&��(*��(2��(4��(7)(D�\(F�q(G�q(H�q(J�q(P��(Q��(R�q(S��(T�q(U��(V��(X��(Y��(Z��([��(\��(]��(��q(��q(��q(��q(��q(��q(���(���(���(���(���(���(���(��q(��\(��\(��\(��\(��\(��\(��q(��q(��q(��q(��q(��q(��q(��q(��q(��q(��q(���(���(���(���(���(�q(�\(�q(�\(�q(�\(��(�q(��(�q(��(�q(��(�q(�q(�q(�q(�q(�q(�q(�q(��(�q(��(�q(��(�q(��(�q(���(��(��(
��(��(�q(��(�q(��(�q(��(�q(��(��(��(!��($)(&)(+��(-��(/��(1��(3��(5��(7��(<��(>��(@��(C�q(D�\(F�\(G��(H�q(J��(���(���(��(��(��(��(��(W��(X�q(Y�\(_��(`�q(b��(�q(�\(�q( �\(!�q("�\(#�q(%�q(&�\('�q((�\()�q(*�\(+�q(,�\(-�q(.�\(/�q(0�\(1�q(2�\(3�q(4�\(6�q(8�q(:�q(<�q(@�q(B�q(D�q(I��(J�q(K��(L�q(M��(N�q(O��(Q��(R�q(S��(T�q(U��(V�q(W��(X�q(Y��(Z�q([��(\�q(]��(^�q(_��(`�q(b��(d��(f��(h��(j��(l��(n��(p��(�)*��*��*$��*���*���*���*���*���*���*��*��*��*C��*��*��*X��*��*��*!��*#��*%��*'��*)��*+��*-��*/��*1��*3��,��,��,$��,���,���,���,���,���,���,��,��,��,C��,��,��,X��,��,��,!��,#��,%��,'��,)��,+��,-��,/��,1��,3��.��.��.$��.���.���.���.���.���.���.��.��.��.C��.��.��.X��.��.��.!��.#��.%��.'��.)��.+��.-��./��.1��.3��0��0��0$��0���0���0���0���0���0���0��0��0��0C��0��0��0X��0��0��0!��0#��0%��0'��0)��0+��0-��0/��01��03��2��2��2$��2���2���2���2���2���2���2��2��2��2C��2��2��2X��2��2��2!��2#��2%��2'��2)��2+��2-��2/��21��23��4��4��4$��4���4���4���4���4���4���4��4��4��4C��4��4��4X��4��4��4!��4#��4%��4'��4)��4+��4-��4/��41��43��6��6��6")6$��6&��6*��62��64��6D��6F��6G��6H��6J��6P��6Q��6R��6S��6T��6U��6V��6X��6���6���6���6���6���6���6���6���6���6���6���6���6���6���6���6���6���6���6���6���6���6���6���6���6���6���6���6���6���6���6���6���6���6���6���6��6��6��6��6��6��6��6��6��6��6��6��6��6��6��6��6��6��6��6��6��6��6��6��6��6��6��6��6��6���6��6��6
��6��6��6��6��6��6��6��6��6��6��6��6!��6+��6-��6/��61��63��65��6C��6D��6F��6G��6H��6J��6��6��6W��6X��6Y��6_��6`��6b��6��6��6��6 ��6!��6"��6#��6%��6&��6'��6(��6)��6*��6+��6,��6-��6.��6/��60��61��62��63��64��66��68��6:��6<��6@��6B��6D��6I��6J��6K��6L��6M��6N��6O��6Q��6R��6S��6T��6U��6V��6W��6X��6Y��6Z��6[��6\��6]��6^��6_��6`��6b��6d��6f��6h��6j��6l��6n��7R7
R7��7��7")7R7��7R7��8��8��8")8$��8&��8*��82��84��8D��8F��8G��8H��8J��8P��8Q��8R��8S��8T��8U��8V��8X��8]��8���8���8���8���8���8���8���8���8���8���8���8���8���8���8���8���8���8���8���8���8���8���8���8���8���8���8���8���8���8���8���8���8���8���8���8��8��8��8��8��8��8��8��8��8��8��8��8��8��8��8��8��8��8��8��8��8��8��8��8��8��8��8��8��8���8��8��8
��8��8��8��8��8��8��8��8��8��8��8��8!��8+��8-��8/��81��83��85��8<��8>��8@��8C��8D��8F��8G��8H��8J��8��8��8W��8X��8Y��8_��8`��8b��8��8��8��8 ��8!��8"��8#��8%��8&��8'��8(��8)��8*��8+��8,��8-��8.��8/��80��81��82��83��84��86��88��8:��8<��8@��8B��8D��8I��8J��8K��8L��8M��8N��8O��8Q��8R��8S��8T��8U��8V��8W��8X��8Y��8Z��8[��8\��8]��8^��8_��8`��8b��8d��8f��8h��8j��8l��8n��9R9
R9��9��9")9R9��9R9��:��:��:"):$��:&��:*��:2��:4��:D��:F��:G��:H��:J��:P��:Q��:R��:S��:T��:U��:V��:X��:]��:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:���:��:��:��:��:��:��:��:��:��:��:��:��:��:��:��:��:��:��:��:��:��:��:��:��:��:��:��:��:��:���:��:��:
��:��:��:��:��:��:��:��:��:��:��:��:!��:+��:-��:/��:1��:3��:5��:<��:>��:@��:C��:D��:F��:G��:H��:J��:��:��:W��:X��:Y��:_��:`��:b��:��:��:��: ��:!��:"��:#��:%��:&��:'��:(��:)��:*��:+��:,��:-��:.��:/��:0��:1��:2��:3��:4��:6��:8��::��:<��:@��:B��:D��:I��:J��:K��:L��:M��:N��:O��:Q��:R��:S��:T��:U��:V��:W��:X��:Y��:Z��:[��:\��:]��:^��:_��:`��:b��:d��:f��:h��:j��:l��:n��;&��;*��;2��;4��;���;���;���;���;���;���;���;��;��;��;��;��;��;��;��;��;��;��;��;G��;_��;I��;K��;M��;O��;Q��;S��;U��;W��;Y��;[��;]��;_��=&��=*��=2��=4��=���=���=���=���=���=���=���=��=��=��=��=��=��=��=��=��=��=��=��=G��=_��=I��=K��=M��=O��=Q��=S��=U��=W��=Y��=[��=]��=_��?&��?*��?2��?4��?���?���?���?���?���?���?���?��?��?��?��?��?��?��?��?��?��?��?��?G��?_��?I��?K��?M��?O��?Q��?S��?U��?W��?Y��?[��?]��?_��C�qC
�qC&��C*��C-
C2��C4��C7�qC9��C:��C<��C���C���C���C���C���C���C���C���C��C��C��C��C��C��C��C��C��C��C��C��C$�qC&�qC6��C8��C:��CG��C���C���C���C��C�qC�qC_��CI��CK��CM��CO��CQ��CS��CU��CW��CY��C[��C]��C_��Co��Cq��Cs��C��qD��D
��D��D��E-{G��G��G$��G7��G9��G:��G;��G<��G=��G���G���G���G���G���G���G���G��G��G��G$��G&��G6��G8��G:��G;��G=��G?��GC��G���G���G���G���G��G��G��GX��G��G��G!��G#��G%��G'��G)��G+��G-��G/��G1��G3��Go��Gq��Gs��G���V�qV
�qVf��Vm��Vq�qVr��Vs��Vu��Vx��V�qV�qVT��[��[��[V��[_��[b��[d��[i��[p��[q��[r��[t��[u��[x��[���[��[��[T��\��\��\V��\_��\b��\f��\i��\m��\s��\v��\y��\z��\{��\|��\}��\~��\���\���\���\���\���\���\���\���\���\���\���\���\���\���\���\���\���\���\��\��\!��]q��]r��]x��]T��^��^
��^��^��_�q_
�q_f��_m��_q�q_r��_s��_u��_x��_�q_�q_T��`��`��`V��`_��`b��`i��`t��`��`��a��a��a��aV�\a_�\ab�\af��ai�\am��as��av��ay�qaz��a{��a|��a}��a~�qa���a���a���a���a���a���a���a���a��qa���a��qa��qa���a��qa���a���a���a���a��qa���a���a��a��a��a��a��a!��aS��b�qb
�qbf��bm��bq�qbr��bs��bu��bx��b�qb�qbT��df��dm��ds��f��f��fV��f_��fb��fd��fi��fp��fq��fr��ft��fu��fx��f���f��f��fT��hf��hm��hs��h���h���i�qi
�qif��im��iq�qir��is��iu��ix��i�qi�qiT��m��m��mV��m_��mb��md��mi��mp��mq��mr��mt��mu��mx��m���m��m��mT��o��o��oV��o_��ob��od��oi��ot��o���o��o��q��q��q��qV�\q_�\qb�\qf��qi�\qm��qs��qv��qy�qqz��q{��q|��q}��q~�qq���q���q���q���q���q���q���q���q��qq���q��qq��qq���q��qq���q���q���q���q��qq���q���q��q��q��q��q��q!��qS��r��r��rV��r_��rb��rf��ri��rm��rs��rv��ry��rz��r{��r|��r}��r~��r���r���r���r���r���r���r���r���r���r���r���r���r���r���r���r���r���r���r��r��r!��s��s��sV��s_��sb��sd��si��sp��sq��sr��st��sx��s���s��s��sT��tf��tm��ts��t���t���u��u��uV��u_��ub��uf��ui��um��u��u��vq��vr��vx��vT��x��x��xV��x_��xb��xf��xi��xm��xs��xv��xy��xz��x{��x|��x}��x~��x���x���x���x���x���x���x���x���x���x���x���x���x���x���x���x���x���x���x��x��x!��y�){��{
��{��{��|��|
��|���|���|��|��~�)��������������������y���~�����������������������������������������������
������������������������
�����������������y���~��������������������������
���y���~����������������������������������������������������������������
��������������������
��������������������������������
��������������������������y���~���������������������������������������������
������������
��������������������
����������������������������������������������������n���|�����������������������������������������������������������������������������������������������������������������������
�������������������������������������q������������������)���������������q��������������������������q��������q��������q���������������������������q����q��������������������������������������j�q�k���l���m���q���r�q�s���u���w���y���}���~����q���������������q�������q�������q���������������q���������������������������������������������������q�������q��)�����������������������������������q�����q��������������������������q��q��q��q������������������q�����q�����q���������������������������������	�q�
�q��q��q��������������������q�����q��������������������������l���~������������������������������������������������������������	������������������������
����������������������������������������������������n���|�����������������������������������������������������������������������������������������������������������������������
���������������������
����������������������������������������������������n���|�����������������������������������������������������������������������������������������������������������������������
���������������������
����������������������������������������������������n���|�����������������������������������������������������������������������������������������������������������������������
�����������������������������������������l���|���~���������������������������������������������������������������������������	����������������������������������q�������������������������������������������������f����������f�������j���l���r�q�s���~�������������������������������������������f��f����������������������������������������������q��q��q��������������������������	�q�
����q��������������������������q�
�q�����������q����������������������q��q�n���|���������������������������������q�����������������������������������������������������������������
�q�����q�������������
�����������������r���|�������������������������������������������������������������
�������������������������q������������������)���������������q��������������������������q��������q��������q���������������������������q����q��������������������������������������j�q�k���l���m���q���r�q�s���u���w���y���}���~����q���������������q�������q�������q���������������q���������������������������������������������������q�������q��)�����������������������������������q�����q��������������������������q��q��q��q������������������q�����q�����q���������������������������������	�q�
�q��q��q��������������������q�����q�����������)������������������������������l���|���~���������������������������������������������������������������������������	����������������������������������������������
�����������������������������l���|���~���������������������������������������������������������������������������	�������������������������������������������������������������������������������������������������r���s���z���|��������������������������������������������������������������������������������������������������������
�����������������������������������������������������������������r���s���v��������������������������������������	�������������������������������������l���~������������������������������������������������������������	����������������������������������q������������������)���������������q��������������������������q��������q��������q���������������������������q����q��������������������������������������j�q�k���l���m���q���r�q�s���u���w���y���}���~����q���������������q�������q�������q���������������q���������������������������������������������������q�������q��)�����������������������������������q�����q��������������������������q��q��q��q������������������q�����q�����q���������������������������������	�q�
�q��q��q��������������������q�����q�����������������������q�������������������������������������������������f����������f�������j���l���r�q�s���~�������������������������������������������f��f����������������������������������������������q��q��q��������������������������	�q�
����q����������������������������������������������������������������������������������������������������r���s���z�����������������������������������������������������������������������������������������
��������������������������������������������l���|���~���������������������������������������������������������������������������	����������������������)�����������)����������
����������������������������������������������������n���|�����������������������������������������������������������������������������������������������������������������������
���������������������
����������������������������������������������������n���|�����������������������������������������������������������������������������������������������������������������������
�������������������������������������������������������������������������������������������������r���s���z���|��������������������������������������������������������������������������������������������������������
����������������������������������������������������������������������������������������������������r���s���z���|��������������������������������������������������������������������������������������������������������
������������������������
����������)����������������������������������������������������j���s���������������������������������������������
���������������������
������������
������������������������������j��������������������������������������������������������������
�������������)����������������������j��������������������������������������������������������������
���������������
�����������������������������������������������������������������������������������������������������
�����������������������������������������������������������������������������������������������������������������������������������������������������j���s���������������������������������������������
���������������������������������������s���������������
�����������������������������������������������������������������������������������������������������������������������j��������������������������������������������������������������
���������������
������������
������������
�����������������������������m������������������������������������������������������������������������������������������������������
�����������������������������m������������������������������������������������������������������������������������������������������
�����������������������������������������������������������������������������������������������������
�����������������������������������������������������������������������������������������������������
������������
������������������������������������������������������������������j���s���������������������������������������������
���������������������
�����������������������������m������������������������������������������������������������������������������������������������������
�����������������������������m������������������������������������������������������������������������������������������������������
������������������������������������j��������������������������������������������������������������
���������������������������������s�������������������������������q������������������)���������������q��������������������������q��������q��������q���������������������������q����q��������������������������������������j�q�k���l���m���q���r�q�s���u���w���y���}���~����q���������������q�������q�������q���������������q���������������������������������������������������q�������q��)�����������������������������������q�����q��������������������������q��q��q��q������������������q�����q�����q���������������������������������	�q�
�q��q��q��������������������q�����q����������������������������������������������������������j���s���������������������������������������������
������������������������")�$���&���*���2���4���D���F���G���H���J���P���Q���R���S���T���U���V���X������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
������������������������������������!���+���-���/���1���3���5���C���D���F���G���H���J���������W���X���Y���_���`���b������������ ���!���"���#���%���&���'���(���)���*���+���,���-���.���/���0���1���2���3���4���6���8���:���<���@���B���D���I���J���K���L���M���N���O���Q���R���S���T���U���V���W���X���Y���Z���[���\���]���^���_���`���b���d���f���h���j���l���n���R�
R�������")�R����R����������")�$���&���*���2���4���D���F���G���H���J���P���Q���R���S���T���U���V���X������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
������������������������������������!���+���-���/���1���3���5���C���D���F���G���H���J���������W���X���Y���_���`���b������������ ���!���"���#���%���&���'���(���)���*���+���,���-���.���/���0���1���2���3���4���6���8���:���<���@���B���D���I���J���K���L���M���N���O���Q���R���S���T���U���V���W���X���Y���Z���[���\���]���^���_���`���b���d���f���h���j���l���n���R�
R�������")�R����R����������")�$���&���*���2���4���D���F���G���H���J���P���Q���R���S���T���U���V���X������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
������������������������������������!���+���-���/���1���3���5���C���D���F���G���H���J���������W���X���Y���_���`���b������������ ���!���"���#���%���&���'���(���)���*���+���,���-���.���/���0���1���2���3���4���6���8���:���<���@���B���D���I���J���K���L���M���N���O���Q���R���S���T���U���V���W���X���Y���Z���[���\���]���^���_���`���b���d���f���h���j���l���n���R�
R�������")�R����R�������")$��&��*��2��4��D��F��G��H��J��P��Q��R��S��T��U��V��X��]����������������������������������������������������������������������������������������������������������������������������������������������������������������������������
������������������������!��+��-��/��1��3��5��<��>��@��C��D��F��G��H��J������W��X��Y��_��`��b�������� ��!��"��#��%��&��'��(��)��*��+��,��-��.��/��0��1��2��3��4��6��8��:��<��@��B��D��I��J��K��L��M��N��O��Q��R��S��T��U��V��W��X��Y��Z��[��\��]��^��_��`��b��d��f��h��j��l��n��R
R����")R��R��7��$��&��q������������������������������������������7��$��&��q������������������������������������������7��$��&��q������������������������������������������$�q7)9):)<D��F��G��H��J��P��Q��R��S��T��U��V��X����q��q��q��q��q��q��������������������������������������������������������������������q���q���q���������������������������������������
����������������!��$)&)+��-��/��1��3��5��6)8:C�qD��F��H��J��V�q_�qb�qi�qy��z��{��~�������������������������������������������q���������q���q�����������q�)�)�)W��X�qY��`��b��j��r�qs�q}�������������������������q�q���q���q����������	�q
���q�����q�������q�q���q ��!�q"��#�q%�q&��'�q(��)�q*��+�q,��-�q.��/�q0��1�q2��3�q4��6��8��:��<��@��B��D��J��L��N��R��T��V��X��Z��\��^��`��b��d��f��h��j��l��n��oqs�)$�q7)9):)<D��F��G��H��J��P��Q��R��S��T��U��V��X����q��q��q��q��q��q��������������������������������������������������������������������q���q���q���������������������������������������
����������������!��$)&)+��-��/��1��3��5��6)8:C�qD��F��H��J��V�q_�qb�qi�qy��z��{��~�������������������������������������������q���������q���q�����������q�)�)�)W��X�qY��`��b��j��r�qs�q}�������������������������q�q���q���q����������	�q
���q�����q�������q�q���q ��!�q"��#�q%�q&��'�q(��)�q*��+�q,��-�q.��/�q0��1�q2��3�q4��6��8��:��<��@��B��D��J��L��N��R��T��V��X��Z��\��^��`��b��d��f��h��j��l��n��oqs�)&��*��2��4��7�q8��9��:��<�����������������������������������������������������������$�q&�q*��,��.��0��2��4��6��8��:��G��f��m��q�qr��s��u��x�������q�����q��������q�����\�q���������������T��_��a��l��|�\~�������������������������q�����������q�����������q�����\�����\�������\�������\���
�����������q��I��K��M��O��Q��S��U��W��Y��[��]��_��a��c��e��g��i��k��m��o��q��s����q
$�q
7)
9)
:)
<
D��
F��
G��
H��
J��
P��
Q��
R��
S��
T��
U��
V��
X��
��q
��q
��q
��q
��q
��q
�
���
���
���
���
���
���
���
���
���
���
���
���
���
���
���
���
���
���
���
���
���
���
�q
��
�q
��
�q
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
��
���
��
��

��
��
��
��
��
��
��
��
!��
$)
&)
+��
-��
/��
1��
3��
5��
6)
8
:
C�q
D��
F��
H��
J��
V�q
_�q
b�q
i�q
y��
z��
{��
~��
���
���
���
���
���
���
���
���
���
���
���
���
���
��q
���
���
��
�q
��
�q
��
��
��
�
�
��
�q
�)
�)
�)

W��
X�q
Y��
`��
b��
j��
r�q
s�q
}��
��
���
���
���
���
���
���
��
�q
�q
��
�q
��
�q
��
�
��
��
�
�
�
	�q

��
�q
��
��
�q
��
��
��
�q
�q
��
�q
 ��
!�q
"��
#�q
%�q
&��
'�q
(��
)�q
*��
+�q
,��
-�q
.��
/�q
0��
1�q
2��
3�q
4��
6��
8��
:��
<��
@��
B��
D��
J��
L��
N��
R��
T��
V��
X��
Z��
\��
^��
`��
b��
d��
f��
h��
j��
l��
n��
o
q
s
�)&��*��2��4��7�q8��9��:��<�����������������������������������������������������������$�q&�q*��,��.��0��2��4��6��8��:��G��f��m��q�qr��s��u��x�������q�����q��������q�����\�q���������������T��_��a��l��|�\~�������������������������q�����������q�����������q�����\�����\�������\�������\���
�����������q��I��K��M��O��Q��S��U��W��Y��[��]��_��a��c��e��g��i��k��m��o��q��s����q!q��!r��!x��!T��S��S��S��S��T��T��TV��T_��Tb��Tf��Ti��Tm��Ts��Tv��Ty��Tz��T{��T|��T}��T~��T���T���T���T���T���T���T���T���T���T���T���T���T���T���T���T���T���T���T��T��T!��X�qX
�qX&��X*��X-
X2��X4��X7�qX9��X:��X<��X���X���X���X���X���X���X���X���X��X��X��X��X��X��X��X��X��X��X��X��X$�qX&�qX6��X8��X:��XG��X���X���X���X��X�qX�qX_��XI��XK��XM��XO��XQ��XS��XU��XW��XY��X[��X]��X_��Xo��Xq��Xs��X��qY��Y
��Y��Y��Z��Z��ZV��Z_��Zb��Zd��Zi��Zp��Zq��Zr��Zt��Zu��Zx��Z���Z��Z��ZT��`IR`WR`Yf`Zf`[f`\f`�f`%R`'R`7f`�f`�f`4R`5R`]R`^R`pf`�R`�RbIfbWfbYfbZfb[fb\fb�fb%fb'fb7fb�fb�fb4fb5fb]fb^fbpfb�fb�fj��j
��j��j��l��l��l���l���l���l���l���l���l���l���l���l���l���l���l���l��l��l��l��l��l��l��lr��ls��lz��l|��l���l���l���l���l���l���l���l���l���l���l���l���l��l��l��l��l��l��l��l��l��l���l���l���l���l���l��l��l
��l��l��l��l��l��l��m��m��m��m��m��m��m��ms��m��m��m��n��n
��n���n���n���n���n��n��n��n��n��n|��n���n���n���n���n���n���n���n���n���n���n���n���n��n���n
��n��n��n��o��o
��o��o��o��o��o�o�o���o��o��om��o���o���o���o���o���o���o���o���o���o���o���o��o��o��o��o��o��o��o���o���o���o���o��o��o��o��o��p���p���p���p���p��pl��p~��p���p���p���p���p���p���p���p���p��p��p��p��p��p��p��p���p	��p��p��p��p��r�qr
�qr���r���r��qr���r���r��r��r��r��r�qr�qrn��r|��r���r���r���r���r���r���r���r��qr���r���r���r���r���r���r���r���r��r��r��r��r��r��r��r���r���r��r
�qr��r�qr��r��r��s�qs
�qs��s��s��s��s��s��s��s�s��s��s���s�qs�qsj��sm��s}��s��s���s���s���s���s���s���s���s���s���s���s���s���s���s���s���s��s��s��s��s��s��s��s��s���s���s���s���s
��s��s��s��s��s��s��t�qt
�qt���t���t��qt���t���t��t��t��t��t�qt�qtn��t|��t���t���t���t���t���t���t���t��qt���t���t���t���t���t���t���t���t��t��t��t��t��t��t��t���t���t��t
�qt��t�qt��t��t��u�qu
�qu��u��u��u��u��u��u��u�u��u��u���u�qu�quj��um��u}��u��u���u���u���u���u���u���u���u���u���u���u���u���u���u���u���u��u��u��u��u��u��u��u��u���u���u���u���u
��u��u��u��u��u��u��v
��v��x
��x��z��z��z��z��z���z���z���z���z
��z��|�q|�q|���|���|���|���|��|��|��|�q|�q|r��|s��|��|��|��|��|��|	��|��|��|��|��|��}��}
��}��}��}��}��}��}��}���}��}��}���}���}���}���}���}���}��}��}��}��}���}���}���}���}��}��}��}��}��~��~��~���~���~���~���~���~���~���~���~���~���~���~���~���~��~��~��~��~��~��~��~r��~s��~z��~|��~���~���~���~���~���~���~���~���~���~���~���~���~��~��~��~��~��~��~��~��~��~���~���~���~���~���~��~��~
��~��~��~��~��~��~����
��������������������������������������������������������������������������������������q�������������������������������������������������f����������f�������j���l���r�q�s���~�������������������������������������������f��f����������������������������������������������q��q��q��������������������������	�q�
����q����������������������������������������������s����������������������������q�������������������������������������������������f����������f�������j���l���r�q�s���~�������������������������������������������f��f����������������������������������������������q��q��q��������������������������	�q�
����q����������������������������������������������s���������������������������������s���������������������������������s�������������������������������������������������������������������������������������������r���s���z���|��������������������������������������������������������������������������������������������������������
������������������������
���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������r���s���z���|��������������������������������������������������������������������������������������������������������
������������������������
���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������r���s���z���|��������������������������������������������������������������������������������������������������������
������������������������������������������s�������������������������������l���~������������������������������������������������������������	�������������������)����������
������������
����������������������������������|����������������������������������������������������������
���������������
�����������������������������m�����������������������������������������������������������������������������������������������������������������������������������������������r���s���v��������������������������������������	���������������������
������������������������������������������������������������������������������������������������������������)����������)����������������������)������)���������������������������������������������������������������������������������)���������������)����������������������������j���k���l���q���r���s���u���w���y���}���~�����������������������������������������������)��������������������������)��)������������������������������)������)���������������������������������������������������������������������������������������������������������	���
������������������������������)�������������������������������������������������j���s���������������������������������������������
���������������������
��������f�������������������������������|������������������������������������������������������������������������
���������������������
������������������������������������������l���{=�}���~���������������������������������������������������������������������������������������������������������������j��������������������������������������������������������
����������������������������������������������
�������)����������������������������l���{=�}���~���������������������������������������������������������������������������������������������������������������j��������������������������������������������������������
�����������������������������������������l���|���~���������������������������������������������������������������������������	���������������������������������������j��������������������������������������������������������������
�����������������������������������l���|���~���������������������������������������������������������������������������	���������������������������������������j��������������������������������������������������������������
�����������������������������������l���|���~���������������������������������������������������������������������������	���������������������������������������j��������������������������������������������������������������
����������������)����������
�������������������)����������)����������������������)������)���������������������������������������������������������������������������������)���������������)����������������������������j���k���l���q���r���s���u���w���y���}���~�����������������������������������������������)��������������������������)��)������������������������������)������)���������������������������������������������������������������������������������������������������������	���
������������������������������)�������������������������������������������������������������j���s���������������������������������������������
�������������������\�
�\������f�������H�����������������\��\�|�����q���q�����������H��������������������������������������������������������������
�H�����H�����������q�
�q����������q��q�m��������������������������������������������������������q��q�����������������������������������������������
������������
���������������������������������������������������������������������������������������������������������������������l���~������������������������������������������������������������	����������������������������������q������������������)���������������q��������������������������q��������q��������q���������������������������q����q��������������������������������������j�q�k���l���m���q���r�q�s���u���w���y���}���~����q���������������q�������q�������q���������������q���������������������������������������������������q�������q��)�����������������������������������q�����q��������������������������q��q��q��q������������������q�����q�����q���������������������������������	�q�
�q��q��q��������������������q�����q����������������������������������������������������������j���s���������������������������������������������
����������������������������������������������������������������������������������������������������������������������������������)��������������)�������������������j���k���l���q���r���s���u���w���y���}���~�����������������������������������������������)������������������)�������������������������������������������������������������������������������������������������������������������������������	���
���������������������������������������������������������������s����������������������������������������������������������������������������������������������������������������������������)��������������)�������������������j���k���l���q���r���s���u���w���y���}���~�����������������������������������������������)������������������)�������������������������������������������������������������������������������������������������������������������������������	���
���������������������������������������������������������������s���������������������������������������l���{=�}���~���������������������������������������������������������������������������������������������������������������j��������������������������������������������������������
����������������������)����������
�������������)����������
������������
��������������������������������������������|������������������������������������������������������������������������������������
���������������������
���������������������������������������������������������������������������������������
���������
���������������������������������
���������
��������������������������������l���|���~���������������������������������������������������������������������������	���������������������������������������j��������������������������������������������������������������
���������������
����f����������������������������m���|������������������������������������������������������������������������������������������������
������������������������������������)����������
�������������)����������
�������������)����������
����������q�
�q�����������q����������������������q��q�n���|���������������������������������q�����������������������������������������������������������������
�q�����q�������������
����������q�
�q�����������q����������������������q��q�n���|���������������������������������q�����������������������������������������������������������������
�q�����q�������������
������������
������������
����������������������������������������������������������������������������������������r���s���z���|��������������������������������������������������������������������������������������������������������
������������������������
���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������r���s���z���|��������������������������������������������������������������������������������������������������������
������������������������
�������������������������������������������������������������������������������������������������������������������������l���|���~���������������������������������������������������������������������������	���������������������������������������j��������������������������������������������������������������
����������������������������������������
�������)����
����������������������������������������������������������������������������������������������r���s���z���|��������������������������������������������������������������������������������������������������������
������������������������
���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������r���s���z���|��������������������������������������������������������������������������������������������������������
������������������������
���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������r���s���z���|��������������������������������������������������������������������������������������������������������
������������������������
���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������r���s���z���|��������������������������������������������������������������������������������������������������������
������������������������
������������������������������������������������������������������������������������������������������������������q�������������������������������������������������f����������f�������j���l���r�q�s���~�������������������������������������������f��f����������������������������������������������q��q��q��������������������������	�q�
����q����������������������������������������������s����������������������������q�������������������������������������������������f����������f�������j���l���r�q�s���~�������������������������������������������f��f����������������������������������������������q��q��q��������������������������	�q�
����q����������������������������������������������s����������������������������q�������������������������������������������������f����������f�������j���l���r�q�s���~�������������������������������������������f��f����������������������������������������������q��q��q��������������������������	�q�
����q����������������������������������������������s�������������������������������q������������������)���������������q��������������������������q��������q��������q���������������������������q����q��������������������������������������j�q�k���l���m���q���r�q�s���u���w���y���}���~����q���������������q�������q�������q���������������q���������������������������������������������������q�������q��)�����������������������������������q�����q��������������������������q��q��q��q������������������q�����q�����q���������������������������������	�q�
�q��q��q��������������������q�����q����������������������������������������j��s��������������������������������
�������������������)�������)����������������)����)�����������������������������������������������������)���������)�������������������j��k��l��q��r��s��u��w��y��}��~�����������������������������������)�������������������)�)����������������������)����)���������������������������������������������������������������������	��
��������������������)��������������������������������j��s��������������������������������
�������������������������������l��{=}��~������������������������������������������������������������������������������j����������������������������������������
�����������������������������l��|��~������������������������������������������������������	��������������������������j����������������������������������������
��������������
�������������������q�������������������������r��v��|����������������������q�������������������������������������������������������������������������
��������������
���������������������������������������������������������������������
��

��
���
���
���
���
���
��q
���
���
���
��
��
��
��
��
��
��
��
r��
v��
|��
���
���
���
���
���
���
��q
���
���
���
���
���
���
���
���
���
���
��
��
��
��
��
��
��
��
��
��
���
���
���
���
���
��
��
��
��

��
��
��
��
��
����
������������������������������������������������������������������������)������
��������
�������������������q�������������������������r��v��|����������������������q�������������������������������������������������������������������������
��������������
�����������������������������������������������������������������������
�������������������q�������������������������r��v��|����������������������q�������������������������������������������������������������������������
��������������
�����������������������������������������������������������������������������������������r���������������������������������������
��������������������������������������������
���������������������������������r��|���������������������������������������������������������������
����������
������������������������������������������������������������������������������������)������
�������q
�q&��*��-
2��4��7�q9��:��<��������������������������������������������������$�q&�q6��8��:��G��������������q�q_��I��K��M��O��Q��S��U��W��Y��[��]��_��o��q��s����q��
�������q
�q&��*��-
2��4��7�q9��:��<��������������������������������������������������$�q&�q6��8��:��G��������������q�q_��I��K��M��O��Q��S��U��W��Y��[��]��_��o��q��s����q �� 
�� �� ��!�q!
�q!&��!*��!-
!2��!4��!7�q!9��!:��!<��!���!���!���!���!���!���!���!���!��!��!��!��!��!��!��!��!��!��!��!��!$�q!&�q!6��!8��!:��!G��!���!���!���!��!�q!�q!_��!I��!K��!M��!O��!Q��!S��!U��!W��!Y��![��!]��!_��!o��!q��!s��!��q"��"
��"��"��#�q#
�q#&��#*��#-
#2��#4��#7�q#9��#:��#<��#���#���#���#���#���#���#���#���#��#��#��#��#��#��#��#��#��#��#��#��#$�q#&�q#6��#8��#:��#G��#���#���#���#��#�q#�q#_��#I��#K��#M��#O��#Q��#S��#U��#W��#Y��#[��#]��#_��#o��#q��#s��#��q$��$
��$��$��%�q%
�q%&��%*��%-
%2��%4��%7�q%9��%:��%<��%���%���%���%���%���%���%���%���%��%��%��%��%��%��%��%��%��%��%��%��%$�q%&�q%6��%8��%:��%G��%���%���%���%��%�q%�q%_��%I��%K��%M��%O��%Q��%S��%U��%W��%Y��%[��%]��%_��%o��%q��%s��%��q&��&
��&��&��'�q'
�q'&��'*��'-
'2��'4��'7�q'9��':��'<��'���'���'���'���'���'���'���'���'��'��'��'��'��'��'��'��'��'��'��'��'$�q'&�q'6��'8��':��'G��'���'���'���'��'�q'�q'_��'I��'K��'M��'O��'Q��'S��'U��'W��'Y��'[��']��'_��'o��'q��'s��'��q(��(
��(��(��)�q)
�q)&��)*��)-
)2��)4��)7�q)9��):��)<��)���)���)���)���)���)���)���)���)��)��)��)��)��)��)��)��)��)��)��)��)$�q)&�q)6��)8��):��)G��)���)���)���)��)�q)�q)_��)I��)K��)M��)O��)Q��)S��)U��)W��)Y��)[��)]��)_��)o��)q��)s��)��q*��*
��*��*��+�q+
�q+&��+*��+-
+2��+4��+7�q+9��+:��+<��+���+���+���+���+���+���+���+���+��+��+��+��+��+��+��+��+��+��+��+��+$�q+&�q+6��+8��+:��+G��+���+���+���+��+�q+�q+_��+I��+K��+M��+O��+Q��+S��+U��+W��+Y��+[��+]��+_��+o��+q��+s��+��q,��,
��,��,��-�q-
�q-&��-*��--
-2��-4��-7�q-9��-:��-<��-���-���-���-���-���-���-���-���-��-��-��-��-��-��-��-��-��-��-��-��-$�q-&�q-6��-8��-:��-G��-���-���-���-��-�q-�q-_��-I��-K��-M��-O��-Q��-S��-U��-W��-Y��-[��-]��-_��-o��-q��-s��-��q.��.
��.��.��/�q/
�q/&��/*��/-
/2��/4��/7�q/9��/:��/<��/���/���/���/���/���/���/���/���/��/��/��/��/��/��/��/��/��/��/��/��/$�q/&�q/6��/8��/:��/G��/���/���/���/��/�q/�q/_��/I��/K��/M��/O��/Q��/S��/U��/W��/Y��/[��/]��/_��/o��/q��/s��/��q0��0
��0��0��1�q1
�q1&��1*��1-
12��14��17�q19��1:��1<��1���1���1���1���1���1���1���1���1��1��1��1��1��1��1��1��1��1��1��1��1$�q1&�q16��18��1:��1G��1���1���1���1��1�q1�q1_��1I��1K��1M��1O��1Q��1S��1U��1W��1Y��1[��1]��1_��1o��1q��1s��1��q2��2
��2��2��3�q3
�q3&��3*��3-
32��34��37�q39��3:��3<��3���3���3���3���3���3���3���3���3��3��3��3��3��3��3��3��3��3��3��3��3$�q3&�q36��38��3:��3G��3���3���3���3��3�q3�q3_��3I��3K��3M��3O��3Q��3S��3U��3W��3Y��3[��3]��3_��3o��3q��3s��3��q4��4
��4��4��5-{6��6
��6Y��6Z��6[��6\��6]��6���67��6<��6>��6@��6���6���6��6��6p��7-{8��8
��8Y��8Z��8[��8\��8]��8���87��8<��8>��8@��8���8���8��8��8p��9-{:��:
��:Y��:Z��:[��:\��:]��:���:7��:<��:>��:@��:���:���:��:��:p��;-{<��<
��<Y��<Z��<[��<\��<]��<���<7��<<��<>��<@��<���<���<��<��<p��=-{>��>
��>Y��>Z��>[��>\��>]��>���>7��><��>>��>@��>���>���>��>��>p��?-{@��@
��@Y��@Z��@[��@\��@]��@���@7��@<��@>��@@��@���@���@��@��@p��A-{B��B
��BY��BZ��B[��B\��B]��B���B7��B<��B>��B@��B���B���B��B��Bp��C-{D��D
��DY��DZ��D[��D\��D]��D���D7��D<��D>��D@��D���D���D��D��Dp��I��I��I$��I7��I9��I:��I;��I<��I=��I���I���I���I���I���I���I���I��I��I��I$��I&��I6��I8��I:��I;��I=��I?��IC��I���I���I���I���I��I��I��IX��I��I��I!��I#��I%��I'��I)��I+��I-��I/��I1��I3��Io��Iq��Is��I���J��J
��JY��JZ��J[��J\��J]��J���J7��J<��J>��J@��J���J���J��J��Jp��K��K��K$��K7��K9��K:��K;��K<��K=��K���K���K���K���K���K���K���K��K��K��K$��K&��K6��K8��K:��K;��K=��K?��KC��K���K���K���K���K��K��K��KX��K��K��K!��K#��K%��K'��K)��K+��K-��K/��K1��K3��Ko��Kq��Ks��K���L��L
��LY��LZ��L[��L\��L]��L���L7��L<��L>��L@��L���L���L��L��Lp��M��M��M$��M7��M9��M:��M;��M<��M=��M���M���M���M���M���M���M���M��M��M��M$��M&��M6��M8��M:��M;��M=��M?��MC��M���M���M���M���M��M��M��MX��M��M��M!��M#��M%��M'��M)��M+��M-��M/��M1��M3��Mo��Mq��Ms��M���O��O��O$��O7��O9��O:��O;��O<��O=��O���O���O���O���O���O���O���O��O��O��O$��O&��O6��O8��O:��O;��O=��O?��OC��O���O���O���O���O��O��O��OX��O��O��O!��O#��O%��O'��O)��O+��O-��O/��O1��O3��Oo��Oq��Os��O���Q��Q��Q$��Q7��Q9��Q:��Q;��Q<��Q=��Q���Q���Q���Q���Q���Q���Q���Q��Q��Q��Q$��Q&��Q6��Q8��Q:��Q;��Q=��Q?��QC��Q���Q���Q���Q���Q��Q��Q��QX��Q��Q��Q!��Q#��Q%��Q'��Q)��Q+��Q-��Q/��Q1��Q3��Qo��Qq��Qs��Q���S��S��S$��S7��S9��S:��S;��S<��S=��S���S���S���S���S���S���S���S��S��S��S$��S&��S6��S8��S:��S;��S=��S?��SC��S���S���S���S���S��S��S��SX��S��S��S!��S#��S%��S'��S)��S+��S-��S/��S1��S3��So��Sq��Ss��S���U��U��U$��U7��U9��U:��U;��U<��U=��U���U���U���U���U���U���U���U��U��U��U$��U&��U6��U8��U:��U;��U=��U?��UC��U���U���U���U���U��U��U��UX��U��U��U!��U#��U%��U'��U)��U+��U-��U/��U1��U3��Uo��Uq��Us��U���XIRXWRXYfXZfX[fX\fX�fX%RX'RX7fX�fX�fX4RX5RX]RX^RXpfX�RX�RZIRZWRZYfZZfZ[fZ\fZ�fZ%RZ'RZ7fZ�fZ�fZ4RZ5RZ]RZ^RZpfZ�RZ�R\IR\WR\Yf\Zf\[f\\f\�f\%R\'R\7f\�f\�f\4R\5R\]R\^R\pf\�R\�R^IR^WR^Yf^Zf^[f^\f^�f^%R^'R^7f^�f^�f^4R^5R^]R^^R^pf^�R^�R`IR`WR`Yf`Zf`[f`\f`�f`%R`'R`7f`�f`�f`4R`5R`]R`^R`pf`�R`�Ra��a��a$��a���a���a���a���a���a���a��a��a��aC��a��a��aX��a��a��a!��a#��a%��a'��a)��a+��a-��a/��a1��a3��fIffWffYffZff[ff\ff�ff%ff'ff7ff�ff�ff4ff5ff]ff^ffpff�ff�fhIfhWfhYfhZfh[fh\fh�fh%fh'fh7fh�fh�fh4fh5fh]fh^fhpfh�fh�fjIfjWfjYfjZfj[fj\fj�fj%fj'fj7fj�fj�fj4fj5fj]fj^fjpfj�fj�flIflWflYflZfl[fl\fl�fl%fl'fl7fl�fl�fl4fl5fl]fl^flpfl�fl�fnIfnWfnYfnZfn[fn\fn�fn%fn'fn7fn�fn�fn4fn5fn]fn^fnpfn�fn�fo��o��o")o$��o&��o*��o2��o4��oD��oF��oG��oH��oJ��oP��oQ��oR��oS��oT��oU��oV��oX��o]��o���o���o���o���o���o���o���o���o���o���o���o���o���o���o���o���o���o���o���o���o���o���o���o���o���o���o���o���o���o���o���o���o���o���o���o��o��o��o��o��o��o��o��o��o��o��o��o��o��o��o��o��o��o��o��o��o��o��o��o��o��o��o��o��o���o��o��o
��o��o��o��o��o��o��o��o��o��o��o��o!��o+��o-��o/��o1��o3��o5��o<��o>��o@��oC��oD��oF��oG��oH��oJ��o��o��oW��oX��oY��o_��o`��ob��o��o��o��o ��o!��o"��o#��o%��o&��o'��o(��o)��o*��o+��o,��o-��o.��o/��o0��o1��o2��o3��o4��o6��o8��o:��o<��o@��oB��oD��oI��oJ��oK��oL��oM��oN��oO��oQ��oR��oS��oT��oU��oV��oW��oX��oY��oZ��o[��o\��o]��o^��o_��o`��ob��od��of��oh��oj��ol��on��pRp
Rp��p��p")pRp��pRp��q��q��q")q$��q&��q*��q2��q4��qD��qF��qG��qH��qJ��qP��qQ��qR��qS��qT��qU��qV��qX��q]��q���q���q���q���q���q���q���q���q���q���q���q���q���q���q���q���q���q���q���q���q���q���q���q���q���q���q���q���q���q���q���q���q���q���q���q��q��q��q��q��q��q��q��q��q��q��q��q��q��q��q��q��q��q��q��q��q��q��q��q��q��q��q��q��q���q��q��q
��q��q��q��q��q��q��q��q��q��q��q��q!��q+��q-��q/��q1��q3��q5��q<��q>��q@��qC��qD��qF��qG��qH��qJ��q��q��qW��qX��qY��q_��q`��qb��q��q��q��q ��q!��q"��q#��q%��q&��q'��q(��q)��q*��q+��q,��q-��q.��q/��q0��q1��q2��q3��q4��q6��q8��q:��q<��q@��qB��qD��qI��qJ��qK��qL��qM��qN��qO��qQ��qR��qS��qT��qU��qV��qW��qX��qY��qZ��q[��q\��q]��q^��q_��q`��qb��qd��qf��qh��qj��ql��qn��rRr
Rr��r��r")rRr��rRr��s��s��s")s$��s&��s*��s2��s4��sD��sF��sG��sH��sJ��sP��sQ��sR��sS��sT��sU��sV��sX��s]��s���s���s���s���s���s���s���s���s���s���s���s���s���s���s���s���s���s���s���s���s���s���s���s���s���s���s���s���s���s���s���s���s���s���s���s��s��s��s��s��s��s��s��s��s��s��s��s��s��s��s��s��s��s��s��s��s��s��s��s��s��s��s��s��s���s��s��s
��s��s��s��s��s��s��s��s��s��s��s��s!��s+��s-��s/��s1��s3��s5��s<��s>��s@��sC��sD��sF��sG��sH��sJ��s��s��sW��sX��sY��s_��s`��sb��s��s��s��s ��s!��s"��s#��s%��s&��s'��s(��s)��s*��s+��s,��s-��s.��s/��s0��s1��s2��s3��s4��s6��s8��s:��s<��s@��sB��sD��sI��sJ��sK��sL��sM��sN��sO��sQ��sR��sS��sT��sU��sV��sW��sX��sY��sZ��s[��s\��s]��s^��s_��s`��sb��sd��sf��sh��sj��sl��sn��tRt
Rt��t��t")tRt��tRt���{�
{�{�{����������")�$�q�&���*���2���4���7)�D�\�F�q�G�q�H�q�J�q�P���Q���R�q�S���T�q�U���V���X���Y���Z���[���\���]�����q���q���q���q���q���q�������������������������������q���\���\���\���\���\���\���q���q���q���q���q���q���q���q���q���q���q����������������������q��\��q��\��q��\�����q�����q�����q�����q��q��q��q��q��q��q��q�����q�����q�����q�����q�����������
�������q�����q�����q�����q����������!���$)�&)�+���-���/���1���3���5���7���<���>���@���C�q�D�\�F�\�G���H�q�J��������������������������W���X�q�Y�\�_���`�q�b����q��\��q� �\�!�q�"�\�#�q�%�q�&�\�'�q�(�\�)�q�*�\�+�q�,�\�-�q�.�\�/�q�0�\�1�q�2�\�3�q�4�\�6�q�8�q�:�q�<�q�@�q�B�q�D�q�I���J�q�K���L�q�M���N�q�O���Q���R�q�S���T�q�U���V�q�W���X�q�Y���Z�q�[���\�q�]���^�q�_���`�q�b���d���f���h���j���l���n���p����)�)�
)�)�)>9	9BI	9gsR{��.�
.+*Y	r�	�		<	�	Q	i	�y	(	8E	\}	
\�	T5Digitized data copyright � 2010-2011, Google Corporation.Open SansRegularAscender - Open Sans Build 100Version 1.10OpenSansOpen Sans is a trademark of Google and may be registered in certain jurisdictions.Ascender Corporationhttp://www.ascendercorp.com/http://www.ascendercorp.com/typedesigners.htmlLicensed under the Apache License, Version 2.0http://www.apache.org/licenses/LICENSE-2.0Digitized data copyright � 2010-2011, Google Corporation.Open SansRegularAscender - Open Sans Build 100Version 1.10OpenSansOpen Sans is a trademark of Google and may be registered in certain jurisdictions.Ascender Corporationhttp://www.ascendercorp.com/http://www.ascendercorp.com/typedesigners.htmlLicensed under the Apache License, Version 2.0http://www.apache.org/licenses/LICENSE-2.0�ff�	

 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~��������������������������������������������������������������������������������������������������������������������������������	

 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~��������������������������������������������������������������������������������������������������������������������������������	

 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~��������������������������������������������������������������������������������������������������������������������������������	

 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~��������������������������������������������.notdefnullnonmarkingreturnspaceexclamquotedbl
numbersigndollarpercent	ampersandquotesingle	parenleft
parenrightasteriskpluscommahyphenperiodslashzeroonetwothreefourfivesixseveneightninecolon	semicolonlessequalgreaterquestionatABCDEFGHI.altJKLMNOPQRSTUVWXYZbracketleft	backslashbracketrightasciicircum
underscoregraveabcdefghijklmnopqrstuvwxyz	braceleftbar
braceright
asciitildenonbreakingspace
exclamdowncentsterlingcurrencyyen	brokenbarsectiondieresis	copyrightordfeminine
guillemotleft
logicalnotuni00AD
registered	overscoredegree	plusminustwosuperior
threesuperioracutemu	paragraphperiodcenteredcedillaonesuperiorordmasculineguillemotright
onequarteronehalf
threequartersquestiondownAgraveAacuteAcircumflexAtilde	AdieresisAringAECcedillaEgraveEacuteEcircumflex	Edieresis
Igrave.alt
Iacute.altIcircumflex.alt
Idieresis.altEthNtildeOgraveOacuteOcircumflexOtilde	OdieresismultiplyOslashUgraveUacuteUcircumflex	UdieresisYacuteThorn
germandblsagraveaacuteacircumflexatilde	adieresisaringaeccedillaegraveeacuteecircumflex	edieresisigraveiacuteicircumflex	idieresisethntildeograveoacuteocircumflexotilde	odieresisdivideoslashugraveuacuteucircumflex	udieresisyacutethorn	ydieresisAmacronamacronAbreveabreveAogonekaogonekCacutecacuteCcircumflexccircumflexCdotcdotCcaronccaronDcarondcaronDcroatdcroatEmacronemacronEbreveebreve
Edotaccent
edotaccentEogonekeogonekEcaronecaronGcircumflexgcircumflexGbrevegbreveGdotgdotGcommaaccentgcommaaccentHcircumflexhcircumflexHbarhbar
Itilde.altitildeImacron.altimacron
Ibreve.altibreveIogonek.altiogonekIdotaccent.altdotlessiIJ.altijJcircumflexjcircumflexKcommaaccentkcommaaccentkgreenlandicLacutelacuteLcommaaccentlcommaaccentLcaronlcaronLdotldotLslashlslashNacutenacuteNcommaaccentncommaaccentNcaronncaronnapostropheEngengOmacronomacronObreveobreve
Ohungarumlaut
ohungarumlautOEoeRacuteracuteRcommaaccentrcommaaccentRcaronrcaronSacutesacuteScircumflexscircumflexScedillascedillaScaronscaronTcommaaccenttcommaaccentTcarontcaronTbartbarUtildeutildeUmacronumacronUbreveubreveUringuring
Uhungarumlaut
uhungarumlautUogonekuogonekWcircumflexwcircumflexYcircumflexycircumflex	YdieresisZacutezacute
Zdotaccent
zdotaccentZcaronzcaronlongsflorin
Aringacute
aringacuteAEacuteaeacuteOslashacuteoslashacuteScommaaccentscommaaccent
circumflexcaronmacronbreve	dotaccentringogonektildehungarumlauttonos
dieresistonos
Alphatonos	anoteleiaEpsilontonosEtatonos
Iotatonos.altOmicrontonosUpsilontonos
OmegatonosiotadieresistonosAlphaBetaGammauni0394EpsilonZetaEtaThetaIota.altKappaLambdaMuNuXiOmicronPiRhoSigmaTauUpsilonPhiChiPsiuni03A9Iotadieresis.altUpsilondieresis
alphatonosepsilontonosetatonos	iotatonosupsilondieresistonosalphabetagammadeltaepsilonzetaetathetaiotakappalambdauni03BCnuxiomicronpirhosigma1sigmatauupsilonphichipsiomegaiotadieresisupsilondieresisomicrontonosupsilontonos
omegatonos	afii10023	afii10051	afii10052	afii10053	afii10054
afii10055.alt
afii10056.alt	afii10057	afii10058	afii10059	afii10060	afii10061	afii10062	afii10145	afii10017	afii10018	afii10019	afii10020	afii10021	afii10022	afii10024	afii10025	afii10026	afii10027	afii10028	afii10029	afii10030	afii10031	afii10032	afii10033	afii10034	afii10035	afii10036	afii10037	afii10038	afii10039	afii10040	afii10041	afii10042	afii10043	afii10044	afii10045	afii10046	afii10047	afii10048	afii10049	afii10065	afii10066	afii10067	afii10068	afii10069	afii10070	afii10072	afii10073	afii10074	afii10075	afii10076	afii10077	afii10078	afii10079	afii10080	afii10081	afii10082	afii10083	afii10084	afii10085	afii10086	afii10087	afii10088	afii10089	afii10090	afii10091	afii10092	afii10093	afii10094	afii10095	afii10096	afii10097	afii10071	afii10099	afii10100	afii10101	afii10102	afii10103	afii10104	afii10105	afii10106	afii10107	afii10108	afii10109	afii10110	afii10193	afii10050	afii10098WgravewgraveWacutewacute	Wdieresis	wdieresisYgraveygraveendashemdash	afii00208
underscoredbl	quoteleft
quoterightquotesinglbase
quotereversedquotedblleft
quotedblrightquotedblbasedagger	daggerdblbulletellipsisperthousandminutesecond
guilsinglleftguilsinglright	exclamdblfraction	nsuperiorfranc	afii08941pesetaEuro	afii61248	afii61289	afii61352	trademarkOmega	estimated	oneeighththreeeighthsfiveeighthsseveneighthspartialdiffDeltaproduct	summationminusradicalinfinityintegralapproxequalnotequal	lessequalgreaterequallozengeuniFB01uniFB02
cyrillicbrevedotlessjcaroncommaaccentcommaaccentcommaaccentrotatezerosuperiorfoursuperiorfivesuperiorsixsuperior
sevensuperior
eightsuperiorninesuperioruni2000uni2001uni2002uni2003uni2004uni2005uni2006uni2007uni2008uni2009uni200Auni200BuniFEFFuniFFFCuniFFFDuni01F0uni02BCuni03D1uni03D2uni03D6uni1E3Euni1E3Funi1E00uni1E01uni1F4Duni02F3	dasiaoxiauniFB03uniFB04OhornohornUhornuhornuni0300uni0301uni0303hookdotbelowuni0400uni040Duni0450uni045Duni0460uni0461uni0462uni0463uni0464uni0465uni0466uni0467uni0468uni0469uni046Auni046Buni046Cuni046Duni046Euni046Funi0470uni0471uni0472uni0473uni0474uni0475uni0476uni0477uni0478uni0479uni047Auni047Buni047Cuni047Duni047Euni047Funi0480uni0481uni0482uni0483uni0484uni0485uni0486uni0488uni0489uni048Auni048Buni048Cuni048Duni048Euni048Funi0492uni0493uni0494uni0495uni0496uni0497uni0498uni0499uni049Auni049Buni049Cuni049Duni049Euni049Funi04A0uni04A1uni04A2uni04A3uni04A4uni04A5uni04A6uni04A7uni04A8uni04A9uni04AAuni04ABuni04ACuni04ADuni04AEuni04AFuni04B0uni04B1uni04B2uni04B3uni04B4uni04B5uni04B6uni04B7uni04B8uni04B9uni04BAuni04BBuni04BCuni04BDuni04BEuni04BFuni04C0.altuni04C1uni04C2uni04C3uni04C4uni04C5uni04C6uni04C7uni04C8uni04C9uni04CAuni04CBuni04CCuni04CDuni04CEuni04CF.altuni04D0uni04D1uni04D2uni04D3uni04D4uni04D5uni04D6uni04D7uni04D8uni04D9uni04DAuni04DBuni04DCuni04DDuni04DEuni04DFuni04E0uni04E1uni04E2uni04E3uni04E4uni04E5uni04E6uni04E7uni04E8uni04E9uni04EAuni04EBuni04ECuni04EDuni04EEuni04EFuni04F0uni04F1uni04F2uni04F3uni04F4uni04F5uni04F6uni04F7uni04F8uni04F9uni04FAuni04FBuni04FCuni04FDuni04FEuni04FFuni0500uni0501uni0502uni0503uni0504uni0505uni0506uni0507uni0508uni0509uni050Auni050Buni050Cuni050Duni050Euni050Funi0510uni0511uni0512uni0513uni1EA0uni1EA1uni1EA2uni1EA3uni1EA4uni1EA5uni1EA6uni1EA7uni1EA8uni1EA9uni1EAAuni1EABuni1EACuni1EADuni1EAEuni1EAFuni1EB0uni1EB1uni1EB2uni1EB3uni1EB4uni1EB5uni1EB6uni1EB7uni1EB8uni1EB9uni1EBAuni1EBBuni1EBCuni1EBDuni1EBEuni1EBFuni1EC0uni1EC1uni1EC2uni1EC3uni1EC4uni1EC5uni1EC6uni1EC7uni1EC8.altuni1EC9uni1ECA.altuni1ECBuni1ECCuni1ECDuni1ECEuni1ECFuni1ED0uni1ED1uni1ED2uni1ED3uni1ED4uni1ED5uni1ED6uni1ED7uni1ED8uni1ED9uni1EDAuni1EDBuni1EDCuni1EDDuni1EDEuni1EDFuni1EE0uni1EE1uni1EE2uni1EE3uni1EE4uni1EE5uni1EE6uni1EE7uni1EE8uni1EE9uni1EEAuni1EEBuni1EECuni1EEDuni1EEEuni1EEFuni1EF0uni1EF1uni1EF4uni1EF5uni1EF6uni1EF7uni1EF8uni1EF9uni20ABuni030Fcircumflexacutecombcircumflexgravecombcircumflexhookcombcircumflextildecombbreveacutecombbrevegravecomb
brevehookcombbrevetildecombcyrillichookleftcyrillicbighookUCcyrillicbighookLCone.pnumzero.osone.ostwo.osthree.osfour.osfive.ossix.osseven.oseight.osnine.osffuni2120Tcedillatcedillag.altgcircumflex.alt
gbreve.altgdot.altgcommaaccent.altIIgraveIacuteIcircumflex	IdieresisItildeImacronIbreveIogonek
IdotaccentIJ	IotatonosIotaIotadieresis	afii10055	afii10056uni04C0uni04CFuni1EC8uni1ECA

���
46latnMOL ROM ������
n�latnMOL (ROM B��	��
	��


liga�liga�liga�lnum�lnum�lnum�locl�locl�onum�onum�onum�pnum�pnum�pnumsalt
saltsaltss01"ss01*ss012ss02:ss02@ss02Fss03Lss03Rss03Xtnum^tnumftnumn			
&.6>FNV^Pz����2H�����JJ��������.,����������Zgw����EG��
����������
������������	�
���
����� !$%IJ6"(^IO]IL�I5O4LI^V0�R	*�H��
��C0�?10	+0a
+�7�S0Q0,
+�7��<<<Obsolete>>>0!0	+������@�mn�TA6���}��]0�z0�b�8%��a����&��Z�0
	*�H��
0S10	UUS10U
VeriSign, Inc.1+0)U"VeriSign Time Stamping Services CA0
070615000000Z
120614235959Z0\10	UUS10U
VeriSign, Inc.1402U+VeriSign Time Stamping Services Signer - G20��0
	*�H��
��0����ĵ�R���`)J[/K�k���5TX5��6^bMRQ4q�{f���*�j
�7٘t������v��JcEG.k�NK+��XJ���,���X��B�-�uލ�ǎ�lL����g�r�Iž`<���cxi{�-���0��04+(0&0$+0�http://ocsp.verisign.com0U�003U,0*0(�&�$�"http://crl.verisign.com/tss-ca.crl0U%�0
+0U��0U0�010
UTSA1-20
	*�H��
�P�K�$���
$�������-��7
�,�Za�����񑑳V@�뒾89�u6t:�O�7���ʕB��Ǡ�W��dB5N�3��M�'���L8M�x�S����ݤ��^�⥾����`�߭(�ǥKd��[��9�8"�3�/���!?DA	�e$�H�D������T���\�y>]r}��,C��S�}=�*:�O��m
�]�^S��Wp��������`�+n����x'���4[^�I2�30��0�-�G��ߍRFC��mH
1�0
	*�H��
0��10	UZA10UWestern Cape10UDurbanville10
U
Thawte10UThawte Certification10UThawte Timestamping CA0
031204000000Z
131203235959Z0S10	UUS10U
VeriSign, Inc.1+0)U"VeriSign Time Stamping Services CA0�"0
	*�H��
�0�
��ʲ��� �
}���u�N���ga��dڻ��3��0�X~��k�6����x�w�~o<���
�h�l�ʽR-�H=���]_��/k�������LR�`�@~�
�?Ǵ߇�_zj1.���G �1s
W-�x43����h/���Š�*Ë!�f��XWou�<�&�]�<���T�
n��Jݹ�"|�>'�x�1���"�ijGC�_���^��|�}�b��M��"V��ͮ�v��
��M٠�h��;�������0��04+(0&0$+0�http://ocsp.verisign.com0U�0�0AU:0806�4�2�0http://crl.verisign.com/ThawteTimestampingCA.crl0U%0
+0U�0$U0�010UTSA2048-1-530
	*�H��
��Jk��X�D1�y�+�����LͰ�Xn�)�^�ʓ�R
�G'/8��ɓN��"b�?7!Op1��8������U�N$ҩ'Nz��aA�*����^ݻ+�>�����W����~����+�;R8'�?J0��0�e�eR&�.�Y)��"�\0
	*�H��
0_10	UUS10U
VeriSign, Inc.1705U.Class 3 Public Primary Certification Authority0
090521000000Z
190520235959Z0��10	UUS10U
VeriSign, Inc.10UVeriSign Trust Network1;09U2Terms of use at https://www.verisign.com/rpa (c)09100.U'VeriSign Class 3 Code Signing 2009-2 CA0�"0
	*�H��
�0�
��g�`�IoV|f�^�
��q��������-�!��ќPL��"���5;��	��.Z��|=;%��X{����
��ξ'tag'Mj��aXy��'��M4+G D��f$f��O��8�T��r�fuj�Ih�8y
�0��,`H�ת���8�09�:|@T���/�ܨR>��+�!��\���P4.M���^%Ԍ��n|)�]�1�ZՌ�gX���5��+�!����`x^{`��W]A
cT`�C!����0��0U�0�0pU i0g0e`�H��E0V0(+https://www.verisign.com/cps0*+0https://www.verisign.com/rpa0U�0m+a0_�]�[0Y0W0U	image/gif0!00+�������k�πj�H,{.0%#http://logo.verisign.com/vslogo.gif0U%0++04+(0&0$+0�http://ocsp.verisign.com01U*0(0&�$�"� http://crl.verisign.com/pca3.crl0)U"0 �010UClass3CA2048-1-550U��k�&pȡ?�-�5����0
	*�H��
����ݔ�A�ai��x�0Ɛ<~B�$��s���/��D�r�P�U �n���Qj�71ܥ-��O�M2���N��gUe�j�z�d8xEv1�z`³]���fv�Y��I�8V��AwX0�0���f��gy�mPSo��0
	*�H��
0��10	UUS10U
VeriSign, Inc.10UVeriSign Trust Network1;09U2Terms of use at https://www.verisign.com/rpa (c)09100.U'VeriSign Class 3 Code Signing 2009-2 CA0
100729000000Z
120808235959Z0��10	UUS10U
Massachusetts10
UWoburn10U
Monotype Imaging Inc.1>0<U5Digital ID Class 3 - Microsoft Software Validation v210UType Operations10UMonotype Imaging Inc.0��0
	*�H��
��0�����D��i|U
���25�L3�^ �L�*�8ט�@�I"SO�C�ʋ�V�nH�9c;$�����5}r�GW�yˊJ�@p-5c���į�����פ��	��{��u��ePd"��}���K�XEM�YLM���0�0	U00U��0DU=0;09�7�5�3http://csc3-2009-2-crl.verisign.com/CSC3-2009-2.crl0DU =0;09`�H��E0*0(+https://www.verisign.com/rpa0U%0
+0u+i0g0$+0�http://ocsp.verisign.com0?+0�3http://csc3-2009-2-aia.verisign.com/CSC3-2009-2.cer0U#0���k�&pȡ?�-�5����0	`�H��B0
+�70�0
	*�H��
�N�"��gA���~�™�c���jrb��<8�=_�G��_[KI� ��	�VD�����
5�<�D�`E*���oL;�4gp��Z9\Z�l��5|eK��m��I��p�=�b��۴���A�~�}����n�"��w6M�ZS1�+(�R�zk�wD���]%,�͊0>K�yʦN���$����񺐶���\<��'M<�o3�ӆ��X3u=�i�DoNl�Յ�V���?�L!h��`���]9!2�1�g0�c0��0��10	UUS10U
VeriSign, Inc.10UVeriSign Trust Network1;09U2Terms of use at https://www.verisign.com/rpa (c)09100.U'VeriSign Class 3 Code Signing 2009-2 CAf��gy�mPSo��0	+�p0
+�7100	*�H��
	1
+�70
+�710
+�70#	*�H��
	1H���c�ƱW' �e�S�0
	*�H��
��E;�Ժ���b;��J�EqA��.��R�A�m2,H�)���/]d$4.����Js��A��h���xA�S��~xR[�!Bܾ	�3�FP�;�+Yi��c�-��4�����T@�G��f��>��0�{	*�H��
	1�l0�h0g0S10	UUS10U
VeriSign, Inc.1+0)U"VeriSign Time Stamping Services CA8%��a����&��Z�0	+�]0	*�H��
	1	*�H��
0	*�H��
	1
110505165510Z0#	*�H��
	1T+��'��S��8V0
	*�H��
���w���o"�k�E�N�@��;'JV�:���j|���{�`N�+W����g3�+)�쾼Y��)����$�w�����ILt�=.o �����!9�V�����8�����c����hX���)����vendor/endroid/qr-code/src/Writer/PngWriter.php000064400000023703150755130600015523 0ustar00<?php

declare (strict_types=1);
/*
 * (c) Jeroen van den Enden <info@endroid.nl>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */
namespace WP2FA_Vendor\Endroid\QrCode\Writer;

use WP2FA_Vendor\Endroid\QrCode\Exception\GenerateImageException;
use WP2FA_Vendor\Endroid\QrCode\Exception\MissingFunctionException;
use WP2FA_Vendor\Endroid\QrCode\Exception\MissingLogoHeightException;
use WP2FA_Vendor\Endroid\QrCode\Exception\ValidationException;
use WP2FA_Vendor\Endroid\QrCode\LabelAlignment;
use WP2FA_Vendor\Endroid\QrCode\QrCodeInterface;
use WP2FA_Vendor\Zxing\QrReader;
class PngWriter extends AbstractWriter
{
    public function writeString(QrCodeInterface $qrCode) : string
    {
        if (!\extension_loaded('gd')) {
            throw new GenerateImageException('Unable to generate image: check your GD installation');
        }
        $image = $this->createImage($qrCode->getData(), $qrCode);
        $logoPath = $qrCode->getLogoPath();
        if (null !== $logoPath) {
            $image = $this->addLogo($image, $logoPath, $qrCode->getLogoWidth(), $qrCode->getLogoHeight());
        }
        $label = $qrCode->getLabel();
        if (null !== $label) {
            $image = $this->addLabel($image, $label, $qrCode->getLabelFontPath(), $qrCode->getLabelFontSize(), $qrCode->getLabelAlignment(), $qrCode->getLabelMargin(), $qrCode->getForegroundColor(), $qrCode->getBackgroundColor());
        }
        $string = $this->imageToString($image);
        if (\PHP_VERSION_ID < 80000) {
            \imagedestroy($image);
        }
        if ($qrCode->getValidateResult()) {
            $reader = new QrReader($string, QrReader::SOURCE_TYPE_BLOB);
            if ($reader->text() !== $qrCode->getText()) {
                throw new ValidationException('Built-in validation reader read "' . $reader->text() . '" instead of "' . $qrCode->getText() . '".
                     Adjust your parameters to increase readability or disable built-in validation.');
            }
        }
        return $string;
    }
    /**
     * @param array<mixed> $data
     *
     * @return mixed
     */
    private function createImage(array $data, QrCodeInterface $qrCode)
    {
        $baseSize = $qrCode->getRoundBlockSize() ? $data['block_size'] : 25;
        $baseImage = $this->createBaseImage($baseSize, $data, $qrCode);
        $interpolatedImage = $this->createInterpolatedImage($baseImage, $data, $qrCode);
        if (\PHP_VERSION_ID < 80000) {
            \imagedestroy($baseImage);
        }
        return $interpolatedImage;
    }
    /**
     * @param array<mixed> $data
     *
     * @return mixed
     */
    private function createBaseImage(int $baseSize, array $data, QrCodeInterface $qrCode)
    {
        $image = \imagecreatetruecolor($data['block_count'] * $baseSize, $data['block_count'] * $baseSize);
        if (!$image) {
            throw new GenerateImageException('Unable to generate image: check your GD installation');
        }
        $foregroundColor = \imagecolorallocatealpha($image, $qrCode->getForegroundColor()['r'], $qrCode->getForegroundColor()['g'], $qrCode->getForegroundColor()['b'], $qrCode->getForegroundColor()['a']);
        if (!\is_int($foregroundColor)) {
            throw new GenerateImageException('Foreground color could not be allocated');
        }
        $backgroundColor = \imagecolorallocatealpha($image, $qrCode->getBackgroundColor()['r'], $qrCode->getBackgroundColor()['g'], $qrCode->getBackgroundColor()['b'], $qrCode->getBackgroundColor()['a']);
        if (!\is_int($backgroundColor)) {
            throw new GenerateImageException('Background color could not be allocated');
        }
        \imagefill($image, 0, 0, $backgroundColor);
        foreach ($data['matrix'] as $row => $values) {
            foreach ($values as $column => $value) {
                if (1 === $value) {
                    \imagefilledrectangle($image, $column * $baseSize, $row * $baseSize, \intval(($column + 1) * $baseSize), \intval(($row + 1) * $baseSize), $foregroundColor);
                }
            }
        }
        return $image;
    }
    /**
     * @param mixed        $baseImage
     * @param array<mixed> $data
     *
     * @return mixed
     */
    private function createInterpolatedImage($baseImage, array $data, QrCodeInterface $qrCode)
    {
        $image = \imagecreatetruecolor($data['outer_width'], $data['outer_height']);
        if (!$image) {
            throw new GenerateImageException('Unable to generate image: check your GD installation');
        }
        $backgroundColor = \imagecolorallocatealpha($image, $qrCode->getBackgroundColor()['r'], $qrCode->getBackgroundColor()['g'], $qrCode->getBackgroundColor()['b'], $qrCode->getBackgroundColor()['a']);
        if (!\is_int($backgroundColor)) {
            throw new GenerateImageException('Background color could not be allocated');
        }
        \imagefill($image, 0, 0, $backgroundColor);
        \imagecopyresampled($image, $baseImage, (int) $data['margin_left'], (int) $data['margin_left'], 0, 0, (int) $data['inner_width'], (int) $data['inner_height'], \imagesx($baseImage), \imagesy($baseImage));
        if ($qrCode->getBackgroundColor()['a'] > 0) {
            \imagesavealpha($image, \true);
        }
        return $image;
    }
    /**
     * @param mixed $sourceImage
     *
     * @return mixed
     */
    private function addLogo($sourceImage, string $logoPath, int $logoWidth = null, int $logoHeight = null)
    {
        $mimeType = $this->getMimeType($logoPath);
        $logoImage = \imagecreatefromstring(\strval(\file_get_contents($logoPath)));
        if ('image/svg+xml' === $mimeType && (null === $logoHeight || null === $logoWidth)) {
            throw new MissingLogoHeightException('SVG Logos require an explicit height set via setLogoSize($width, $height)');
        }
        if (!$logoImage) {
            throw new GenerateImageException('Unable to generate image: check your GD installation or logo path');
        }
        $logoSourceWidth = \imagesx($logoImage);
        $logoSourceHeight = \imagesy($logoImage);
        if (null === $logoWidth) {
            $logoWidth = $logoSourceWidth;
        }
        if (null === $logoHeight) {
            $aspectRatio = $logoWidth / $logoSourceWidth;
            $logoHeight = \intval($logoSourceHeight * $aspectRatio);
        }
        $logoX = \imagesx($sourceImage) / 2 - $logoWidth / 2;
        $logoY = \imagesy($sourceImage) / 2 - $logoHeight / 2;
        \imagecopyresampled($sourceImage, $logoImage, \intval($logoX), \intval($logoY), 0, 0, $logoWidth, $logoHeight, $logoSourceWidth, $logoSourceHeight);
        if (\PHP_VERSION_ID < 80000) {
            \imagedestroy($logoImage);
        }
        return $sourceImage;
    }
    /**
     * @param mixed      $sourceImage
     * @param array<int> $labelMargin
     * @param array<int> $foregroundColor
     * @param array<int> $backgroundColor
     *
     * @return mixed
     */
    private function addLabel($sourceImage, string $label, string $labelFontPath, int $labelFontSize, string $labelAlignment, array $labelMargin, array $foregroundColor, array $backgroundColor)
    {
        if (!\function_exists('imagettfbbox')) {
            throw new MissingFunctionException('Missing function "imagettfbbox", please make sure you installed the FreeType library');
        }
        $labelBox = \imagettfbbox($labelFontSize, 0, $labelFontPath, $label);
        if (!$labelBox) {
            throw new GenerateImageException('Unable to add label: check your GD installation');
        }
        $labelBoxWidth = \intval($labelBox[2] - $labelBox[0]);
        $labelBoxHeight = \intval($labelBox[0] - $labelBox[7]);
        $sourceWidth = \imagesx($sourceImage);
        $sourceHeight = \imagesy($sourceImage);
        $targetWidth = $sourceWidth;
        $targetHeight = $sourceHeight + $labelBoxHeight + $labelMargin['t'] + $labelMargin['b'];
        // Create empty target image
        $targetImage = \imagecreatetruecolor($targetWidth, $targetHeight);
        if (!$targetImage) {
            throw new GenerateImageException('Unable to generate image: check your GD installation');
        }
        $foregroundColor = \imagecolorallocate($targetImage, $foregroundColor['r'], $foregroundColor['g'], $foregroundColor['b']);
        if (!\is_int($foregroundColor)) {
            throw new GenerateImageException('Foreground color could not be allocated');
        }
        $backgroundColor = \imagecolorallocate($targetImage, $backgroundColor['r'], $backgroundColor['g'], $backgroundColor['b']);
        if (!\is_int($backgroundColor)) {
            throw new GenerateImageException('Background color could not be allocated');
        }
        \imagefill($targetImage, 0, 0, $backgroundColor);
        // Copy source image to target image
        \imagecopyresampled($targetImage, $sourceImage, 0, 0, 0, 0, $sourceWidth, $sourceHeight, $sourceWidth, $sourceHeight);
        if (\PHP_VERSION_ID < 80000) {
            \imagedestroy($sourceImage);
        }
        switch ($labelAlignment) {
            case LabelAlignment::LEFT:
                $labelX = $labelMargin['l'];
                break;
            case LabelAlignment::RIGHT:
                $labelX = $targetWidth - $labelBoxWidth - $labelMargin['r'];
                break;
            default:
                $labelX = \intval($targetWidth / 2 - $labelBoxWidth / 2);
                break;
        }
        $labelY = $targetHeight - $labelMargin['b'];
        \imagettftext($targetImage, $labelFontSize, 0, $labelX, $labelY, $foregroundColor, $labelFontPath, $label);
        return $targetImage;
    }
    /**
     * @param mixed $image
     */
    private function imageToString($image) : string
    {
        \ob_start();
        \imagepng($image);
        return (string) \ob_get_clean();
    }
    public static function getContentType() : string
    {
        return 'image/png';
    }
    public static function getSupportedExtensions() : array
    {
        return ['png'];
    }
    public function getName() : string
    {
        return 'png';
    }
}
vendor/endroid/qr-code/src/Writer/AbstractWriter.php000064400000005204150755130600016536 0ustar00<?php

declare (strict_types=1);
/*
 * (c) Jeroen van den Enden <info@endroid.nl>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */
namespace WP2FA_Vendor\Endroid\QrCode\Writer;

use WP2FA_Vendor\Endroid\QrCode\Exception\GenerateImageException;
use WP2FA_Vendor\Endroid\QrCode\Exception\InvalidLogoException;
use WP2FA_Vendor\Endroid\QrCode\Exception\MissingExtensionException;
use WP2FA_Vendor\Endroid\QrCode\QrCodeInterface;
abstract class AbstractWriter implements WriterInterface
{
    protected function getMimeType(string $path) : string
    {
        if (\false !== \filter_var($path, \FILTER_VALIDATE_URL)) {
            return $this->getMimeTypeFromUrl($path);
        }
        return $this->getMimeTypeFromPath($path);
    }
    private function getMimeTypeFromUrl(string $url) : string
    {
        /** @var mixed $format */
        $format = \PHP_VERSION > 80000 ? \true : 1;
        $headers = \get_headers($url, $format);
        if (!\is_array($headers) || !isset($headers['Content-Type'])) {
            throw new InvalidLogoException(\sprintf('Content type could not be determined for logo URL "%s"', $url));
        }
        return $headers['Content-Type'];
    }
    private function getMimeTypeFromPath(string $path) : string
    {
        if (!\function_exists('mime_content_type')) {
            throw new MissingExtensionException('You need the ext-fileinfo extension to determine logo mime type');
        }
        $mimeType = \mime_content_type($path);
        if (!\is_string($mimeType)) {
            throw new InvalidLogoException('Could not determine mime type');
        }
        if (!\preg_match('#^image/#', $mimeType)) {
            throw new GenerateImageException('Logo path is not an image');
        }
        // Passing mime type image/svg results in invisible images
        if ('image/svg' === $mimeType) {
            return 'image/svg+xml';
        }
        return $mimeType;
    }
    public function writeDataUri(QrCodeInterface $qrCode) : string
    {
        $dataUri = 'data:' . $this->getContentType() . ';base64,' . \base64_encode($this->writeString($qrCode));
        return $dataUri;
    }
    public function writeFile(QrCodeInterface $qrCode, string $path) : void
    {
        $string = $this->writeString($qrCode);
        \file_put_contents($path, $string);
    }
    public static function supportsExtension(string $extension) : bool
    {
        return \in_array($extension, static::getSupportedExtensions());
    }
    public static function getSupportedExtensions() : array
    {
        return [];
    }
    public abstract function getName() : string;
}
vendor/endroid/qr-code/src/Writer/EpsWriter.php000064400000004177150755130600015532 0ustar00<?php

declare (strict_types=1);
/*
 * (c) Jeroen van den Enden <info@endroid.nl>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */
namespace WP2FA_Vendor\Endroid\QrCode\Writer;

use WP2FA_Vendor\Endroid\QrCode\QrCodeInterface;
class EpsWriter extends AbstractWriter
{
    public function writeString(QrCodeInterface $qrCode) : string
    {
        $data = $qrCode->getData();
        $epsData = [];
        $epsData[] = '%!PS-Adobe-3.0 EPSF-3.0';
        $epsData[] = '%%BoundingBox: 0 0 ' . $data['outer_width'] . ' ' . $data['outer_height'];
        $epsData[] = '/F { rectfill } def';
        $epsData[] = \number_format($qrCode->getBackgroundColor()['r'] / 100, 2, '.', ',') . ' ' . \number_format($qrCode->getBackgroundColor()['g'] / 100, 2, '.', ',') . ' ' . \number_format($qrCode->getBackgroundColor()['b'] / 100, 2, '.', ',') . ' setrgbcolor';
        $epsData[] = '0 0 ' . $data['outer_width'] . ' ' . $data['outer_height'] . ' F';
        $epsData[] = \number_format($qrCode->getForegroundColor()['r'] / 100, 2, '.', ',') . ' ' . \number_format($qrCode->getForegroundColor()['g'] / 100, 2, '.', ',') . ' ' . \number_format($qrCode->getForegroundColor()['b'] / 100, 2, '.', ',') . ' setrgbcolor';
        // Please note an EPS has a reversed Y axis compared to PNG and SVG
        $data['matrix'] = \array_reverse($data['matrix']);
        foreach ($data['matrix'] as $row => $values) {
            foreach ($values as $column => $value) {
                if (1 === $value) {
                    $x = $data['margin_left'] + $data['block_size'] * $column;
                    $y = $data['margin_left'] + $data['block_size'] * $row;
                    $epsData[] = $x . ' ' . $y . ' ' . $data['block_size'] . ' ' . $data['block_size'] . ' F';
                }
            }
        }
        return \implode("\n", $epsData);
    }
    public static function getContentType() : string
    {
        return 'image/eps';
    }
    public static function getSupportedExtensions() : array
    {
        return ['eps'];
    }
    public function getName() : string
    {
        return 'eps';
    }
}
vendor/endroid/qr-code/src/Writer/BinaryWriter.php000064400000001733150755130600016222 0ustar00<?php

declare (strict_types=1);
/*
 * (c) Jeroen van den Enden <info@endroid.nl>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */
namespace WP2FA_Vendor\Endroid\QrCode\Writer;

use WP2FA_Vendor\Endroid\QrCode\QrCodeInterface;
class BinaryWriter extends AbstractWriter
{
    public function writeString(QrCodeInterface $qrCode) : string
    {
        $rows = [];
        $data = $qrCode->getData();
        foreach ($data['matrix'] as $row) {
            $values = '';
            foreach ($row as $value) {
                $values .= $value;
            }
            $rows[] = $values;
        }
        return \implode("\n", $rows);
    }
    public static function getContentType() : string
    {
        return 'text/plain';
    }
    public static function getSupportedExtensions() : array
    {
        return ['bin', 'txt'];
    }
    public function getName() : string
    {
        return 'binary';
    }
}
vendor/endroid/qr-code/src/Writer/WriterInterface.php000064400000001437150755130600016677 0ustar00<?php

declare (strict_types=1);
/*
 * (c) Jeroen van den Enden <info@endroid.nl>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */
namespace WP2FA_Vendor\Endroid\QrCode\Writer;

use WP2FA_Vendor\Endroid\QrCode\QrCodeInterface;
interface WriterInterface
{
    public function writeString(QrCodeInterface $qrCode) : string;
    public function writeDataUri(QrCodeInterface $qrCode) : string;
    public function writeFile(QrCodeInterface $qrCode, string $path) : void;
    public static function getContentType() : string;
    public static function supportsExtension(string $extension) : bool;
    /** @return array<string> */
    public static function getSupportedExtensions() : array;
    public function getName() : string;
}
vendor/endroid/qr-code/src/Writer/SvgWriter.php000064400000015363150755130600015541 0ustar00<?php

declare (strict_types=1);
/*
 * (c) Jeroen van den Enden <info@endroid.nl>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */
namespace WP2FA_Vendor\Endroid\QrCode\Writer;

use WP2FA_Vendor\Endroid\QrCode\Exception\GenerateImageException;
use WP2FA_Vendor\Endroid\QrCode\Exception\MissingLogoHeightException;
use WP2FA_Vendor\Endroid\QrCode\Exception\ValidationException;
use WP2FA_Vendor\Endroid\QrCode\QrCodeInterface;
use SimpleXMLElement;
class SvgWriter extends AbstractWriter
{
    public function writeString(QrCodeInterface $qrCode) : string
    {
        $options = $qrCode->getWriterOptions();
        if ($qrCode->getValidateResult()) {
            throw new ValidationException('Built-in validation reader can not check SVG images: please disable via setValidateResult(false)');
        }
        $data = $qrCode->getData();
        $svg = new SimpleXMLElement('<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"/>');
        $svg->addAttribute('version', '1.1');
        $svg->addAttribute('width', $data['outer_width'] . 'px');
        $svg->addAttribute('height', $data['outer_height'] . 'px');
        $svg->addAttribute('viewBox', '0 0 ' . $data['outer_width'] . ' ' . $data['outer_height']);
        $svg->addChild('defs');
        // Block definition
        $block_id = isset($options['rect_id']) && $options['rect_id'] ? $options['rect_id'] : 'block';
        $blockDefinition = $svg->defs->addChild('rect');
        $blockDefinition->addAttribute('id', $block_id);
        $blockDefinition->addAttribute('width', \strval($data['block_size']));
        $blockDefinition->addAttribute('height', \strval($data['block_size']));
        $blockDefinition->addAttribute('fill', '#' . \sprintf('%02x%02x%02x', $qrCode->getForegroundColor()['r'], $qrCode->getForegroundColor()['g'], $qrCode->getForegroundColor()['b']));
        $blockDefinition->addAttribute('fill-opacity', \strval($this->getOpacity($qrCode->getForegroundColor()['a'])));
        // Background
        $background = $svg->addChild('rect');
        $background->addAttribute('x', '0');
        $background->addAttribute('y', '0');
        $background->addAttribute('width', \strval($data['outer_width']));
        $background->addAttribute('height', \strval($data['outer_height']));
        $background->addAttribute('fill', '#' . \sprintf('%02x%02x%02x', $qrCode->getBackgroundColor()['r'], $qrCode->getBackgroundColor()['g'], $qrCode->getBackgroundColor()['b']));
        $background->addAttribute('fill-opacity', \strval($this->getOpacity($qrCode->getBackgroundColor()['a'])));
        foreach ($data['matrix'] as $row => $values) {
            foreach ($values as $column => $value) {
                if (1 === $value) {
                    $block = $svg->addChild('use');
                    $block->addAttribute('x', \strval($data['margin_left'] + $data['block_size'] * $column));
                    $block->addAttribute('y', \strval($data['margin_left'] + $data['block_size'] * $row));
                    $block->addAttribute('xlink:href', '#' . $block_id, 'http://www.w3.org/1999/xlink');
                }
            }
        }
        $logoPath = $qrCode->getLogoPath();
        if (\is_string($logoPath)) {
            $forceXlinkHref = \false;
            if (isset($options['force_xlink_href']) && $options['force_xlink_href']) {
                $forceXlinkHref = \true;
            }
            $this->addLogo($svg, $data['outer_width'], $data['outer_height'], $logoPath, $qrCode->getLogoWidth(), $qrCode->getLogoHeight(), $forceXlinkHref);
        }
        $xml = $svg->asXML();
        if (!\is_string($xml)) {
            throw new GenerateImageException('Unable to save SVG XML');
        }
        if (isset($options['exclude_xml_declaration']) && $options['exclude_xml_declaration']) {
            $xml = \str_replace("<?xml version=\"1.0\"?>\n", '', $xml);
        }
        return $xml;
    }
    private function addLogo(SimpleXMLElement $svg, int $imageWidth, int $imageHeight, string $logoPath, int $logoWidth = null, int $logoHeight = null, bool $forceXlinkHref = \false) : void
    {
        $mimeType = $this->getMimeType($logoPath);
        $imageData = \file_get_contents($logoPath);
        if (!\is_string($imageData)) {
            throw new GenerateImageException('Unable to read image data: check your logo path');
        }
        if ('image/svg+xml' === $mimeType && (null === $logoHeight || null === $logoWidth)) {
            throw new MissingLogoHeightException('SVG Logos require an explicit height set via setLogoSize($width, $height)');
        }
        if (null === $logoHeight || null === $logoWidth) {
            $logoImage = \imagecreatefromstring(\strval($imageData));
            if (!$logoImage) {
                throw new GenerateImageException('Unable to generate image: check your GD installation or logo path');
            }
            /** @var mixed $logoImage */
            $logoSourceWidth = \imagesx($logoImage);
            $logoSourceHeight = \imagesy($logoImage);
            if (\PHP_VERSION_ID < 80000) {
                \imagedestroy($logoImage);
            }
            if (null === $logoWidth) {
                $logoWidth = $logoSourceWidth;
            }
            if (null === $logoHeight) {
                $aspectRatio = $logoWidth / $logoSourceWidth;
                $logoHeight = \intval($logoSourceHeight * $aspectRatio);
            }
        }
        $logoX = $imageWidth / 2 - $logoWidth / 2;
        $logoY = $imageHeight / 2 - $logoHeight / 2;
        $imageDefinition = $svg->addChild('image');
        $imageDefinition->addAttribute('x', \strval($logoX));
        $imageDefinition->addAttribute('y', \strval($logoY));
        $imageDefinition->addAttribute('width', \strval($logoWidth));
        $imageDefinition->addAttribute('height', \strval($logoHeight));
        $imageDefinition->addAttribute('preserveAspectRatio', 'none');
        // xlink:href is actually deprecated, but still required when placing the qr code in a pdf.
        // SimpleXML strips out the xlink part by using addAttribute(), so it must be set directly.
        if ($forceXlinkHref) {
            $imageDefinition['xlink:href'] = 'data:' . $mimeType . ';base64,' . \base64_encode($imageData);
        } else {
            $imageDefinition->addAttribute('href', 'data:' . $mimeType . ';base64,' . \base64_encode($imageData));
        }
    }
    private function getOpacity(int $alpha) : float
    {
        $opacity = 1 - $alpha / 127;
        return $opacity;
    }
    public static function getContentType() : string
    {
        return 'image/svg+xml';
    }
    public static function getSupportedExtensions() : array
    {
        return ['svg'];
    }
    public function getName() : string
    {
        return 'svg';
    }
}
vendor/endroid/qr-code/src/Writer/FpdfWriter.php000064400000010153150755130600015651 0ustar00<?php

declare (strict_types=1);
/*
 * (c) Jeroen van den Enden <info@endroid.nl>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */
namespace WP2FA_Vendor\Endroid\QrCode\Writer;

use WP2FA_Vendor\Endroid\QrCode\Exception\ValidationException;
use WP2FA_Vendor\Endroid\QrCode\QrCodeInterface;
class FpdfWriter extends AbstractWriter
{
    /**
     * Defines as which unit the size is handled. Default is: "mm".
     *
     * Allowed values: 'mm', 'pt', 'cm', 'in'
     */
    public const WRITER_OPTION_MEASURE_UNIT = 'fpdf_measure_unit';
    public function writeString(QrCodeInterface $qrCode) : string
    {
        if (!\class_exists(\WP2FA_Vendor\FPDF::class)) {
            throw new \BadMethodCallException('The Fpdf writer requires FPDF as dependency but the class "\\FPDF" couldn\'t be found.');
        }
        if ($qrCode->getValidateResult()) {
            throw new ValidationException('Built-in validation reader can not check fpdf qr codes: please disable via setValidateResult(false)');
        }
        $foregroundColor = $qrCode->getForegroundColor();
        if (0 !== $foregroundColor['a']) {
            throw new \InvalidArgumentException('The foreground color has an alpha channel, but the fpdf qr writer doesn\'t support alpha channels.');
        }
        $backgroundColor = $qrCode->getBackgroundColor();
        if (0 !== $backgroundColor['a']) {
            throw new \InvalidArgumentException('The foreground color has an alpha channel, but the fpdf qr writer doesn\'t support alpha channels.');
        }
        $label = $qrCode->getLabel();
        $labelHeight = null !== $label ? 30 : 0;
        $data = $qrCode->getData();
        $options = $qrCode->getWriterOptions();
        $fpdf = new \WP2FA_Vendor\FPDF('P', $options[self::WRITER_OPTION_MEASURE_UNIT] ?? 'mm', [$data['outer_width'], $data['outer_height'] + $labelHeight]);
        $fpdf->AddPage();
        $fpdf->SetFillColor($backgroundColor['r'], $backgroundColor['g'], $backgroundColor['b']);
        $fpdf->Rect(0, 0, $data['outer_width'], $data['outer_height'], 'F');
        $fpdf->SetFillColor($foregroundColor['r'], $foregroundColor['g'], $foregroundColor['b']);
        foreach ($data['matrix'] as $row => $values) {
            foreach ($values as $column => $value) {
                if (1 === $value) {
                    $fpdf->Rect($data['margin_left'] + $column * $data['block_size'], $data['margin_left'] + $row * $data['block_size'], $data['block_size'], $data['block_size'], 'F');
                }
            }
        }
        $logoPath = $qrCode->getLogoPath();
        if (null !== $logoPath) {
            $this->addLogo($fpdf, $logoPath, $qrCode->getLogoWidth(), $qrCode->getLogoHeight(), $data['outer_width'], $data['outer_height']);
        }
        if (null !== $label) {
            $fpdf->setY($data['outer_height'] + 5);
            $fpdf->SetFont('Helvetica', null, $qrCode->getLabelFontSize());
            $fpdf->Cell(0, 0, $label, 0, 0, \strtoupper($qrCode->getLabelAlignment()[0]));
        }
        return $fpdf->Output('S');
    }
    protected function addLogo(\WP2FA_Vendor\FPDF $fpdf, string $logoPath, ?int $logoWidth, ?int $logoHeight, int $imageWidth, int $imageHeight) : void
    {
        if (null === $logoHeight || null === $logoWidth) {
            [$logoSourceWidth, $logoSourceHeight] = \getimagesize($logoPath);
            if (null === $logoWidth) {
                $logoWidth = (int) $logoSourceWidth;
            }
            if (null === $logoHeight) {
                $aspectRatio = $logoWidth / $logoSourceWidth;
                $logoHeight = (int) ($logoSourceHeight * $aspectRatio);
            }
        }
        $logoX = $imageWidth / 2 - (int) $logoWidth / 2;
        $logoY = $imageHeight / 2 - (int) $logoHeight / 2;
        $fpdf->Image($logoPath, $logoX, $logoY, $logoWidth, $logoHeight);
    }
    public static function getContentType() : string
    {
        return 'application/pdf';
    }
    public static function getSupportedExtensions() : array
    {
        return ['pdf'];
    }
    public function getName() : string
    {
        return 'fpdf';
    }
}
vendor/endroid/qr-code/src/Writer/DebugWriter.php000064400000003244150755130600016023 0ustar00<?php

declare (strict_types=1);
/*
 * (c) Jeroen van den Enden <info@endroid.nl>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */
namespace WP2FA_Vendor\Endroid\QrCode\Writer;

use WP2FA_Vendor\Endroid\QrCode\QrCodeInterface;
use Exception;
use ReflectionClass;
class DebugWriter extends AbstractWriter
{
    public function writeString(QrCodeInterface $qrCode) : string
    {
        $data = [];
        $skip = ['getData'];
        $reflectionClass = new ReflectionClass($qrCode);
        foreach ($reflectionClass->getMethods() as $method) {
            $methodName = $method->getShortName();
            if (0 === \strpos($methodName, 'get') && 0 == $method->getNumberOfParameters() && !\in_array($methodName, $skip)) {
                $value = $qrCode->{$methodName}();
                if (\is_array($value) && !\is_object(\current($value))) {
                    $value = '[' . \implode(', ', $value) . ']';
                } elseif (\is_bool($value)) {
                    $value = $value ? 'true' : 'false';
                } elseif (\is_string($value)) {
                    $value = '"' . $value . '"';
                } elseif (\is_null($value)) {
                    $value = 'null';
                }
                try {
                    $data[] = $methodName . ': ' . $value;
                } catch (Exception $exception) {
                }
            }
        }
        $string = \implode(" \n", $data);
        return $string;
    }
    public static function getContentType() : string
    {
        return 'text/plain';
    }
    public function getName() : string
    {
        return 'debug';
    }
}
vendor/endroid/qr-code/src/WriterRegistryInterface.php000064400000001275150755130600017154 0ustar00<?php

declare (strict_types=1);
/*
 * (c) Jeroen van den Enden <info@endroid.nl>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */
namespace WP2FA_Vendor\Endroid\QrCode;

use WP2FA_Vendor\Endroid\QrCode\Writer\WriterInterface;
interface WriterRegistryInterface
{
    /** @param WriterInterface[] $writers */
    public function addWriters(iterable $writers) : void;
    public function addWriter(WriterInterface $writer) : void;
    public function getWriter(string $name) : WriterInterface;
    public function getDefaultWriter() : WriterInterface;
    /** @return WriterInterface[] */
    public function getWriters() : array;
}
vendor/endroid/qr-code/src/Exception/InvalidWriterException.php000064400000000464150755130600020725 0ustar00<?php

declare (strict_types=1);
/*
 * (c) Jeroen van den Enden <info@endroid.nl>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */
namespace WP2FA_Vendor\Endroid\QrCode\Exception;

class InvalidWriterException extends QrCodeException
{
}
vendor/endroid/qr-code/src/Exception/InvalidFontException.php000064400000000462150755130600020355 0ustar00<?php

declare (strict_types=1);
/*
 * (c) Jeroen van den Enden <info@endroid.nl>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */
namespace WP2FA_Vendor\Endroid\QrCode\Exception;

class InvalidFontException extends QrCodeException
{
}
vendor/endroid/qr-code/src/Exception/MissingExtensionException.php000064400000000467150755130600021453 0ustar00<?php

declare (strict_types=1);
/*
 * (c) Jeroen van den Enden <info@endroid.nl>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */
namespace WP2FA_Vendor\Endroid\QrCode\Exception;

class MissingExtensionException extends QrCodeException
{
}
vendor/endroid/qr-code/src/Exception/ValidationException.php000064400000000461150755130600020231 0ustar00<?php

declare (strict_types=1);
/*
 * (c) Jeroen van den Enden <info@endroid.nl>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */
namespace WP2FA_Vendor\Endroid\QrCode\Exception;

class ValidationException extends QrCodeException
{
}
vendor/endroid/qr-code/src/Exception/GenerateImageException.php000064400000000464150755130600020637 0ustar00<?php

declare (strict_types=1);
/*
 * (c) Jeroen van den Enden <info@endroid.nl>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */
namespace WP2FA_Vendor\Endroid\QrCode\Exception;

class GenerateImageException extends QrCodeException
{
}
vendor/endroid/qr-code/src/Exception/MissingLogoHeightException.php000064400000000470150755130600021522 0ustar00<?php

declare (strict_types=1);
/*
 * (c) Jeroen van den Enden <info@endroid.nl>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */
namespace WP2FA_Vendor\Endroid\QrCode\Exception;

class MissingLogoHeightException extends QrCodeException
{
}
vendor/endroid/qr-code/src/Exception/InvalidLogoException.php000064400000000462150755130600020347 0ustar00<?php

declare (strict_types=1);
/*
 * (c) Jeroen van den Enden <info@endroid.nl>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */
namespace WP2FA_Vendor\Endroid\QrCode\Exception;

class InvalidLogoException extends QrCodeException
{
}
vendor/endroid/qr-code/src/Exception/QrCodeException.php000064400000000477150755130600017323 0ustar00<?php

declare (strict_types=1);
/*
 * (c) Jeroen van den Enden <info@endroid.nl>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */
namespace WP2FA_Vendor\Endroid\QrCode\Exception;

use Exception;
abstract class QrCodeException extends Exception
{
}
vendor/endroid/qr-code/src/Exception/MissingFunctionException.php000064400000000466150755130600021263 0ustar00<?php

declare (strict_types=1);
/*
 * (c) Jeroen van den Enden <info@endroid.nl>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */
namespace WP2FA_Vendor\Endroid\QrCode\Exception;

class MissingFunctionException extends QrCodeException
{
}
vendor/endroid/qr-code/src/Exception/UnsupportedExtensionException.php000064400000000473150755130600022367 0ustar00<?php

declare (strict_types=1);
/*
 * (c) Jeroen van den Enden <info@endroid.nl>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */
namespace WP2FA_Vendor\Endroid\QrCode\Exception;

class UnsupportedExtensionException extends QrCodeException
{
}
vendor/endroid/qr-code/src/Factory/QrCodeFactory.php000064400000006106150755130600016440 0ustar00<?php

declare (strict_types=1);
/*
 * (c) Jeroen van den Enden <info@endroid.nl>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */
namespace WP2FA_Vendor\Endroid\QrCode\Factory;

use WP2FA_Vendor\Endroid\QrCode\ErrorCorrectionLevel;
use WP2FA_Vendor\Endroid\QrCode\Exception\ValidationException;
use WP2FA_Vendor\Endroid\QrCode\QrCode;
use WP2FA_Vendor\Endroid\QrCode\QrCodeInterface;
use WP2FA_Vendor\Endroid\QrCode\WriterRegistryInterface;
use WP2FA_Vendor\Symfony\Component\OptionsResolver\OptionsResolver;
use WP2FA_Vendor\Symfony\Component\PropertyAccess\PropertyAccess;
class QrCodeFactory implements QrCodeFactoryInterface
{
    private $writerRegistry;
    /** @var OptionsResolver */
    private $optionsResolver;
    /** @var array<string, mixed> */
    private $defaultOptions;
    /** @var array<int, string> */
    private $definedOptions = ['writer', 'writer_options', 'size', 'margin', 'foreground_color', 'background_color', 'encoding', 'round_block_size', 'round_block_size_mode', 'error_correction_level', 'logo_path', 'logo_width', 'logo_height', 'label', 'label_font_size', 'label_font_path', 'label_alignment', 'label_margin', 'validate_result'];
    /** @param array<string, mixed> $defaultOptions */
    public function __construct(array $defaultOptions = [], WriterRegistryInterface $writerRegistry = null)
    {
        $this->defaultOptions = $defaultOptions;
        $this->writerRegistry = $writerRegistry;
    }
    public function create(string $text = '', array $options = []) : QrCodeInterface
    {
        $options = $this->getOptionsResolver()->resolve($options);
        $accessor = PropertyAccess::createPropertyAccessor();
        $qrCode = new QrCode($text);
        if ($this->writerRegistry instanceof WriterRegistryInterface) {
            $qrCode->setWriterRegistry($this->writerRegistry);
        }
        foreach ($this->definedOptions as $option) {
            if (isset($options[$option])) {
                if ('writer' === $option) {
                    $options['writer_by_name'] = $options[$option];
                    $option = 'writer_by_name';
                }
                if ('error_correction_level' === $option) {
                    $options[$option] = new ErrorCorrectionLevel($options[$option]);
                }
                $accessor->setValue($qrCode, $option, $options[$option]);
            }
        }
        if (!$qrCode instanceof QrCodeInterface) {
            throw new ValidationException('QR Code was messed up by property accessor');
        }
        return $qrCode;
    }
    private function getOptionsResolver() : OptionsResolver
    {
        if (!$this->optionsResolver instanceof OptionsResolver) {
            $this->optionsResolver = $this->createOptionsResolver();
        }
        return $this->optionsResolver;
    }
    private function createOptionsResolver() : OptionsResolver
    {
        $optionsResolver = new OptionsResolver();
        $optionsResolver->setDefaults($this->defaultOptions)->setDefined($this->definedOptions);
        return $optionsResolver;
    }
}
vendor/endroid/qr-code/src/Factory/QrCodeFactoryInterface.php000064400000000725150755130600020262 0ustar00<?php

declare (strict_types=1);
/*
 * (c) Jeroen van den Enden <info@endroid.nl>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */
namespace WP2FA_Vendor\Endroid\QrCode\Factory;

use WP2FA_Vendor\Endroid\QrCode\QrCodeInterface;
interface QrCodeFactoryInterface
{
    /** @param array<string, mixed> $options */
    public function create(string $text = '', array $options = []) : QrCodeInterface;
}
vendor/endroid/qr-code/src/QrCode.php000064400000030162150755130600013500 0ustar00<?php

declare (strict_types=1);
/*
 * (c) Jeroen van den Enden <info@endroid.nl>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */
namespace WP2FA_Vendor\Endroid\QrCode;

use WP2FA_Vendor\BaconQrCode\Encoder\Encoder;
use WP2FA_Vendor\Endroid\QrCode\Exception\InvalidFontException;
use WP2FA_Vendor\Endroid\QrCode\Exception\UnsupportedExtensionException;
use WP2FA_Vendor\Endroid\QrCode\Exception\ValidationException;
use WP2FA_Vendor\Endroid\QrCode\Writer\WriterInterface;
class QrCode implements QrCodeInterface
{
    const LABEL_FONT_PATH_DEFAULT = __DIR__ . '/../assets/fonts/noto_sans.otf';
    const ROUND_BLOCK_SIZE_MODE_MARGIN = 'margin';
    const ROUND_BLOCK_SIZE_MODE_SHRINK = 'shrink';
    const ROUND_BLOCK_SIZE_MODE_ENLARGE = 'enlarge';
    private $text;
    /** @var int */
    private $size = 300;
    /** @var int */
    private $margin = 10;
    /** @var array<int> */
    private $foregroundColor = ['r' => 0, 'g' => 0, 'b' => 0, 'a' => 0];
    /** @var array<int> */
    private $backgroundColor = ['r' => 255, 'g' => 255, 'b' => 255, 'a' => 0];
    /** @var string */
    private $encoding = 'UTF-8';
    /** @var bool */
    private $roundBlockSize = \true;
    /** @var string */
    private $roundBlockSizeMode = self::ROUND_BLOCK_SIZE_MODE_MARGIN;
    private $errorCorrectionLevel;
    /** @var string */
    private $logoPath;
    /** @var int|null */
    private $logoWidth;
    /** @var int|null */
    private $logoHeight;
    /** @var string */
    private $label;
    /** @var int */
    private $labelFontSize = 16;
    /** @var string */
    private $labelFontPath = self::LABEL_FONT_PATH_DEFAULT;
    private $labelAlignment;
    /** @var array<string, int> */
    private $labelMargin = ['t' => 0, 'r' => 10, 'b' => 10, 'l' => 10];
    /** @var WriterRegistryInterface */
    private $writerRegistry;
    /** @var WriterInterface|null */
    private $writer;
    /** @var array<mixed> */
    private $writerOptions = [];
    /** @var bool */
    private $validateResult = \false;
    public function __construct(string $text = '')
    {
        $this->text = $text;
        $this->errorCorrectionLevel = ErrorCorrectionLevel::LOW();
        $this->labelAlignment = LabelAlignment::CENTER();
        $this->createWriterRegistry();
    }
    public function setText(string $text) : void
    {
        $this->text = $text;
    }
    public function getText() : string
    {
        return $this->text;
    }
    public function setSize(int $size) : void
    {
        $this->size = $size;
    }
    public function getSize() : int
    {
        return $this->size;
    }
    public function setMargin(int $margin) : void
    {
        $this->margin = $margin;
    }
    public function getMargin() : int
    {
        return $this->margin;
    }
    /** @param array<int> $foregroundColor */
    public function setForegroundColor(array $foregroundColor) : void
    {
        if (!isset($foregroundColor['a'])) {
            $foregroundColor['a'] = 0;
        }
        foreach ($foregroundColor as &$color) {
            $color = \intval($color);
        }
        $this->foregroundColor = $foregroundColor;
    }
    public function getForegroundColor() : array
    {
        return $this->foregroundColor;
    }
    /** @param array<int> $backgroundColor */
    public function setBackgroundColor(array $backgroundColor) : void
    {
        if (!isset($backgroundColor['a'])) {
            $backgroundColor['a'] = 0;
        }
        foreach ($backgroundColor as &$color) {
            $color = \intval($color);
        }
        $this->backgroundColor = $backgroundColor;
    }
    public function getBackgroundColor() : array
    {
        return $this->backgroundColor;
    }
    public function setEncoding(string $encoding) : void
    {
        $this->encoding = $encoding;
    }
    public function getEncoding() : string
    {
        return $this->encoding;
    }
    public function setRoundBlockSize(bool $roundBlockSize, string $roundBlockSizeMode = self::ROUND_BLOCK_SIZE_MODE_MARGIN) : void
    {
        $this->roundBlockSize = $roundBlockSize;
        $this->setRoundBlockSizeMode($roundBlockSizeMode);
    }
    public function getRoundBlockSize() : bool
    {
        return $this->roundBlockSize;
    }
    public function setRoundBlockSizeMode(string $roundBlockSizeMode) : void
    {
        if (!\in_array($roundBlockSizeMode, [self::ROUND_BLOCK_SIZE_MODE_ENLARGE, self::ROUND_BLOCK_SIZE_MODE_MARGIN, self::ROUND_BLOCK_SIZE_MODE_SHRINK])) {
            throw new ValidationException('Invalid round block size mode: ' . $roundBlockSizeMode);
        }
        $this->roundBlockSizeMode = $roundBlockSizeMode;
    }
    public function setErrorCorrectionLevel(ErrorCorrectionLevel $errorCorrectionLevel) : void
    {
        $this->errorCorrectionLevel = $errorCorrectionLevel;
    }
    public function getErrorCorrectionLevel() : ErrorCorrectionLevel
    {
        return $this->errorCorrectionLevel;
    }
    public function setLogoPath(string $logoPath) : void
    {
        $this->logoPath = $logoPath;
    }
    public function getLogoPath() : ?string
    {
        return $this->logoPath;
    }
    public function setLogoSize(int $logoWidth, int $logoHeight = null) : void
    {
        $this->logoWidth = $logoWidth;
        $this->logoHeight = $logoHeight;
    }
    public function setLogoWidth(int $logoWidth) : void
    {
        $this->logoWidth = $logoWidth;
    }
    public function getLogoWidth() : ?int
    {
        return $this->logoWidth;
    }
    public function setLogoHeight(int $logoHeight) : void
    {
        $this->logoHeight = $logoHeight;
    }
    public function getLogoHeight() : ?int
    {
        return $this->logoHeight;
    }
    /** @param array<string, int> $labelMargin */
    public function setLabel(string $label, int $labelFontSize = null, string $labelFontPath = null, string $labelAlignment = null, array $labelMargin = null) : void
    {
        $this->label = $label;
        if (null !== $labelFontSize) {
            $this->setLabelFontSize($labelFontSize);
        }
        if (null !== $labelFontPath) {
            $this->setLabelFontPath($labelFontPath);
        }
        if (null !== $labelAlignment) {
            $this->setLabelAlignment($labelAlignment);
        }
        if (null !== $labelMargin) {
            $this->setLabelMargin($labelMargin);
        }
    }
    public function getLabel() : ?string
    {
        return $this->label;
    }
    public function setLabelFontSize(int $labelFontSize) : void
    {
        $this->labelFontSize = $labelFontSize;
    }
    public function getLabelFontSize() : int
    {
        return $this->labelFontSize;
    }
    public function setLabelFontPath(string $labelFontPath) : void
    {
        $resolvedLabelFontPath = (string) \realpath($labelFontPath);
        if (!\is_file($resolvedLabelFontPath)) {
            throw new InvalidFontException('Invalid label font path: ' . $labelFontPath);
        }
        $this->labelFontPath = $resolvedLabelFontPath;
    }
    public function getLabelFontPath() : string
    {
        return $this->labelFontPath;
    }
    public function setLabelAlignment(string $labelAlignment) : void
    {
        $this->labelAlignment = new LabelAlignment($labelAlignment);
    }
    public function getLabelAlignment() : string
    {
        return $this->labelAlignment->getValue();
    }
    /** @param array<string, int> $labelMargin */
    public function setLabelMargin(array $labelMargin) : void
    {
        $this->labelMargin = \array_merge($this->labelMargin, $labelMargin);
    }
    public function getLabelMargin() : array
    {
        return $this->labelMargin;
    }
    public function setWriterRegistry(WriterRegistryInterface $writerRegistry) : void
    {
        $this->writerRegistry = $writerRegistry;
    }
    public function setWriter(WriterInterface $writer) : void
    {
        $this->writer = $writer;
    }
    public function getWriter(string $name = null) : WriterInterface
    {
        if (!\is_null($name)) {
            return $this->writerRegistry->getWriter($name);
        }
        if ($this->writer instanceof WriterInterface) {
            return $this->writer;
        }
        return $this->writerRegistry->getDefaultWriter();
    }
    /** @param array<string, mixed> $writerOptions */
    public function setWriterOptions(array $writerOptions) : void
    {
        $this->writerOptions = $writerOptions;
    }
    public function getWriterOptions() : array
    {
        return $this->writerOptions;
    }
    private function createWriterRegistry() : void
    {
        $this->writerRegistry = new WriterRegistry();
        $this->writerRegistry->loadDefaultWriters();
    }
    public function setWriterByName(string $name) : void
    {
        $this->writer = $this->getWriter($name);
    }
    public function setWriterByPath(string $path) : void
    {
        $extension = \pathinfo($path, \PATHINFO_EXTENSION);
        $this->setWriterByExtension($extension);
    }
    public function setWriterByExtension(string $extension) : void
    {
        foreach ($this->writerRegistry->getWriters() as $writer) {
            if ($writer->supportsExtension($extension)) {
                $this->writer = $writer;
                return;
            }
        }
        throw new UnsupportedExtensionException('Missing writer for extension "' . $extension . '"');
    }
    public function writeString() : string
    {
        return $this->getWriter()->writeString($this);
    }
    public function writeDataUri() : string
    {
        return $this->getWriter()->writeDataUri($this);
    }
    public function writeFile(string $path) : void
    {
        $this->getWriter()->writeFile($this, $path);
    }
    public function getContentType() : string
    {
        return $this->getWriter()->getContentType();
    }
    public function setValidateResult(bool $validateResult) : void
    {
        $this->validateResult = $validateResult;
    }
    public function getValidateResult() : bool
    {
        return $this->validateResult;
    }
    public function getData() : array
    {
        $baconErrorCorrectionLevel = $this->errorCorrectionLevel->toBaconErrorCorrectionLevel();
        $baconQrCode = Encoder::encode($this->text, $baconErrorCorrectionLevel, $this->encoding);
        $baconMatrix = $baconQrCode->getMatrix();
        $matrix = [];
        $columnCount = $baconMatrix->getWidth();
        $rowCount = $baconMatrix->getHeight();
        for ($rowIndex = 0; $rowIndex < $rowCount; ++$rowIndex) {
            $matrix[$rowIndex] = [];
            for ($columnIndex = 0; $columnIndex < $columnCount; ++$columnIndex) {
                $matrix[$rowIndex][$columnIndex] = $baconMatrix->get($columnIndex, $rowIndex);
            }
        }
        $data = ['matrix' => $matrix];
        $data['block_count'] = \count($matrix[0]);
        $data['block_size'] = $this->size / $data['block_count'];
        if ($this->roundBlockSize) {
            switch ($this->roundBlockSizeMode) {
                case self::ROUND_BLOCK_SIZE_MODE_ENLARGE:
                    $data['block_size'] = \intval(\ceil($data['block_size']));
                    $this->size = $data['block_size'] * $data['block_count'];
                    break;
                case self::ROUND_BLOCK_SIZE_MODE_SHRINK:
                    $data['block_size'] = \intval(\floor($data['block_size']));
                    $this->size = $data['block_size'] * $data['block_count'];
                    break;
                case self::ROUND_BLOCK_SIZE_MODE_MARGIN:
                default:
                    $data['block_size'] = \intval(\floor($data['block_size']));
            }
        }
        $data['inner_width'] = $data['block_size'] * $data['block_count'];
        $data['inner_height'] = $data['block_size'] * $data['block_count'];
        $data['outer_width'] = $this->size + 2 * $this->margin;
        $data['outer_height'] = $this->size + 2 * $this->margin;
        $data['margin_left'] = ($data['outer_width'] - $data['inner_width']) / 2;
        if ($this->roundBlockSize) {
            $data['margin_left'] = \intval(\floor($data['margin_left']));
        }
        $data['margin_right'] = $data['outer_width'] - $data['inner_width'] - $data['margin_left'];
        return $data;
    }
}
vendor/endroid/qr-code/src/WriterRegistry.php000064400000004411150755130600015326 0ustar00<?php

declare (strict_types=1);
/*
 * (c) Jeroen van den Enden <info@endroid.nl>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */
namespace WP2FA_Vendor\Endroid\QrCode;

use WP2FA_Vendor\Endroid\QrCode\Exception\InvalidWriterException;
use WP2FA_Vendor\Endroid\QrCode\Writer\BinaryWriter;
use WP2FA_Vendor\Endroid\QrCode\Writer\DebugWriter;
use WP2FA_Vendor\Endroid\QrCode\Writer\EpsWriter;
use WP2FA_Vendor\Endroid\QrCode\Writer\FpdfWriter;
use WP2FA_Vendor\Endroid\QrCode\Writer\PngWriter;
use WP2FA_Vendor\Endroid\QrCode\Writer\SvgWriter;
use WP2FA_Vendor\Endroid\QrCode\Writer\WriterInterface;
class WriterRegistry implements WriterRegistryInterface
{
    /** @var WriterInterface[] */
    private $writers = [];
    /** @var WriterInterface|null */
    private $defaultWriter;
    public function loadDefaultWriters() : void
    {
        if (\count($this->writers) > 0) {
            return;
        }
        $this->addWriters([new BinaryWriter(), new DebugWriter(), new EpsWriter(), new PngWriter(), new SvgWriter(), new FpdfWriter()]);
        $this->setDefaultWriter('png');
    }
    public function addWriters(iterable $writers) : void
    {
        foreach ($writers as $writer) {
            $this->addWriter($writer);
        }
    }
    public function addWriter(WriterInterface $writer) : void
    {
        $this->writers[$writer->getName()] = $writer;
    }
    public function getWriter(string $name) : WriterInterface
    {
        $this->assertValidWriter($name);
        return $this->writers[$name];
    }
    public function getDefaultWriter() : WriterInterface
    {
        if ($this->defaultWriter instanceof WriterInterface) {
            return $this->defaultWriter;
        }
        throw new InvalidWriterException('Please set the default writer via the second argument of addWriter');
    }
    public function setDefaultWriter(string $name) : void
    {
        $this->defaultWriter = $this->writers[$name];
    }
    public function getWriters() : array
    {
        return $this->writers;
    }
    private function assertValidWriter(string $name) : void
    {
        if (!isset($this->writers[$name])) {
            throw new InvalidWriterException('Invalid writer "' . $name . '"');
        }
    }
}
vendor/endroid/qr-code/src/LabelAlignment.php000064400000001077150755130600015204 0ustar00<?php

declare (strict_types=1);
/*
 * (c) Jeroen van den Enden <info@endroid.nl>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */
namespace WP2FA_Vendor\Endroid\QrCode;

use WP2FA_Vendor\MyCLabs\Enum\Enum;
/**
 * @method static LabelAlignment LEFT()
 * @method static LabelAlignment CENTER()
 * @method static LabelAlignment RIGHT()
 *
 * @extends Enum<string>
 * @psalm-immutable
 */
class LabelAlignment extends Enum
{
    const LEFT = 'left';
    const CENTER = 'center';
    const RIGHT = 'right';
}
vendor/endroid/qr-code/src/ErrorCorrectionLevel.php000064400000002006150755130600016430 0ustar00<?php

declare (strict_types=1);
/*
 * (c) Jeroen van den Enden <info@endroid.nl>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */
namespace WP2FA_Vendor\Endroid\QrCode;

use WP2FA_Vendor\BaconQrCode\Common\ErrorCorrectionLevel as BaconErrorCorrectionLevel;
use WP2FA_Vendor\MyCLabs\Enum\Enum;
/**
 * @method static ErrorCorrectionLevel LOW()
 * @method static ErrorCorrectionLevel MEDIUM()
 * @method static ErrorCorrectionLevel QUARTILE()
 * @method static ErrorCorrectionLevel HIGH()
 *
 * @extends Enum<string>
 * @psalm-immutable
 */
class ErrorCorrectionLevel extends Enum
{
    const LOW = 'low';
    const MEDIUM = 'medium';
    const QUARTILE = 'quartile';
    const HIGH = 'high';
    /**
     * @psalm-suppress ImpureMethodCall
     */
    public function toBaconErrorCorrectionLevel() : BaconErrorCorrectionLevel
    {
        $name = \strtoupper(\substr($this->getValue(), 0, 1));
        return BaconErrorCorrectionLevel::valueOf($name);
    }
}
vendor/endroid/qr-code/src/QrCodeInterface.php000064400000003052150755130600015317 0ustar00<?php

declare (strict_types=1);
/*
 * (c) Jeroen van den Enden <info@endroid.nl>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */
namespace WP2FA_Vendor\Endroid\QrCode;

interface QrCodeInterface
{
    public function getText() : string;
    public function getSize() : int;
    public function getMargin() : int;
    /** @return array<int> */
    public function getForegroundColor() : array;
    /** @return array<int> */
    public function getBackgroundColor() : array;
    public function getEncoding() : string;
    public function getRoundBlockSize() : bool;
    public function getErrorCorrectionLevel() : ErrorCorrectionLevel;
    public function getLogoPath() : ?string;
    public function getLogoWidth() : ?int;
    public function getLogoHeight() : ?int;
    public function getLabel() : ?string;
    public function getLabelFontPath() : string;
    public function getLabelFontSize() : int;
    public function getLabelAlignment() : string;
    /** @return array<int> */
    public function getLabelMargin() : array;
    public function getValidateResult() : bool;
    /** @return array<mixed> */
    public function getWriterOptions() : array;
    public function getContentType() : string;
    public function setWriterRegistry(WriterRegistryInterface $writerRegistry) : void;
    public function writeString() : string;
    public function writeDataUri() : string;
    public function writeFile(string $path) : void;
    /** @return array<mixed> */
    public function getData() : array;
}
vendor/composer/ClassLoader.php000064400000037772150755130600012617 0ustar00<?php

/*
 * This file is part of Composer.
 *
 * (c) Nils Adermann <naderman@naderman.de>
 *     Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Composer\Autoload;

/**
 * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
 *
 *     $loader = new \Composer\Autoload\ClassLoader();
 *
 *     // register classes with namespaces
 *     $loader->add('Symfony\Component', __DIR__.'/component');
 *     $loader->add('Symfony',           __DIR__.'/framework');
 *
 *     // activate the autoloader
 *     $loader->register();
 *
 *     // to enable searching the include path (eg. for PEAR packages)
 *     $loader->setUseIncludePath(true);
 *
 * In this example, if you try to use a class in the Symfony\Component
 * namespace or one of its children (Symfony\Component\Console for instance),
 * the autoloader will first look for the class under the component/
 * directory, and it will then fallback to the framework/ directory if not
 * found before giving up.
 *
 * This class is loosely based on the Symfony UniversalClassLoader.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 * @author Jordi Boggiano <j.boggiano@seld.be>
 * @see    https://www.php-fig.org/psr/psr-0/
 * @see    https://www.php-fig.org/psr/psr-4/
 */
class ClassLoader
{
    /** @var \Closure(string):void */
    private static $includeFile;

    /** @var string|null */
    private $vendorDir;

    // PSR-4
    /**
     * @var array<string, array<string, int>>
     */
    private $prefixLengthsPsr4 = array();
    /**
     * @var array<string, list<string>>
     */
    private $prefixDirsPsr4 = array();
    /**
     * @var list<string>
     */
    private $fallbackDirsPsr4 = array();

    // PSR-0
    /**
     * List of PSR-0 prefixes
     *
     * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
     *
     * @var array<string, array<string, list<string>>>
     */
    private $prefixesPsr0 = array();
    /**
     * @var list<string>
     */
    private $fallbackDirsPsr0 = array();

    /** @var bool */
    private $useIncludePath = false;

    /**
     * @var array<string, string>
     */
    private $classMap = array();

    /** @var bool */
    private $classMapAuthoritative = false;

    /**
     * @var array<string, bool>
     */
    private $missingClasses = array();

    /** @var string|null */
    private $apcuPrefix;

    /**
     * @var array<string, self>
     */
    private static $registeredLoaders = array();

    /**
     * @param string|null $vendorDir
     */
    public function __construct($vendorDir = null)
    {
        $this->vendorDir = $vendorDir;
        self::initializeIncludeClosure();
    }

    /**
     * @return array<string, list<string>>
     */
    public function getPrefixes()
    {
        if (!empty($this->prefixesPsr0)) {
            return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
        }

        return array();
    }

    /**
     * @return array<string, list<string>>
     */
    public function getPrefixesPsr4()
    {
        return $this->prefixDirsPsr4;
    }

    /**
     * @return list<string>
     */
    public function getFallbackDirs()
    {
        return $this->fallbackDirsPsr0;
    }

    /**
     * @return list<string>
     */
    public function getFallbackDirsPsr4()
    {
        return $this->fallbackDirsPsr4;
    }

    /**
     * @return array<string, string> Array of classname => path
     */
    public function getClassMap()
    {
        return $this->classMap;
    }

    /**
     * @param array<string, string> $classMap Class to filename map
     *
     * @return void
     */
    public function addClassMap(array $classMap)
    {
        if ($this->classMap) {
            $this->classMap = array_merge($this->classMap, $classMap);
        } else {
            $this->classMap = $classMap;
        }
    }

    /**
     * Registers a set of PSR-0 directories for a given prefix, either
     * appending or prepending to the ones previously set for this prefix.
     *
     * @param string              $prefix  The prefix
     * @param list<string>|string $paths   The PSR-0 root directories
     * @param bool                $prepend Whether to prepend the directories
     *
     * @return void
     */
    public function add($prefix, $paths, $prepend = false)
    {
        $paths = (array) $paths;
        if (!$prefix) {
            if ($prepend) {
                $this->fallbackDirsPsr0 = array_merge(
                    $paths,
                    $this->fallbackDirsPsr0
                );
            } else {
                $this->fallbackDirsPsr0 = array_merge(
                    $this->fallbackDirsPsr0,
                    $paths
                );
            }

            return;
        }

        $first = $prefix[0];
        if (!isset($this->prefixesPsr0[$first][$prefix])) {
            $this->prefixesPsr0[$first][$prefix] = $paths;

            return;
        }
        if ($prepend) {
            $this->prefixesPsr0[$first][$prefix] = array_merge(
                $paths,
                $this->prefixesPsr0[$first][$prefix]
            );
        } else {
            $this->prefixesPsr0[$first][$prefix] = array_merge(
                $this->prefixesPsr0[$first][$prefix],
                $paths
            );
        }
    }

    /**
     * Registers a set of PSR-4 directories for a given namespace, either
     * appending or prepending to the ones previously set for this namespace.
     *
     * @param string              $prefix  The prefix/namespace, with trailing '\\'
     * @param list<string>|string $paths   The PSR-4 base directories
     * @param bool                $prepend Whether to prepend the directories
     *
     * @throws \InvalidArgumentException
     *
     * @return void
     */
    public function addPsr4($prefix, $paths, $prepend = false)
    {
        $paths = (array) $paths;
        if (!$prefix) {
            // Register directories for the root namespace.
            if ($prepend) {
                $this->fallbackDirsPsr4 = array_merge(
                    $paths,
                    $this->fallbackDirsPsr4
                );
            } else {
                $this->fallbackDirsPsr4 = array_merge(
                    $this->fallbackDirsPsr4,
                    $paths
                );
            }
        } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
            // Register directories for a new namespace.
            $length = strlen($prefix);
            if ('\\' !== $prefix[$length - 1]) {
                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
            }
            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
            $this->prefixDirsPsr4[$prefix] = $paths;
        } elseif ($prepend) {
            // Prepend directories for an already registered namespace.
            $this->prefixDirsPsr4[$prefix] = array_merge(
                $paths,
                $this->prefixDirsPsr4[$prefix]
            );
        } else {
            // Append directories for an already registered namespace.
            $this->prefixDirsPsr4[$prefix] = array_merge(
                $this->prefixDirsPsr4[$prefix],
                $paths
            );
        }
    }

    /**
     * Registers a set of PSR-0 directories for a given prefix,
     * replacing any others previously set for this prefix.
     *
     * @param string              $prefix The prefix
     * @param list<string>|string $paths  The PSR-0 base directories
     *
     * @return void
     */
    public function set($prefix, $paths)
    {
        if (!$prefix) {
            $this->fallbackDirsPsr0 = (array) $paths;
        } else {
            $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
        }
    }

    /**
     * Registers a set of PSR-4 directories for a given namespace,
     * replacing any others previously set for this namespace.
     *
     * @param string              $prefix The prefix/namespace, with trailing '\\'
     * @param list<string>|string $paths  The PSR-4 base directories
     *
     * @throws \InvalidArgumentException
     *
     * @return void
     */
    public function setPsr4($prefix, $paths)
    {
        if (!$prefix) {
            $this->fallbackDirsPsr4 = (array) $paths;
        } else {
            $length = strlen($prefix);
            if ('\\' !== $prefix[$length - 1]) {
                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
            }
            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
            $this->prefixDirsPsr4[$prefix] = (array) $paths;
        }
    }

    /**
     * Turns on searching the include path for class files.
     *
     * @param bool $useIncludePath
     *
     * @return void
     */
    public function setUseIncludePath($useIncludePath)
    {
        $this->useIncludePath = $useIncludePath;
    }

    /**
     * Can be used to check if the autoloader uses the include path to check
     * for classes.
     *
     * @return bool
     */
    public function getUseIncludePath()
    {
        return $this->useIncludePath;
    }

    /**
     * Turns off searching the prefix and fallback directories for classes
     * that have not been registered with the class map.
     *
     * @param bool $classMapAuthoritative
     *
     * @return void
     */
    public function setClassMapAuthoritative($classMapAuthoritative)
    {
        $this->classMapAuthoritative = $classMapAuthoritative;
    }

    /**
     * Should class lookup fail if not found in the current class map?
     *
     * @return bool
     */
    public function isClassMapAuthoritative()
    {
        return $this->classMapAuthoritative;
    }

    /**
     * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
     *
     * @param string|null $apcuPrefix
     *
     * @return void
     */
    public function setApcuPrefix($apcuPrefix)
    {
        $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
    }

    /**
     * The APCu prefix in use, or null if APCu caching is not enabled.
     *
     * @return string|null
     */
    public function getApcuPrefix()
    {
        return $this->apcuPrefix;
    }

    /**
     * Registers this instance as an autoloader.
     *
     * @param bool $prepend Whether to prepend the autoloader or not
     *
     * @return void
     */
    public function register($prepend = false)
    {
        spl_autoload_register(array($this, 'loadClass'), true, $prepend);

        if (null === $this->vendorDir) {
            return;
        }

        if ($prepend) {
            self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
        } else {
            unset(self::$registeredLoaders[$this->vendorDir]);
            self::$registeredLoaders[$this->vendorDir] = $this;
        }
    }

    /**
     * Unregisters this instance as an autoloader.
     *
     * @return void
     */
    public function unregister()
    {
        spl_autoload_unregister(array($this, 'loadClass'));

        if (null !== $this->vendorDir) {
            unset(self::$registeredLoaders[$this->vendorDir]);
        }
    }

    /**
     * Loads the given class or interface.
     *
     * @param  string    $class The name of the class
     * @return true|null True if loaded, null otherwise
     */
    public function loadClass($class)
    {
        if ($file = $this->findFile($class)) {
            $includeFile = self::$includeFile;
            $includeFile($file);

            return true;
        }

        return null;
    }

    /**
     * Finds the path to the file where the class is defined.
     *
     * @param string $class The name of the class
     *
     * @return string|false The path if found, false otherwise
     */
    public function findFile($class)
    {
        // class map lookup
        if (isset($this->classMap[$class])) {
            return $this->classMap[$class];
        }
        if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
            return false;
        }
        if (null !== $this->apcuPrefix) {
            $file = apcu_fetch($this->apcuPrefix.$class, $hit);
            if ($hit) {
                return $file;
            }
        }

        $file = $this->findFileWithExtension($class, '.php');

        // Search for Hack files if we are running on HHVM
        if (false === $file && defined('HHVM_VERSION')) {
            $file = $this->findFileWithExtension($class, '.hh');
        }

        if (null !== $this->apcuPrefix) {
            apcu_add($this->apcuPrefix.$class, $file);
        }

        if (false === $file) {
            // Remember that this class does not exist.
            $this->missingClasses[$class] = true;
        }

        return $file;
    }

    /**
     * Returns the currently registered loaders keyed by their corresponding vendor directories.
     *
     * @return array<string, self>
     */
    public static function getRegisteredLoaders()
    {
        return self::$registeredLoaders;
    }

    /**
     * @param  string       $class
     * @param  string       $ext
     * @return string|false
     */
    private function findFileWithExtension($class, $ext)
    {
        // PSR-4 lookup
        $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;

        $first = $class[0];
        if (isset($this->prefixLengthsPsr4[$first])) {
            $subPath = $class;
            while (false !== $lastPos = strrpos($subPath, '\\')) {
                $subPath = substr($subPath, 0, $lastPos);
                $search = $subPath . '\\';
                if (isset($this->prefixDirsPsr4[$search])) {
                    $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
                    foreach ($this->prefixDirsPsr4[$search] as $dir) {
                        if (file_exists($file = $dir . $pathEnd)) {
                            return $file;
                        }
                    }
                }
            }
        }

        // PSR-4 fallback dirs
        foreach ($this->fallbackDirsPsr4 as $dir) {
            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
                return $file;
            }
        }

        // PSR-0 lookup
        if (false !== $pos = strrpos($class, '\\')) {
            // namespaced class name
            $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
                . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
        } else {
            // PEAR-like class name
            $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
        }

        if (isset($this->prefixesPsr0[$first])) {
            foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
                if (0 === strpos($class, $prefix)) {
                    foreach ($dirs as $dir) {
                        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
                            return $file;
                        }
                    }
                }
            }
        }

        // PSR-0 fallback dirs
        foreach ($this->fallbackDirsPsr0 as $dir) {
            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
                return $file;
            }
        }

        // PSR-0 include paths.
        if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
            return $file;
        }

        return false;
    }

    /**
     * @return void
     */
    private static function initializeIncludeClosure()
    {
        if (self::$includeFile !== null) {
            return;
        }

        /**
         * Scope isolated include.
         *
         * Prevents access to $this/self from included files.
         *
         * @param  string $file
         * @return void
         */
        self::$includeFile = \Closure::bind(static function($file) {
            include $file;
        }, null, null);
    }
}
vendor/composer/autoload_psr4.php000064400000002227150755130600013166 0ustar00<?php

// autoload_psr4.php @generated by Composer

$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);

return array(
    'WP2FA_Vendor\\Zxing\\' => array($vendorDir . '/khanamiryan/qrcode-detector-decoder/lib'),
    'WP2FA_Vendor\\Twilio\\' => array($vendorDir . '/twilio/sdk/src/Twilio'),
    'WP2FA_Vendor\\Symfony\\Component\\PropertyAccess\\' => array($vendorDir . '/symfony/property-access'),
    'WP2FA_Vendor\\Symfony\\Component\\Inflector\\' => array($vendorDir . '/symfony/inflector'),
    'WP2FA_Vendor\\MyCLabs\\Enum\\' => array($vendorDir . '/myclabs/php-enum/src'),
    'WP2FA_Vendor\\Firebase\\JWT\\' => array($vendorDir . '/firebase/php-jwt/src'),
    'WP2FA_Vendor\\Endroid\\QrCode\\' => array($vendorDir . '/endroid/qr-code/src'),
    'WP2FA_Vendor\\DASPRiD\\Enum\\' => array($vendorDir . '/dasprid/enum/src'),
    'WP2FA_Vendor\\Clickatell\\' => array($vendorDir . '/arcturial/clickatell/src', $vendorDir . '/arcturial/clickatell/test'),
    'WP2FA_Vendor\\BaconQrCode\\' => array($vendorDir . '/bacon/bacon-qr-code/src'),
    'WP2FA\\Extensions\\' => array($baseDir . '/extensions'),
    'WP2FA\\' => array($baseDir . '/includes/classes'),
);
vendor/composer/LICENSE000064400000002056150755130600010702 0ustar00
Copyright (c) Nils Adermann, Jordi Boggiano

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

vendor/composer/autoload_real.php000064400000003001150755130600013210 0ustar00<?php

// autoload_real.php @generated by Composer

class ComposerAutoloaderInit29692
{
    private static $loader;

    public static function loadClassLoader($class)
    {
        if ('Composer\Autoload\ClassLoader' === $class) {
            require __DIR__ . '/ClassLoader.php';
        }
    }

    /**
     * @return \Composer\Autoload\ClassLoader
     */
    public static function getLoader()
    {
        if (null !== self::$loader) {
            return self::$loader;
        }

        require __DIR__ . '/platform_check.php';

        spl_autoload_register(array('ComposerAutoloaderInit29692', 'loadClassLoader'), true, true);
        self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
        spl_autoload_unregister(array('ComposerAutoloaderInit29692', 'loadClassLoader'));

        require __DIR__ . '/autoload_static.php';
        call_user_func(\Composer\Autoload\ComposerStaticInit29692::getInitializer($loader));

        $loader->register(true);

        $filesToLoad = \Composer\Autoload\ComposerStaticInit29692::$files;
        $requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
            if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
                $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;

                require $file;
            }
        }, null, null);
        foreach ($filesToLoad as $fileIdentifier => $file) {
            $requireFile($fileIdentifier, $file);
        }

        return $loader;
    }
}
vendor/composer/installed.json000064400000063131150755130600012550 0ustar00{
    "packages": [
        {
            "name": "arcturial\/clickatell",
            "version": "3.0.0",
            "version_normalized": "3.0.0.0",
            "source": {
                "type": "git",
                "url": "https:\/\/github.com\/arcturial\/clickatell.git",
                "reference": "541ab55c3a807374fe727aad57004c09887080cf"
            },
            "dist": {
                "type": "zip",
                "url": "https:\/\/api.github.com\/repos\/arcturial\/clickatell\/zipball\/541ab55c3a807374fe727aad57004c09887080cf",
                "reference": "541ab55c3a807374fe727aad57004c09887080cf",
                "shasum": ""
            },
            "require": {
                "php": ">=5.3.3"
            },
            "require-dev": {
                "phpunit\/phpunit": "4.3.*"
            },
            "time": "2017-03-10T09:48:13+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "WP2FA_Vendor\\Clickatell\\": [
                        "src",
                        "test"
                    ]
                }
            },
            "notification-url": "https:\/\/packagist.org\/downloads\/",
            "license": [
                "GNU General Public License"
            ],
            "authors": [
                {
                    "name": "Chris Brand"
                }
            ],
            "description": "Standalone PHP library to integrate with the Clickatell SMS gateway",
            "homepage": "https:\/\/arcturial.github.com",
            "support": {
                "issues": "https:\/\/github.com\/arcturial\/clickatell\/issues",
                "source": "https:\/\/github.com\/arcturial\/clickatell\/tree\/3.0.0"
            },
            "install-path": "..\/arcturial\/clickatell"
        },
        {
            "name": "bacon\/bacon-qr-code",
            "version": "2.0.8",
            "version_normalized": "2.0.8.0",
            "source": {
                "type": "git",
                "url": "https:\/\/github.com\/Bacon\/BaconQrCode.git",
                "reference": "8674e51bb65af933a5ffaf1c308a660387c35c22"
            },
            "dist": {
                "type": "zip",
                "url": "https:\/\/api.github.com\/repos\/Bacon\/BaconQrCode\/zipball\/8674e51bb65af933a5ffaf1c308a660387c35c22",
                "reference": "8674e51bb65af933a5ffaf1c308a660387c35c22",
                "shasum": ""
            },
            "require": {
                "dasprid\/enum": "^1.0.3",
                "ext-iconv": "*",
                "php": "^7.1 || ^8.0"
            },
            "require-dev": {
                "phly\/keep-a-changelog": "^2.1",
                "phpunit\/phpunit": "^7 | ^8 | ^9",
                "spatie\/phpunit-snapshot-assertions": "^4.2.9",
                "squizlabs\/php_codesniffer": "^3.4"
            },
            "suggest": {
                "ext-imagick": "to generate QR code images"
            },
            "time": "2022-12-07T17:46:57+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "WP2FA_Vendor\\BaconQrCode\\": "src\/"
                }
            },
            "notification-url": "https:\/\/packagist.org\/downloads\/",
            "license": [
                "BSD-2-Clause"
            ],
            "authors": [
                {
                    "name": "Ben Scholzen 'DASPRiD'",
                    "email": "mail@dasprids.de",
                    "homepage": "https:\/\/dasprids.de\/",
                    "role": "Developer"
                }
            ],
            "description": "BaconQrCode is a QR code generator for PHP.",
            "homepage": "https:\/\/github.com\/Bacon\/BaconQrCode",
            "support": {
                "issues": "https:\/\/github.com\/Bacon\/BaconQrCode\/issues",
                "source": "https:\/\/github.com\/Bacon\/BaconQrCode\/tree\/2.0.8"
            },
            "install-path": "..\/bacon\/bacon-qr-code"
        },
        {
            "name": "dasprid\/enum",
            "version": "1.0.5",
            "version_normalized": "1.0.5.0",
            "source": {
                "type": "git",
                "url": "https:\/\/github.com\/DASPRiD\/Enum.git",
                "reference": "6faf451159fb8ba4126b925ed2d78acfce0dc016"
            },
            "dist": {
                "type": "zip",
                "url": "https:\/\/api.github.com\/repos\/DASPRiD\/Enum\/zipball\/6faf451159fb8ba4126b925ed2d78acfce0dc016",
                "reference": "6faf451159fb8ba4126b925ed2d78acfce0dc016",
                "shasum": ""
            },
            "require": {
                "php": ">=7.1 <9.0"
            },
            "require-dev": {
                "phpunit\/phpunit": "^7 | ^8 | ^9",
                "squizlabs\/php_codesniffer": "*"
            },
            "time": "2023-08-25T16:18:39+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "WP2FA_Vendor\\DASPRiD\\Enum\\": "src\/"
                }
            },
            "notification-url": "https:\/\/packagist.org\/downloads\/",
            "license": [
                "BSD-2-Clause"
            ],
            "authors": [
                {
                    "name": "Ben Scholzen 'DASPRiD'",
                    "email": "mail@dasprids.de",
                    "homepage": "https:\/\/dasprids.de\/",
                    "role": "Developer"
                }
            ],
            "description": "PHP 7.1 enum implementation",
            "keywords": [
                "enum",
                "map"
            ],
            "support": {
                "issues": "https:\/\/github.com\/DASPRiD\/Enum\/issues",
                "source": "https:\/\/github.com\/DASPRiD\/Enum\/tree\/1.0.5"
            },
            "install-path": "..\/dasprid\/enum"
        },
        {
            "name": "endroid\/qr-code",
            "version": "3.9.7",
            "version_normalized": "3.9.7.0",
            "source": {
                "type": "git",
                "url": "https:\/\/github.com\/endroid\/qr-code.git",
                "reference": "94563d7b3105288e6ac53a67ae720e3669fac1f6"
            },
            "dist": {
                "type": "zip",
                "url": "https:\/\/api.github.com\/repos\/endroid\/qr-code\/zipball\/94563d7b3105288e6ac53a67ae720e3669fac1f6",
                "reference": "94563d7b3105288e6ac53a67ae720e3669fac1f6",
                "shasum": ""
            },
            "require": {
                "bacon\/bacon-qr-code": "^2.0",
                "khanamiryan\/qrcode-detector-decoder": "^1.0.5",
                "myclabs\/php-enum": "^1.5",
                "php": "^7.3||^8.0",
                "symfony\/options-resolver": "^3.4||^4.4||^5.0",
                "symfony\/property-access": "^3.4||^4.4||^5.0"
            },
            "require-dev": {
                "endroid\/quality": "^1.5.2",
                "setasign\/fpdf": "^1.8"
            },
            "suggest": {
                "ext-gd": "Required for generating PNG images",
                "roave\/security-advisories": "Avoids installation of package versions with vulnerabilities",
                "setasign\/fpdf": "Required to use the FPDF writer.",
                "symfony\/security-checker": "Checks your composer.lock for vulnerabilities"
            },
            "time": "2021-04-20T19:10:54+00:00",
            "type": "library",
            "extra": {
                "branch-alias": {
                    "dev-master": "3.x-dev"
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "WP2FA_Vendor\\Endroid\\QrCode\\": "src\/"
                }
            },
            "notification-url": "https:\/\/packagist.org\/downloads\/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Jeroen van den Enden",
                    "email": "info@endroid.nl"
                }
            ],
            "description": "Endroid QR Code",
            "homepage": "https:\/\/github.com\/endroid\/qr-code",
            "keywords": [
                "bundle",
                "code",
                "endroid",
                "php",
                "qr",
                "qrcode"
            ],
            "support": {
                "issues": "https:\/\/github.com\/endroid\/qr-code\/issues",
                "source": "https:\/\/github.com\/endroid\/qr-code\/tree\/3.9.7"
            },
            "funding": [
                {
                    "url": "https:\/\/github.com\/endroid",
                    "type": "github"
                }
            ],
            "install-path": "..\/endroid\/qr-code"
        },
        {
            "name": "firebase\/php-jwt",
            "version": "v5.5.1",
            "version_normalized": "5.5.1.0",
            "source": {
                "type": "git",
                "url": "https:\/\/github.com\/firebase\/php-jwt.git",
                "reference": "83b609028194aa042ea33b5af2d41a7427de80e6"
            },
            "dist": {
                "type": "zip",
                "url": "https:\/\/api.github.com\/repos\/firebase\/php-jwt\/zipball\/83b609028194aa042ea33b5af2d41a7427de80e6",
                "reference": "83b609028194aa042ea33b5af2d41a7427de80e6",
                "shasum": ""
            },
            "require": {
                "php": ">=5.3.0"
            },
            "require-dev": {
                "phpunit\/phpunit": ">=4.8 <=9"
            },
            "suggest": {
                "paragonie\/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present"
            },
            "time": "2021-11-08T20:18:51+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "WP2FA_Vendor\\Firebase\\JWT\\": "src"
                }
            },
            "notification-url": "https:\/\/packagist.org\/downloads\/",
            "license": [
                "BSD-3-Clause"
            ],
            "authors": [
                {
                    "name": "Neuman Vong",
                    "email": "neuman+pear@twilio.com",
                    "role": "Developer"
                },
                {
                    "name": "Anant Narayanan",
                    "email": "anant@php.net",
                    "role": "Developer"
                }
            ],
            "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.",
            "homepage": "https:\/\/github.com\/firebase\/php-jwt",
            "keywords": [
                "jwt",
                "php"
            ],
            "support": {
                "issues": "https:\/\/github.com\/firebase\/php-jwt\/issues",
                "source": "https:\/\/github.com\/firebase\/php-jwt\/tree\/v5.5.1"
            },
            "install-path": "..\/firebase\/php-jwt"
        },
        {
            "name": "freemius\/wordpress-sdk",
            "version": "2.7.2",
            "version_normalized": "2.7.2.0",
            "source": {
                "type": "git",
                "url": "https:\/\/github.com\/Freemius\/wordpress-sdk.git",
                "reference": "eeac5f905746822207729ed0d944c4434ee165ff"
            },
            "dist": {
                "type": "zip",
                "url": "https:\/\/api.github.com\/repos\/Freemius\/wordpress-sdk\/zipball\/eeac5f905746822207729ed0d944c4434ee165ff",
                "reference": "eeac5f905746822207729ed0d944c4434ee165ff",
                "shasum": ""
            },
            "require": {
                "php": ">=5.6"
            },
            "require-dev": {
                "dealerdirect\/phpcodesniffer-composer-installer": "^1.0",
                "phpcompatibility\/php-compatibility": "^9.3",
                "phpcompatibility\/phpcompatibility-wp": "^2.1",
                "phpstan\/extension-installer": "^1.3",
                "squizlabs\/php_codesniffer": "^3.7",
                "szepeviktor\/phpstan-wordpress": "^1.3",
                "wp-coding-standards\/wpcs": "^2.3"
            },
            "time": "2024-04-24T10:16:16+00:00",
            "type": "library",
            "installation-source": "dist",
            "notification-url": "https:\/\/packagist.org\/downloads\/",
            "license": [
                "GPL-3.0-only"
            ],
            "description": "Freemius WordPress SDK",
            "homepage": "https:\/\/freemius.com",
            "keywords": [
                "freemius",
                "plugin",
                "sdk",
                "theme",
                "wordpress",
                "wordpress-plugin",
                "wordpress-theme"
            ],
            "support": {
                "issues": "https:\/\/github.com\/Freemius\/wordpress-sdk\/issues",
                "source": "https:\/\/github.com\/Freemius\/wordpress-sdk\/tree\/2.7.2"
            },
            "install-path": "..\/freemius\/wordpress-sdk"
        },
        {
            "name": "khanamiryan\/qrcode-detector-decoder",
            "version": "1.0.6",
            "version_normalized": "1.0.6.0",
            "source": {
                "type": "git",
                "url": "https:\/\/github.com\/khanamiryan\/php-qrcode-detector-decoder.git",
                "reference": "45326fb83a2a375065dbb3a134b5b8a5872da569"
            },
            "dist": {
                "type": "zip",
                "url": "https:\/\/api.github.com\/repos\/khanamiryan\/php-qrcode-detector-decoder\/zipball\/45326fb83a2a375065dbb3a134b5b8a5872da569",
                "reference": "45326fb83a2a375065dbb3a134b5b8a5872da569",
                "shasum": ""
            },
            "require": {
                "php": ">=5.6"
            },
            "require-dev": {
                "phpunit\/phpunit": "^5.7 | ^7.5 | ^8.0 | ^9.0",
                "rector\/rector": "^0.13.6",
                "symplify\/easy-coding-standard": "^11.0"
            },
            "time": "2022-06-29T09:25:13+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "files": [
                    "lib\/Common\/customFunctions.php"
                ],
                "psr-4": {
                    "WP2FA_Vendor\\Zxing\\": "lib\/"
                }
            },
            "notification-url": "https:\/\/packagist.org\/downloads\/",
            "license": [
                "MIT",
                "Apache-2.0"
            ],
            "authors": [
                {
                    "name": "Ashot Khanamiryan",
                    "email": "a.khanamiryan@gmail.com",
                    "homepage": "https:\/\/github.com\/khanamiryan",
                    "role": "Developer"
                }
            ],
            "description": "QR code decoder \/ reader",
            "homepage": "https:\/\/github.com\/khanamiryan\/php-qrcode-detector-decoder\/",
            "keywords": [
                "barcode",
                "qr",
                "zxing"
            ],
            "support": {
                "issues": "https:\/\/github.com\/khanamiryan\/php-qrcode-detector-decoder\/issues",
                "source": "https:\/\/github.com\/khanamiryan\/php-qrcode-detector-decoder\/tree\/1.0.6"
            },
            "install-path": "..\/khanamiryan\/qrcode-detector-decoder"
        },
        {
            "name": "myclabs\/php-enum",
            "version": "1.8.4",
            "version_normalized": "1.8.4.0",
            "source": {
                "type": "git",
                "url": "https:\/\/github.com\/myclabs\/php-enum.git",
                "reference": "a867478eae49c9f59ece437ae7f9506bfaa27483"
            },
            "dist": {
                "type": "zip",
                "url": "https:\/\/api.github.com\/repos\/myclabs\/php-enum\/zipball\/a867478eae49c9f59ece437ae7f9506bfaa27483",
                "reference": "a867478eae49c9f59ece437ae7f9506bfaa27483",
                "shasum": ""
            },
            "require": {
                "ext-json": "*",
                "php": "^7.3 || ^8.0"
            },
            "require-dev": {
                "phpunit\/phpunit": "^9.5",
                "squizlabs\/php_codesniffer": "1.*",
                "vimeo\/psalm": "^4.6.2"
            },
            "time": "2022-08-04T09:53:51+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "WP2FA_Vendor\\MyCLabs\\Enum\\": "src\/"
                },
                "classmap": [
                    "stubs\/Stringable.php"
                ]
            },
            "notification-url": "https:\/\/packagist.org\/downloads\/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "PHP Enum contributors",
                    "homepage": "https:\/\/github.com\/myclabs\/php-enum\/graphs\/contributors"
                }
            ],
            "description": "PHP Enum implementation",
            "homepage": "http:\/\/github.com\/myclabs\/php-enum",
            "keywords": [
                "enum"
            ],
            "support": {
                "issues": "https:\/\/github.com\/myclabs\/php-enum\/issues",
                "source": "https:\/\/github.com\/myclabs\/php-enum\/tree\/1.8.4"
            },
            "funding": [
                {
                    "url": "https:\/\/github.com\/mnapoli",
                    "type": "github"
                },
                {
                    "url": "https:\/\/tidelift.com\/funding\/github\/packagist\/myclabs\/php-enum",
                    "type": "tidelift"
                }
            ],
            "install-path": "..\/myclabs\/php-enum"
        },
        {
            "name": "symfony\/inflector",
            "version": "v5.0.11",
            "version_normalized": "5.0.11.0",
            "source": {
                "type": "git",
                "url": "https:\/\/github.com\/symfony\/inflector.git",
                "reference": "7eff2643934179cd0e5a6609a583fc22fc495fc4"
            },
            "dist": {
                "type": "zip",
                "url": "https:\/\/api.github.com\/repos\/symfony\/inflector\/zipball\/7eff2643934179cd0e5a6609a583fc22fc495fc4",
                "reference": "7eff2643934179cd0e5a6609a583fc22fc495fc4",
                "shasum": ""
            },
            "require": {
                "php": ">=7.2.5",
                "symfony\/polyfill-ctype": "~1.8"
            },
            "time": "2020-05-20T17:38:26+00:00",
            "type": "library",
            "extra": {
                "branch-alias": {
                    "dev-master": "5.0-dev"
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "WP2FA_Vendor\\Symfony\\Component\\Inflector\\": ""
                },
                "exclude-from-classmap": [
                    "\/Tests\/"
                ]
            },
            "notification-url": "https:\/\/packagist.org\/downloads\/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Bernhard Schussek",
                    "email": "bschussek@gmail.com"
                },
                {
                    "name": "Symfony Community",
                    "homepage": "https:\/\/symfony.com\/contributors"
                }
            ],
            "description": "Symfony Inflector Component",
            "homepage": "https:\/\/symfony.com",
            "keywords": [
                "inflection",
                "pluralize",
                "singularize",
                "string",
                "symfony",
                "words"
            ],
            "support": {
                "source": "https:\/\/github.com\/symfony\/inflector\/tree\/v5.0.9"
            },
            "funding": [
                {
                    "url": "https:\/\/symfony.com\/sponsor",
                    "type": "custom"
                },
                {
                    "url": "https:\/\/github.com\/fabpot",
                    "type": "github"
                },
                {
                    "url": "https:\/\/tidelift.com\/funding\/github\/packagist\/symfony\/symfony",
                    "type": "tidelift"
                }
            ],
            "abandoned": "EnglishInflector from the String component",
            "install-path": "..\/symfony\/inflector"
        },
        {
            "name": "symfony\/property-access",
            "version": "v5.0.11",
            "version_normalized": "5.0.11.0",
            "source": {
                "type": "git",
                "url": "https:\/\/github.com\/symfony\/property-access.git",
                "reference": "fdc47c3780ebb29077c3421c6253ccc91040c24a"
            },
            "dist": {
                "type": "zip",
                "url": "https:\/\/api.github.com\/repos\/symfony\/property-access\/zipball\/fdc47c3780ebb29077c3421c6253ccc91040c24a",
                "reference": "fdc47c3780ebb29077c3421c6253ccc91040c24a",
                "shasum": ""
            },
            "require": {
                "php": ">=7.2.5",
                "symfony\/inflector": "^4.4|^5.0"
            },
            "require-dev": {
                "symfony\/cache": "^4.4|^5.0"
            },
            "suggest": {
                "psr\/cache-implementation": "To cache access methods."
            },
            "time": "2020-06-18T18:18:56+00:00",
            "type": "library",
            "extra": {
                "branch-alias": {
                    "dev-master": "5.0-dev"
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "WP2FA_Vendor\\Symfony\\Component\\PropertyAccess\\": ""
                },
                "exclude-from-classmap": [
                    "\/Tests\/"
                ]
            },
            "notification-url": "https:\/\/packagist.org\/downloads\/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Fabien Potencier",
                    "email": "fabien@symfony.com"
                },
                {
                    "name": "Symfony Community",
                    "homepage": "https:\/\/symfony.com\/contributors"
                }
            ],
            "description": "Symfony PropertyAccess Component",
            "homepage": "https:\/\/symfony.com",
            "keywords": [
                "access",
                "array",
                "extraction",
                "index",
                "injection",
                "object",
                "property",
                "property path",
                "reflection"
            ],
            "support": {
                "source": "https:\/\/github.com\/symfony\/property-access\/tree\/v5.0.11"
            },
            "funding": [
                {
                    "url": "https:\/\/symfony.com\/sponsor",
                    "type": "custom"
                },
                {
                    "url": "https:\/\/github.com\/fabpot",
                    "type": "github"
                },
                {
                    "url": "https:\/\/tidelift.com\/funding\/github\/packagist\/symfony\/symfony",
                    "type": "tidelift"
                }
            ],
            "install-path": "..\/symfony\/property-access"
        },
        {
            "name": "twilio\/sdk",
            "version": "6.44.4",
            "version_normalized": "6.44.4.0",
            "source": {
                "type": "git",
                "url": "https:\/\/github.com\/twilio\/twilio-php.git",
                "reference": "08aad5f377e2245b9cd7508e7762d95e7392fa4d"
            },
            "dist": {
                "type": "zip",
                "url": "https:\/\/api.github.com\/repos\/twilio\/twilio-php\/zipball\/08aad5f377e2245b9cd7508e7762d95e7392fa4d",
                "reference": "08aad5f377e2245b9cd7508e7762d95e7392fa4d",
                "shasum": ""
            },
            "require": {
                "php": ">=7.1.0"
            },
            "require-dev": {
                "guzzlehttp\/guzzle": "^6.3 || ^7.0",
                "phpunit\/phpunit": ">=7.0 < 10"
            },
            "suggest": {
                "guzzlehttp\/guzzle": "An HTTP client to execute the API requests"
            },
            "time": "2023-02-22T19:59:53+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "WP2FA_Vendor\\Twilio\\": "src\/Twilio\/"
                }
            },
            "notification-url": "https:\/\/packagist.org\/downloads\/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Twilio API Team",
                    "email": "api@twilio.com"
                }
            ],
            "description": "A PHP wrapper for Twilio's API",
            "homepage": "https:\/\/github.com\/twilio\/twilio-php",
            "keywords": [
                "api",
                "sms",
                "twilio"
            ],
            "support": {
                "issues": "https:\/\/github.com\/twilio\/twilio-php\/issues",
                "source": "https:\/\/github.com\/twilio\/twilio-php\/tree\/6.44.4"
            },
            "install-path": "..\/twilio\/sdk"
        }
    ],
    "dev": false,
    "dev-package-names": []
}vendor/composer/installed.php000064400000007346150755130600012374 0ustar00<?php

namespace WP2FA_Vendor;

return array('root' => array('name' => 'wp-white-security/wp-2fa', 'pretty_version' => '2.2.0', 'version' => '2.2.0.0', 'reference' => null, 'type' => 'wordpress-plugin', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), 'dev' => \false), 'versions' => array('arcturial/clickatell' => array('pretty_version' => '3.0.0', 'version' => '3.0.0.0', 'reference' => '541ab55c3a807374fe727aad57004c09887080cf', 'type' => 'library', 'install_path' => __DIR__ . '/../arcturial/clickatell', 'aliases' => array(), 'dev_requirement' => \false), 'bacon/bacon-qr-code' => array('pretty_version' => '2.0.8', 'version' => '2.0.8.0', 'reference' => '8674e51bb65af933a5ffaf1c308a660387c35c22', 'type' => 'library', 'install_path' => __DIR__ . '/../bacon/bacon-qr-code', 'aliases' => array(), 'dev_requirement' => \false), 'dasprid/enum' => array('pretty_version' => '1.0.5', 'version' => '1.0.5.0', 'reference' => '6faf451159fb8ba4126b925ed2d78acfce0dc016', 'type' => 'library', 'install_path' => __DIR__ . '/../dasprid/enum', 'aliases' => array(), 'dev_requirement' => \false), 'endroid/qr-code' => array('pretty_version' => '3.9.7', 'version' => '3.9.7.0', 'reference' => '94563d7b3105288e6ac53a67ae720e3669fac1f6', 'type' => 'library', 'install_path' => __DIR__ . '/../endroid/qr-code', 'aliases' => array(), 'dev_requirement' => \false), 'firebase/php-jwt' => array('pretty_version' => 'v5.5.1', 'version' => '5.5.1.0', 'reference' => '83b609028194aa042ea33b5af2d41a7427de80e6', 'type' => 'library', 'install_path' => __DIR__ . '/../firebase/php-jwt', 'aliases' => array(), 'dev_requirement' => \false), 'freemius/wordpress-sdk' => array('pretty_version' => '2.7.2', 'version' => '2.7.2.0', 'reference' => 'eeac5f905746822207729ed0d944c4434ee165ff', 'type' => 'library', 'install_path' => __DIR__ . '/../freemius/wordpress-sdk', 'aliases' => array(), 'dev_requirement' => \false), 'khanamiryan/qrcode-detector-decoder' => array('pretty_version' => '1.0.6', 'version' => '1.0.6.0', 'reference' => '45326fb83a2a375065dbb3a134b5b8a5872da569', 'type' => 'library', 'install_path' => __DIR__ . '/../khanamiryan/qrcode-detector-decoder', 'aliases' => array(), 'dev_requirement' => \false), 'myclabs/php-enum' => array('pretty_version' => '1.8.4', 'version' => '1.8.4.0', 'reference' => 'a867478eae49c9f59ece437ae7f9506bfaa27483', 'type' => 'library', 'install_path' => __DIR__ . '/../myclabs/php-enum', 'aliases' => array(), 'dev_requirement' => \false), 'symfony/inflector' => array('pretty_version' => 'v5.0.11', 'version' => '5.0.11.0', 'reference' => '7eff2643934179cd0e5a6609a583fc22fc495fc4', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/inflector', 'aliases' => array(), 'dev_requirement' => \false), 'symfony/options-resolver' => array('dev_requirement' => \false, 'replaced' => array(0 => 'v5.0.11')), 'symfony/polyfill-ctype' => array('dev_requirement' => \false, 'replaced' => array(0 => 'v1.20.0')), 'symfony/property-access' => array('pretty_version' => 'v5.0.11', 'version' => '5.0.11.0', 'reference' => 'fdc47c3780ebb29077c3421c6253ccc91040c24a', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/property-access', 'aliases' => array(), 'dev_requirement' => \false), 'symfony/string' => array('dev_requirement' => \false, 'replaced' => array(0 => 'v5.0.11')), 'twilio/sdk' => array('pretty_version' => '6.44.4', 'version' => '6.44.4.0', 'reference' => '08aad5f377e2245b9cd7508e7762d95e7392fa4d', 'type' => 'library', 'install_path' => __DIR__ . '/../twilio/sdk', 'aliases' => array(), 'dev_requirement' => \false), 'wp-white-security/wp-2fa' => array('pretty_version' => '2.2.0', 'version' => '2.2.0.0', 'reference' => null, 'type' => 'wordpress-plugin', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), 'dev_requirement' => \false)));
vendor/composer/autoload_namespaces.php000064400000000213150755130600014406 0ustar00<?php

// autoload_namespaces.php @generated by Composer

$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);

return array(
);
vendor/composer/autoload_static.php000064400000024113150755130600013563 0ustar00<?php

// autoload_static.php @generated by Composer

namespace Composer\Autoload;

class ComposerStaticInit29692
{
    public static $files = array (
        'a9ed0d27b5a698798a89181429f162c5' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/Common/customFunctions.php',
    );

    public static $prefixLengthsPsr4 = array (
        'W' => 
        array (
            'WP2FA_Vendor\\Zxing\\' => 19,
            'WP2FA_Vendor\\Twilio\\' => 20,
            'WP2FA_Vendor\\Symfony\\Component\\PropertyAccess\\' => 46,
            'WP2FA_Vendor\\Symfony\\Component\\Inflector\\' => 41,
            'WP2FA_Vendor\\MyCLabs\\Enum\\' => 26,
            'WP2FA_Vendor\\Firebase\\JWT\\' => 26,
            'WP2FA_Vendor\\Endroid\\QrCode\\' => 28,
            'WP2FA_Vendor\\DASPRiD\\Enum\\' => 26,
            'WP2FA_Vendor\\Clickatell\\' => 24,
            'WP2FA_Vendor\\BaconQrCode\\' => 25,
            
            'WP2FA\\' => 6,
        ),
    );

    public static $prefixDirsPsr4 = array (
        'WP2FA_Vendor\\Zxing\\' => 
        array (
            0 => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib',
        ),
        'WP2FA_Vendor\\Twilio\\' => 
        array (
            0 => __DIR__ . '/..' . '/twilio/sdk/src/Twilio',
        ),
        'WP2FA_Vendor\\Symfony\\Component\\PropertyAccess\\' => 
        array (
            0 => __DIR__ . '/..' . '/symfony/property-access',
        ),
        'WP2FA_Vendor\\Symfony\\Component\\Inflector\\' => 
        array (
            0 => __DIR__ . '/..' . '/symfony/inflector',
        ),
        'WP2FA_Vendor\\MyCLabs\\Enum\\' => 
        array (
            0 => __DIR__ . '/..' . '/myclabs/php-enum/src',
        ),
        'WP2FA_Vendor\\Firebase\\JWT\\' => 
        array (
            0 => __DIR__ . '/..' . '/firebase/php-jwt/src',
        ),
        'WP2FA_Vendor\\Endroid\\QrCode\\' => 
        array (
            0 => __DIR__ . '/..' . '/endroid/qr-code/src',
        ),
        'WP2FA_Vendor\\DASPRiD\\Enum\\' => 
        array (
            0 => __DIR__ . '/..' . '/dasprid/enum/src',
        ),
        'WP2FA_Vendor\\Clickatell\\' => 
        array (
            0 => __DIR__ . '/..' . '/arcturial/clickatell/src',
            1 => __DIR__ . '/..' . '/arcturial/clickatell/test',
        ),
        'WP2FA_Vendor\\BaconQrCode\\' => 
        array (
            0 => __DIR__ . '/..' . '/bacon/bacon-qr-code/src',
        ),
        
        array (
            0 => __DIR__ . '/../..' . '/extensions',
        ),
        'WP2FA\\' => 
        array (
            0 => __DIR__ . '/../..' . '/includes/classes',
        ),
    );

    public static $classMap = array (
        'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
        'WP2FA\\Admin\\Controllers\\Methods' => __DIR__ . '/../..' . '/includes/classes/Admin/Controllers/class-methods.php',
        'WP2FA\\Admin\\Controllers\\Settings' => __DIR__ . '/../..' . '/includes/classes/Admin/Controllers/class-settings.php',
        'WP2FA\\Admin\\FlyOut\\FlyOut' => __DIR__ . '/../..' . '/includes/classes/Admin/Fly-Out/class-flyout.php',
        'WP2FA\\Admin\\Help_Contact_Us' => __DIR__ . '/../..' . '/includes/classes/Admin/class-help-contact-us.php',
        'WP2FA\\Admin\\Helpers\\Ajax_Helper' => __DIR__ . '/../..' . '/includes/classes/Admin/Helpers/class-ajax-helper.php',
        'WP2FA\\Admin\\Helpers\\Classes_Helper' => __DIR__ . '/../..' . '/includes/classes/Admin/Helpers/class-classes-helper.php',
        'WP2FA\\Admin\\Helpers\\File_Writer' => __DIR__ . '/../..' . '/includes/classes/Admin/Helpers/class-file-writer.php',
        'WP2FA\\Admin\\Helpers\\Methods_Helper' => __DIR__ . '/../..' . '/includes/classes/Admin/Helpers/class-methods-helper.php',
        'WP2FA\\Admin\\Helpers\\PHP_Helper' => __DIR__ . '/../..' . '/includes/classes/Admin/Helpers/class-php-helper.php',
        'WP2FA\\Admin\\Helpers\\User_Helper' => __DIR__ . '/../..' . '/includes/classes/Admin/Helpers/class-user-helper.php',
        'WP2FA\\Admin\\Helpers\\WP_Helper' => __DIR__ . '/../..' . '/includes/classes/Admin/Helpers/class-wp-helper.php',
        'WP2FA\\Admin\\Methods\\Traits\\Login_Attempts' => __DIR__ . '/../..' . '/includes/classes/Admin/Methods/Traits/class-login-attempts.php',
        'WP2FA\\Admin\\Methods\\Traits\\Methods_Wizards_Trait' => __DIR__ . '/../..' . '/includes/classes/Admin/Methods/Traits/class-methods-wizards-trait.php',
        'WP2FA\\Admin\\Plugin_Updated_Notice' => __DIR__ . '/../..' . '/includes/classes/Admin/class-plugin-updated-notice.php',
        'WP2FA\\Admin\\Premium_Features' => __DIR__ . '/../..' . '/includes/classes/Admin/class-premium-features.php',
        'WP2FA\\Admin\\SettingsPages\\Settings_Page_Email' => __DIR__ . '/../..' . '/includes/classes/Admin/SettingsPages/class-settings-page-email.php',
        'WP2FA\\Admin\\SettingsPages\\Settings_Page_General' => __DIR__ . '/../..' . '/includes/classes/Admin/SettingsPages/class-settings-page-general.php',
        'WP2FA\\Admin\\SettingsPages\\Settings_Page_Policies' => __DIR__ . '/../..' . '/includes/classes/Admin/SettingsPages/class-settings-page-policies.php',
        'WP2FA\\Admin\\SettingsPages\\Settings_Page_Render' => __DIR__ . '/../..' . '/includes/classes/Admin/SettingsPages/class-settings-page-render.php',
        'WP2FA\\Admin\\SettingsPages\\Settings_Page_White_Label' => __DIR__ . '/../..' . '/includes/classes/Admin/SettingsPages/class-settings-page-white-label.php',
        'WP2FA\\Admin\\Settings_Page' => __DIR__ . '/../..' . '/includes/classes/Admin/class-settings-page.php',
        'WP2FA\\Admin\\Setup_Wizard' => __DIR__ . '/../..' . '/includes/classes/Admin/class-setup-wizard.php',
        'WP2FA\\Admin\\User_Listing' => __DIR__ . '/../..' . '/includes/classes/Admin/class-user-listing.php',
        'WP2FA\\Admin\\User_Notices' => __DIR__ . '/../..' . '/includes/classes/Admin/class-user-notices.php',
        'WP2FA\\Admin\\User_Profile' => __DIR__ . '/../..' . '/includes/classes/Admin/class-user-profile.php',
        'WP2FA\\Admin\\User_Registered' => __DIR__ . '/../..' . '/includes/classes/Admin/class-user-registered.php',
        'WP2FA\\Admin\\Views\\First_Time_Wizard_Steps' => __DIR__ . '/../..' . '/includes/classes/Admin/Views/class-first-time-wizard-steps.php',
        'WP2FA\\Admin\\Views\\Grace_Period_Notifications' => __DIR__ . '/../..' . '/includes/classes/Admin/Views/class-grace-period-notifications.php',
        'WP2FA\\Admin\\Views\\Password_Reset_2FA' => __DIR__ . '/../..' . '/includes/classes/Admin/Views/class-passord-reset-2fa.php',
        'WP2FA\\Admin\\Views\\Re_Login_2FA' => __DIR__ . '/../..' . '/includes/classes/Admin/Views/class-re-login-2fa.php',
        'WP2FA\\Admin\\Views\\Wizard_Steps' => __DIR__ . '/../..' . '/includes/classes/Admin/Views/class-wizard-steps.php',
        'WP2FA\\App\\Grace_Period' => __DIR__ . '/../..' . '/includes/classes/App/grace-period/class-grace-period.php',
        'WP2FA\\Authenticator\\Authentication' => __DIR__ . '/../..' . '/includes/classes/Authenticator/class-authentication.php',
        'WP2FA\\Authenticator\\Login' => __DIR__ . '/../..' . '/includes/classes/Authenticator/class-login.php',
        'WP2FA\\Authenticator\\Open_SSL' => __DIR__ . '/../..' . '/includes/classes/Authenticator/class-open-ssl.php',
        'WP2FA\\Authenticator\\Reset_Password' => __DIR__ . '/../..' . '/includes/classes/Authenticator/class-reset-passord.php',
        'WP2FA\\Email_Template' => __DIR__ . '/../..' . '/includes/classes/class-email-template.php',
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        'WP2FA\\Methods\\Backup_Codes' => __DIR__ . '/../..' . '/includes/classes/Admin/Methods/class-backup-codes.php',
        'WP2FA\\Methods\\Email' => __DIR__ . '/../..' . '/includes/classes/Admin/Methods/class-email.php',
        'WP2FA\\Methods\\TOTP' => __DIR__ . '/../..' . '/includes/classes/Admin/Methods/class-totp.php',
        'WP2FA\\Methods\\Wizards\\Email_Wizard_Steps' => __DIR__ . '/../..' . '/includes/classes/Admin/Methods/class-email-wizard-steps.php',
        'WP2FA\\Methods\\Wizards\\TOTP_Wizard_Steps' => __DIR__ . '/../..' . '/includes/classes/Admin/Methods/class-totp-wizard-steps.php',
        'WP2FA\\Shortcodes\\Shortcodes' => __DIR__ . '/../..' . '/includes/classes/Shortcodes/class-shortcodes.php',
        'WP2FA\\Utils\\Abstract_Migration' => __DIR__ . '/../..' . '/includes/classes/Utils/class-abstract-migration.php',
        'WP2FA\\Utils\\Date_Time_Utils' => __DIR__ . '/../..' . '/includes/classes/Utils/class-date-time-utils.php',
        'WP2FA\\Utils\\Debugging' => __DIR__ . '/../..' . '/includes/classes/Utils/class-debugging.php',
        'WP2FA\\Utils\\Generate_Modal' => __DIR__ . '/../..' . '/includes/classes/Utils/class-generate-modal.php',
        'WP2FA\\Utils\\Migration' => __DIR__ . '/../..' . '/includes/classes/Utils/class-migration.php',
        'WP2FA\\Utils\\Request_Utils' => __DIR__ . '/../..' . '/includes/classes/Utils/class-request-utils.php',
        'WP2FA\\Utils\\Settings_Utils' => __DIR__ . '/../..' . '/includes/classes/Utils/class-settings-utils.php',
        'WP2FA\\Utils\\User_Utils' => __DIR__ . '/../..' . '/includes/classes/Utils/class-user-utils.php',
        'WP2FA\\Utils\\White_Label' => __DIR__ . '/../..' . '/includes/classes/Utils/class-white-label.php',
        'WP2FA\\WP2FA' => __DIR__ . '/../..' . '/includes/classes/class-wp2fa.php',
        'WP2FA_Vendor\\Stringable' => __DIR__ . '/..' . '/myclabs/php-enum/stubs/Stringable.php',
    );

    public static function getInitializer(ClassLoader $loader)
    {
        return \Closure::bind(function () use ($loader) {
            $loader->prefixLengthsPsr4 = ComposerStaticInit29692::$prefixLengthsPsr4;
            $loader->prefixDirsPsr4 = ComposerStaticInit29692::$prefixDirsPsr4;
            $loader->classMap = ComposerStaticInit29692::$classMap;

        }, null, ClassLoader::class);
    }
}
vendor/composer/autoload_classmap.php000064400000014427150755130600014106 0ustar00<?php

// autoload_classmap.php @generated by Composer

$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);

return array(
    'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
    'WP2FA\\Admin\\Controllers\\Methods' => $baseDir . '/includes/classes/Admin/Controllers/class-methods.php',
    'WP2FA\\Admin\\Controllers\\Settings' => $baseDir . '/includes/classes/Admin/Controllers/class-settings.php',
    'WP2FA\\Admin\\FlyOut\\FlyOut' => $baseDir . '/includes/classes/Admin/Fly-Out/class-flyout.php',
    'WP2FA\\Admin\\Help_Contact_Us' => $baseDir . '/includes/classes/Admin/class-help-contact-us.php',
    'WP2FA\\Admin\\Helpers\\Ajax_Helper' => $baseDir . '/includes/classes/Admin/Helpers/class-ajax-helper.php',
    'WP2FA\\Admin\\Helpers\\Classes_Helper' => $baseDir . '/includes/classes/Admin/Helpers/class-classes-helper.php',
    'WP2FA\\Admin\\Helpers\\File_Writer' => $baseDir . '/includes/classes/Admin/Helpers/class-file-writer.php',
    'WP2FA\\Admin\\Helpers\\Methods_Helper' => $baseDir . '/includes/classes/Admin/Helpers/class-methods-helper.php',
    'WP2FA\\Admin\\Helpers\\PHP_Helper' => $baseDir . '/includes/classes/Admin/Helpers/class-php-helper.php',
    'WP2FA\\Admin\\Helpers\\User_Helper' => $baseDir . '/includes/classes/Admin/Helpers/class-user-helper.php',
    'WP2FA\\Admin\\Helpers\\WP_Helper' => $baseDir . '/includes/classes/Admin/Helpers/class-wp-helper.php',
    'WP2FA\\Admin\\Methods\\Traits\\Login_Attempts' => $baseDir . '/includes/classes/Admin/Methods/Traits/class-login-attempts.php',
    'WP2FA\\Admin\\Methods\\Traits\\Methods_Wizards_Trait' => $baseDir . '/includes/classes/Admin/Methods/Traits/class-methods-wizards-trait.php',
    'WP2FA\\Admin\\Plugin_Updated_Notice' => $baseDir . '/includes/classes/Admin/class-plugin-updated-notice.php',
    'WP2FA\\Admin\\Premium_Features' => $baseDir . '/includes/classes/Admin/class-premium-features.php',
    'WP2FA\\Admin\\SettingsPages\\Settings_Page_Email' => $baseDir . '/includes/classes/Admin/SettingsPages/class-settings-page-email.php',
    'WP2FA\\Admin\\SettingsPages\\Settings_Page_General' => $baseDir . '/includes/classes/Admin/SettingsPages/class-settings-page-general.php',
    'WP2FA\\Admin\\SettingsPages\\Settings_Page_Policies' => $baseDir . '/includes/classes/Admin/SettingsPages/class-settings-page-policies.php',
    'WP2FA\\Admin\\SettingsPages\\Settings_Page_Render' => $baseDir . '/includes/classes/Admin/SettingsPages/class-settings-page-render.php',
    'WP2FA\\Admin\\SettingsPages\\Settings_Page_White_Label' => $baseDir . '/includes/classes/Admin/SettingsPages/class-settings-page-white-label.php',
    'WP2FA\\Admin\\Settings_Page' => $baseDir . '/includes/classes/Admin/class-settings-page.php',
    'WP2FA\\Admin\\Setup_Wizard' => $baseDir . '/includes/classes/Admin/class-setup-wizard.php',
    'WP2FA\\Admin\\User_Listing' => $baseDir . '/includes/classes/Admin/class-user-listing.php',
    'WP2FA\\Admin\\User_Notices' => $baseDir . '/includes/classes/Admin/class-user-notices.php',
    'WP2FA\\Admin\\User_Profile' => $baseDir . '/includes/classes/Admin/class-user-profile.php',
    'WP2FA\\Admin\\User_Registered' => $baseDir . '/includes/classes/Admin/class-user-registered.php',
    'WP2FA\\Admin\\Views\\First_Time_Wizard_Steps' => $baseDir . '/includes/classes/Admin/Views/class-first-time-wizard-steps.php',
    'WP2FA\\Admin\\Views\\Grace_Period_Notifications' => $baseDir . '/includes/classes/Admin/Views/class-grace-period-notifications.php',
    'WP2FA\\Admin\\Views\\Password_Reset_2FA' => $baseDir . '/includes/classes/Admin/Views/class-passord-reset-2fa.php',
    'WP2FA\\Admin\\Views\\Re_Login_2FA' => $baseDir . '/includes/classes/Admin/Views/class-re-login-2fa.php',
    'WP2FA\\Admin\\Views\\Wizard_Steps' => $baseDir . '/includes/classes/Admin/Views/class-wizard-steps.php',
    'WP2FA\\App\\Grace_Period' => $baseDir . '/includes/classes/App/grace-period/class-grace-period.php',
    'WP2FA\\Authenticator\\Authentication' => $baseDir . '/includes/classes/Authenticator/class-authentication.php',
    'WP2FA\\Authenticator\\Login' => $baseDir . '/includes/classes/Authenticator/class-login.php',
    'WP2FA\\Authenticator\\Open_SSL' => $baseDir . '/includes/classes/Authenticator/class-open-ssl.php',
    'WP2FA\\Authenticator\\Reset_Password' => $baseDir . '/includes/classes/Authenticator/class-reset-passord.php',
    'WP2FA\\Email_Template' => $baseDir . '/includes/classes/class-email-template.php',
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    'WP2FA\\Methods\\Backup_Codes' => $baseDir . '/includes/classes/Admin/Methods/class-backup-codes.php',
    'WP2FA\\Methods\\Email' => $baseDir . '/includes/classes/Admin/Methods/class-email.php',
    'WP2FA\\Methods\\TOTP' => $baseDir . '/includes/classes/Admin/Methods/class-totp.php',
    'WP2FA\\Methods\\Wizards\\Email_Wizard_Steps' => $baseDir . '/includes/classes/Admin/Methods/class-email-wizard-steps.php',
    'WP2FA\\Methods\\Wizards\\TOTP_Wizard_Steps' => $baseDir . '/includes/classes/Admin/Methods/class-totp-wizard-steps.php',
    'WP2FA\\Shortcodes\\Shortcodes' => $baseDir . '/includes/classes/Shortcodes/class-shortcodes.php',
    'WP2FA\\Utils\\Abstract_Migration' => $baseDir . '/includes/classes/Utils/class-abstract-migration.php',
    'WP2FA\\Utils\\Date_Time_Utils' => $baseDir . '/includes/classes/Utils/class-date-time-utils.php',
    'WP2FA\\Utils\\Debugging' => $baseDir . '/includes/classes/Utils/class-debugging.php',
    'WP2FA\\Utils\\Generate_Modal' => $baseDir . '/includes/classes/Utils/class-generate-modal.php',
    'WP2FA\\Utils\\Migration' => $baseDir . '/includes/classes/Utils/class-migration.php',
    'WP2FA\\Utils\\Request_Utils' => $baseDir . '/includes/classes/Utils/class-request-utils.php',
    'WP2FA\\Utils\\Settings_Utils' => $baseDir . '/includes/classes/Utils/class-settings-utils.php',
    'WP2FA\\Utils\\User_Utils' => $baseDir . '/includes/classes/Utils/class-user-utils.php',
    'WP2FA\\Utils\\White_Label' => $baseDir . '/includes/classes/Utils/class-white-label.php',
    'WP2FA\\WP2FA' => $baseDir . '/includes/classes/class-wp2fa.php',
    'WP2FA_Vendor\\Stringable' => $vendorDir . '/myclabs/php-enum/stubs/Stringable.php',
);
vendor/composer/InstalledVersions.php000064400000037405150755130600014064 0ustar00<?php

/*
 * This file is part of Composer.
 *
 * (c) Nils Adermann <naderman@naderman.de>
 *     Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace Composer;

use Composer\Autoload\ClassLoader;
use Composer\Semver\VersionParser;
/**
 * This class is copied in every Composer installed project and available to all
 *
 * See also https://getcomposer.org/doc/07-runtime.md#installed-versions
 *
 * To require its presence, you can require `composer-runtime-api ^2.0`
 *
 * @final
 */
class InstalledVersions
{
    /**
     * @var mixed[]|null
     * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
     */
    private static $installed;
    /**
     * @var bool|null
     */
    private static $canGetVendors;
    /**
     * @var array[]
     * @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
     */
    private static $installedByVendor = array();
    /**
     * Returns a list of all package names which are present, either by being installed, replaced or provided
     *
     * @return string[]
     * @psalm-return list<string>
     */
    public static function getInstalledPackages()
    {
        $packages = array();
        foreach (self::getInstalled() as $installed) {
            $packages[] = \array_keys($installed['versions']);
        }
        if (1 === \count($packages)) {
            return $packages[0];
        }
        return \array_keys(\array_flip(\call_user_func_array('array_merge', $packages)));
    }
    /**
     * Returns a list of all package names with a specific type e.g. 'library'
     *
     * @param  string   $type
     * @return string[]
     * @psalm-return list<string>
     */
    public static function getInstalledPackagesByType($type)
    {
        $packagesByType = array();
        foreach (self::getInstalled() as $installed) {
            foreach ($installed['versions'] as $name => $package) {
                if (isset($package['type']) && $package['type'] === $type) {
                    $packagesByType[] = $name;
                }
            }
        }
        return $packagesByType;
    }
    /**
     * Checks whether the given package is installed
     *
     * This also returns true if the package name is provided or replaced by another package
     *
     * @param  string $packageName
     * @param  bool   $includeDevRequirements
     * @return bool
     */
    public static function isInstalled($packageName, $includeDevRequirements = \true)
    {
        foreach (self::getInstalled() as $installed) {
            if (isset($installed['versions'][$packageName])) {
                return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === \false;
            }
        }
        return \false;
    }
    /**
     * Checks whether the given package satisfies a version constraint
     *
     * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
     *
     *   Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
     *
     * @param  VersionParser $parser      Install composer/semver to have access to this class and functionality
     * @param  string        $packageName
     * @param  string|null   $constraint  A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
     * @return bool
     */
    public static function satisfies(VersionParser $parser, $packageName, $constraint)
    {
        $constraint = $parser->parseConstraints((string) $constraint);
        $provided = $parser->parseConstraints(self::getVersionRanges($packageName));
        return $provided->matches($constraint);
    }
    /**
     * Returns a version constraint representing all the range(s) which are installed for a given package
     *
     * It is easier to use this via isInstalled() with the $constraint argument if you need to check
     * whether a given version of a package is installed, and not just whether it exists
     *
     * @param  string $packageName
     * @return string Version constraint usable with composer/semver
     */
    public static function getVersionRanges($packageName)
    {
        foreach (self::getInstalled() as $installed) {
            if (!isset($installed['versions'][$packageName])) {
                continue;
            }
            $ranges = array();
            if (isset($installed['versions'][$packageName]['pretty_version'])) {
                $ranges[] = $installed['versions'][$packageName]['pretty_version'];
            }
            if (\array_key_exists('aliases', $installed['versions'][$packageName])) {
                $ranges = \array_merge($ranges, $installed['versions'][$packageName]['aliases']);
            }
            if (\array_key_exists('replaced', $installed['versions'][$packageName])) {
                $ranges = \array_merge($ranges, $installed['versions'][$packageName]['replaced']);
            }
            if (\array_key_exists('provided', $installed['versions'][$packageName])) {
                $ranges = \array_merge($ranges, $installed['versions'][$packageName]['provided']);
            }
            return \implode(' || ', $ranges);
        }
        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
    }
    /**
     * @param  string      $packageName
     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
     */
    public static function getVersion($packageName)
    {
        foreach (self::getInstalled() as $installed) {
            if (!isset($installed['versions'][$packageName])) {
                continue;
            }
            if (!isset($installed['versions'][$packageName]['version'])) {
                return null;
            }
            return $installed['versions'][$packageName]['version'];
        }
        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
    }
    /**
     * @param  string      $packageName
     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
     */
    public static function getPrettyVersion($packageName)
    {
        foreach (self::getInstalled() as $installed) {
            if (!isset($installed['versions'][$packageName])) {
                continue;
            }
            if (!isset($installed['versions'][$packageName]['pretty_version'])) {
                return null;
            }
            return $installed['versions'][$packageName]['pretty_version'];
        }
        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
    }
    /**
     * @param  string      $packageName
     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
     */
    public static function getReference($packageName)
    {
        foreach (self::getInstalled() as $installed) {
            if (!isset($installed['versions'][$packageName])) {
                continue;
            }
            if (!isset($installed['versions'][$packageName]['reference'])) {
                return null;
            }
            return $installed['versions'][$packageName]['reference'];
        }
        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
    }
    /**
     * @param  string      $packageName
     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
     */
    public static function getInstallPath($packageName)
    {
        foreach (self::getInstalled() as $installed) {
            if (!isset($installed['versions'][$packageName])) {
                continue;
            }
            return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
        }
        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
    }
    /**
     * @return array
     * @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
     */
    public static function getRootPackage()
    {
        $installed = self::getInstalled();
        return $installed[0]['root'];
    }
    /**
     * Returns the raw installed.php data for custom implementations
     *
     * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
     * @return array[]
     * @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
     */
    public static function getRawData()
    {
        @\trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', \E_USER_DEPRECATED);
        if (null === self::$installed) {
            // only require the installed.php file if this file is loaded from its dumped location,
            // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
            if (\substr(__DIR__, -8, 1) !== 'C') {
                self::$installed = (include __DIR__ . '/installed.php');
            } else {
                self::$installed = array();
            }
        }
        return self::$installed;
    }
    /**
     * Returns the raw data of all installed.php which are currently loaded for custom implementations
     *
     * @return array[]
     * @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
     */
    public static function getAllRawData()
    {
        return self::getInstalled();
    }
    /**
     * Lets you reload the static array from another file
     *
     * This is only useful for complex integrations in which a project needs to use
     * this class but then also needs to execute another project's autoloader in process,
     * and wants to ensure both projects have access to their version of installed.php.
     *
     * A typical case would be PHPUnit, where it would need to make sure it reads all
     * the data it needs from this class, then call reload() with
     * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
     * the project in which it runs can then also use this class safely, without
     * interference between PHPUnit's dependencies and the project's dependencies.
     *
     * @param  array[] $data A vendor/composer/installed.php data set
     * @return void
     *
     * @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
     */
    public static function reload($data)
    {
        self::$installed = $data;
        self::$installedByVendor = array();
    }
    /**
     * @return array[]
     * @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
     */
    private static function getInstalled()
    {
        if (null === self::$canGetVendors) {
            self::$canGetVendors = \method_exists('Composer\\Autoload\\ClassLoader', 'getRegisteredLoaders');
        }
        $installed = array();
        if (self::$canGetVendors) {
            foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
                if (isset(self::$installedByVendor[$vendorDir])) {
                    $installed[] = self::$installedByVendor[$vendorDir];
                } elseif (\is_file($vendorDir . '/composer/installed.php')) {
                    /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
                    $required = (require $vendorDir . '/composer/installed.php');
                    $installed[] = self::$installedByVendor[$vendorDir] = $required;
                    if (null === self::$installed && \strtr($vendorDir . '/composer', '\\', '/') === \strtr(__DIR__, '\\', '/')) {
                        self::$installed = $installed[\count($installed) - 1];
                    }
                }
            }
        }
        if (null === self::$installed) {
            // only require the installed.php file if this file is loaded from its dumped location,
            // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
            if (\substr(__DIR__, -8, 1) !== 'C') {
                /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
                $required = (require __DIR__ . '/installed.php');
                self::$installed = $required;
            } else {
                self::$installed = array();
            }
        }
        if (self::$installed !== array()) {
            $installed[] = self::$installed;
        }
        return $installed;
    }
}
vendor/composer/platform_check.php000064400000001635150755130600013371 0ustar00<?php

// platform_check.php @generated by Composer

$issues = array();

if (!(PHP_VERSION_ID >= 70300)) {
    $issues[] = 'Your Composer dependencies require a PHP version ">= 7.3.0". You are running ' . PHP_VERSION . '.';
}

if ($issues) {
    if (!headers_sent()) {
        header('HTTP/1.1 500 Internal Server Error');
    }
    if (!ini_get('display_errors')) {
        if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
            fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
        } elseif (!headers_sent()) {
            echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
        }
    }
    trigger_error(
        'Composer detected issues in your platform: ' . implode(' ', $issues),
        E_USER_ERROR
    );
}
vendor/composer/autoload_files.php000064400000000404150755130600013373 0ustar00<?php

// autoload_files.php @generated by Composer

$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);

return array(
    'a9ed0d27b5a698798a89181429f162c5' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/Common/customFunctions.php',
);
vendor/dasprid/enum/src/NullValue.php000064400000002210150755130600013637 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\DASPRiD\Enum;

use WP2FA_Vendor\DASPRiD\Enum\Exception\CloneNotSupportedException;
use WP2FA_Vendor\DASPRiD\Enum\Exception\SerializeNotSupportedException;
use WP2FA_Vendor\DASPRiD\Enum\Exception\UnserializeNotSupportedException;
final class NullValue
{
    /**
     * @var self
     */
    private static $instance;
    private function __construct()
    {
    }
    public static function instance() : self
    {
        return self::$instance ?: (self::$instance = new self());
    }
    /**
     * Forbid cloning enums.
     *
     * @throws CloneNotSupportedException
     */
    public final function __clone()
    {
        throw new CloneNotSupportedException();
    }
    /**
     * Forbid serializing enums.
     *
     * @throws SerializeNotSupportedException
     */
    public final function __sleep() : array
    {
        throw new SerializeNotSupportedException();
    }
    /**
     * Forbid unserializing enums.
     *
     * @throws UnserializeNotSupportedException
     */
    public final function __wakeup() : void
    {
        throw new UnserializeNotSupportedException();
    }
}
vendor/dasprid/enum/src/Exception/ExceptionInterface.php000064400000000223150755130600017447 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\DASPRiD\Enum\Exception;

use Throwable;
interface ExceptionInterface extends Throwable
{
}
vendor/dasprid/enum/src/Exception/ExpectationException.php000064400000000265150755130600020040 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\DASPRiD\Enum\Exception;

use Exception;
final class ExpectationException extends Exception implements ExceptionInterface
{
}
vendor/dasprid/enum/src/Exception/MismatchException.php000064400000000262150755130600017317 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\DASPRiD\Enum\Exception;

use Exception;
final class MismatchException extends Exception implements ExceptionInterface
{
}
vendor/dasprid/enum/src/Exception/SerializeNotSupportedException.php000064400000000277150755130600022076 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\DASPRiD\Enum\Exception;

use Exception;
final class SerializeNotSupportedException extends Exception implements ExceptionInterface
{
}
vendor/dasprid/enum/src/Exception/IllegalArgumentException.php000064400000000271150755130600020626 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\DASPRiD\Enum\Exception;

use Exception;
final class IllegalArgumentException extends Exception implements ExceptionInterface
{
}
vendor/dasprid/enum/src/Exception/UnserializeNotSupportedException.php000064400000000301150755130600022425 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\DASPRiD\Enum\Exception;

use Exception;
final class UnserializeNotSupportedException extends Exception implements ExceptionInterface
{
}
vendor/dasprid/enum/src/Exception/CloneNotSupportedException.php000064400000000273150755130600021203 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\DASPRiD\Enum\Exception;

use Exception;
final class CloneNotSupportedException extends Exception implements ExceptionInterface
{
}
vendor/dasprid/enum/src/EnumMap.php000064400000025475150755130600013314 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\DASPRiD\Enum;

use WP2FA_Vendor\DASPRiD\Enum\Exception\ExpectationException;
use WP2FA_Vendor\DASPRiD\Enum\Exception\IllegalArgumentException;
use IteratorAggregate;
use Serializable;
use Traversable;
/**
 * A specialized map implementation for use with enum type keys.
 *
 * All of the keys in an enum map must come from a single enum type that is specified, when the map is created. Enum
 * maps are represented internally as arrays. This representation is extremely compact and efficient.
 *
 * Enum maps are maintained in the natural order of their keys (the order in which the enum constants are declared).
 * This is reflected in the iterators returned by the collection views {@see self::getIterator()} and
 * {@see self::values()}.
 *
 * Iterators returned by the collection views are not consistent: They may or may not show the effects of modifications
 * to the map that occur while the iteration is in progress.
 */
final class EnumMap implements Serializable, IteratorAggregate
{
    /**
     * The class name of the key.
     *
     * @var string
     */
    private $keyType;
    /**
     * The type of the value.
     *
     * @var string
     */
    private $valueType;
    /**
     * @var bool
     */
    private $allowNullValues;
    /**
     * All of the constants comprising the enum, cached for performance.
     *
     * @var array<int, AbstractEnum>
     */
    private $keyUniverse;
    /**
     * Array representation of this map. The ith element is the value to which universe[i] is currently mapped, or null
     * if it isn't mapped to anything, or NullValue if it's mapped to null.
     *
     * @var array<int, mixed>
     */
    private $values;
    /**
     * @var int
     */
    private $size = 0;
    /**
     * Creates a new enum map.
     *
     * @param string $keyType the type of the keys, must extend AbstractEnum
     * @param string $valueType the type of the values
     * @param bool $allowNullValues whether to allow null values
     * @throws IllegalArgumentException when key type does not extend AbstractEnum
     */
    public function __construct(string $keyType, string $valueType, bool $allowNullValues)
    {
        if (!\is_subclass_of($keyType, AbstractEnum::class)) {
            throw new IllegalArgumentException(\sprintf('Class %s does not extend %s', $keyType, AbstractEnum::class));
        }
        $this->keyType = $keyType;
        $this->valueType = $valueType;
        $this->allowNullValues = $allowNullValues;
        $this->keyUniverse = $keyType::values();
        $this->values = \array_fill(0, \count($this->keyUniverse), null);
    }
    public function __serialize() : array
    {
        $values = [];
        foreach ($this->values as $ordinal => $value) {
            if (null === $value) {
                continue;
            }
            $values[$ordinal] = $this->unmaskNull($value);
        }
        return ['keyType' => $this->keyType, 'valueType' => $this->valueType, 'allowNullValues' => $this->allowNullValues, 'values' => $values];
    }
    public function __unserialize(array $data) : void
    {
        $this->unserialize(\serialize($data));
    }
    /**
     * Checks whether the map types match the supplied ones.
     *
     * You should call this method when an EnumMap is passed to you and you want to ensure that it's made up of the
     * correct types.
     *
     * @throws ExpectationException when supplied key type mismatches local key type
     * @throws ExpectationException when supplied value type mismatches local value type
     * @throws ExpectationException when the supplied map allows null values, abut should not
     */
    public function expect(string $keyType, string $valueType, bool $allowNullValues) : void
    {
        if ($keyType !== $this->keyType) {
            throw new ExpectationException(\sprintf('Callee expected an EnumMap with key type %s, but got %s', $keyType, $this->keyType));
        }
        if ($valueType !== $this->valueType) {
            throw new ExpectationException(\sprintf('Callee expected an EnumMap with value type %s, but got %s', $keyType, $this->keyType));
        }
        if ($allowNullValues !== $this->allowNullValues) {
            throw new ExpectationException(\sprintf('Callee expected an EnumMap with nullable flag %s, but got %s', $allowNullValues ? 'true' : 'false', $this->allowNullValues ? 'true' : 'false'));
        }
    }
    /**
     * Returns the number of key-value mappings in this map.
     */
    public function size() : int
    {
        return $this->size;
    }
    /**
     * Returns true if this map maps one or more keys to the specified value.
     */
    public function containsValue($value) : bool
    {
        return \in_array($this->maskNull($value), $this->values, \true);
    }
    /**
     * Returns true if this map contains a mapping for the specified key.
     */
    public function containsKey(AbstractEnum $key) : bool
    {
        $this->checkKeyType($key);
        return null !== $this->values[$key->ordinal()];
    }
    /**
     * Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key.
     *
     * More formally, if this map contains a mapping from a key to a value, then this method returns the value;
     * otherwise it returns null (there can be at most one such mapping).
     *
     * A return value of null does not necessarily indicate that the map contains no mapping for the key; it's also
     * possible that hte map explicitly maps the key to null. The {@see self::containsKey()} operation may be used to
     * distinguish these two cases.
     *
     * @return mixed
     */
    public function get(AbstractEnum $key)
    {
        $this->checkKeyType($key);
        return $this->unmaskNull($this->values[$key->ordinal()]);
    }
    /**
     * Associates the specified value with the specified key in this map.
     *
     * If the map previously contained a mapping for this key, the old value is replaced.
     *
     * @return mixed the previous value associated with the specified key, or null if there was no mapping for the key.
     *               (a null return can also indicate that the map previously associated null with the specified key.)
     * @throws IllegalArgumentException when the passed values does not match the internal value type
     */
    public function put(AbstractEnum $key, $value)
    {
        $this->checkKeyType($key);
        if (!$this->isValidValue($value)) {
            throw new IllegalArgumentException(\sprintf('Value is not of type %s', $this->valueType));
        }
        $index = $key->ordinal();
        $oldValue = $this->values[$index];
        $this->values[$index] = $this->maskNull($value);
        if (null === $oldValue) {
            ++$this->size;
        }
        return $this->unmaskNull($oldValue);
    }
    /**
     * Removes the mapping for this key frm this map if present.
     *
     * @return mixed the previous value associated with the specified key, or null if there was no mapping for the key.
     *               (a null return can also indicate that the map previously associated null with the specified key.)
     */
    public function remove(AbstractEnum $key)
    {
        $this->checkKeyType($key);
        $index = $key->ordinal();
        $oldValue = $this->values[$index];
        $this->values[$index] = null;
        if (null !== $oldValue) {
            --$this->size;
        }
        return $this->unmaskNull($oldValue);
    }
    /**
     * Removes all mappings from this map.
     */
    public function clear() : void
    {
        $this->values = \array_fill(0, \count($this->keyUniverse), null);
        $this->size = 0;
    }
    /**
     * Compares the specified map with this map for quality.
     *
     * Returns true if the two maps represent the same mappings.
     */
    public function equals(self $other) : bool
    {
        if ($this === $other) {
            return \true;
        }
        if ($this->size !== $other->size) {
            return \false;
        }
        return $this->values === $other->values;
    }
    /**
     * Returns the values contained in this map.
     *
     * The array will contain the values in the order their corresponding keys appear in the map, which is their natural
     * order (the order in which the num constants are declared).
     */
    public function values() : array
    {
        return \array_values(\array_map(function ($value) {
            return $this->unmaskNull($value);
        }, \array_filter($this->values, function ($value) : bool {
            return null !== $value;
        })));
    }
    public function serialize() : string
    {
        return \serialize($this->__serialize());
    }
    public function unserialize($serialized) : void
    {
        $data = \unserialize($serialized);
        $this->__construct($data['keyType'], $data['valueType'], $data['allowNullValues']);
        foreach ($this->keyUniverse as $key) {
            if (\array_key_exists($key->ordinal(), $data['values'])) {
                $this->put($key, $data['values'][$key->ordinal()]);
            }
        }
    }
    public function getIterator() : Traversable
    {
        foreach ($this->keyUniverse as $key) {
            if (null === $this->values[$key->ordinal()]) {
                continue;
            }
            (yield $key => $this->unmaskNull($this->values[$key->ordinal()]));
        }
    }
    private function maskNull($value)
    {
        if (null === $value) {
            return NullValue::instance();
        }
        return $value;
    }
    private function unmaskNull($value)
    {
        if ($value instanceof NullValue) {
            return null;
        }
        return $value;
    }
    /**
     * @throws IllegalArgumentException when the passed key does not match the internal key type
     */
    private function checkKeyType(AbstractEnum $key) : void
    {
        if (\get_class($key) !== $this->keyType) {
            throw new IllegalArgumentException(\sprintf('Object of type %s is not the same type as %s', \get_class($key), $this->keyType));
        }
    }
    private function isValidValue($value) : bool
    {
        if (null === $value) {
            if ($this->allowNullValues) {
                return \true;
            }
            return \false;
        }
        switch ($this->valueType) {
            case 'mixed':
                return \true;
            case 'bool':
            case 'boolean':
                return \is_bool($value);
            case 'int':
            case 'integer':
                return \is_int($value);
            case 'float':
            case 'double':
                return \is_float($value);
            case 'string':
                return \is_string($value);
            case 'object':
                return \is_object($value);
            case 'array':
                return \is_array($value);
        }
        return $value instanceof $this->valueType;
    }
}
vendor/dasprid/enum/src/AbstractEnum.php000064400000015320150755130600014326 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\DASPRiD\Enum;

use WP2FA_Vendor\DASPRiD\Enum\Exception\CloneNotSupportedException;
use WP2FA_Vendor\DASPRiD\Enum\Exception\IllegalArgumentException;
use WP2FA_Vendor\DASPRiD\Enum\Exception\MismatchException;
use WP2FA_Vendor\DASPRiD\Enum\Exception\SerializeNotSupportedException;
use WP2FA_Vendor\DASPRiD\Enum\Exception\UnserializeNotSupportedException;
use ReflectionClass;
abstract class AbstractEnum
{
    /**
     * @var string
     */
    private $name;
    /**
     * @var int
     */
    private $ordinal;
    /**
     * @var array<string, array<string, static>>
     */
    private static $values = [];
    /**
     * @var array<string, bool>
     */
    private static $allValuesLoaded = [];
    /**
     * @var array<string, array>
     */
    private static $constants = [];
    /**
     * The constructor is private by default to avoid arbitrary enum creation.
     *
     * When creating your own constructor for a parameterized enum, make sure to declare it as protected, so that
     * the static methods are able to construct it. Avoid making it public, as that would allow creation of
     * non-singleton enum instances.
     */
    private function __construct()
    {
    }
    /**
     * Magic getter which forwards all calls to {@see self::valueOf()}.
     *
     * @return static
     */
    public static final function __callStatic(string $name, array $arguments) : self
    {
        return static::valueOf($name);
    }
    /**
     * Returns an enum with the specified name.
     *
     * The name must match exactly an identifier used to declare an enum in this type (extraneous whitespace characters
     * are not permitted).
     *
     * @return static
     * @throws IllegalArgumentException if the enum has no constant with the specified name
     */
    public static final function valueOf(string $name) : self
    {
        if (isset(self::$values[static::class][$name])) {
            return self::$values[static::class][$name];
        }
        $constants = self::constants();
        if (\array_key_exists($name, $constants)) {
            return self::createValue($name, $constants[$name][0], $constants[$name][1]);
        }
        throw new IllegalArgumentException(\sprintf('No enum constant %s::%s', static::class, $name));
    }
    /**
     * @return static
     */
    private static function createValue(string $name, int $ordinal, array $arguments) : self
    {
        $instance = new static(...$arguments);
        $instance->name = $name;
        $instance->ordinal = $ordinal;
        self::$values[static::class][$name] = $instance;
        return $instance;
    }
    /**
     * Obtains all possible types defined by this enum.
     *
     * @return static[]
     */
    public static final function values() : array
    {
        if (isset(self::$allValuesLoaded[static::class])) {
            return self::$values[static::class];
        }
        if (!isset(self::$values[static::class])) {
            self::$values[static::class] = [];
        }
        foreach (self::constants() as $name => $constant) {
            if (\array_key_exists($name, self::$values[static::class])) {
                continue;
            }
            static::createValue($name, $constant[0], $constant[1]);
        }
        \uasort(self::$values[static::class], function (self $a, self $b) {
            return $a->ordinal() <=> $b->ordinal();
        });
        self::$allValuesLoaded[static::class] = \true;
        return self::$values[static::class];
    }
    private static function constants() : array
    {
        if (isset(self::$constants[static::class])) {
            return self::$constants[static::class];
        }
        self::$constants[static::class] = [];
        $reflectionClass = new ReflectionClass(static::class);
        $ordinal = -1;
        foreach ($reflectionClass->getReflectionConstants() as $reflectionConstant) {
            if (!$reflectionConstant->isProtected()) {
                continue;
            }
            $value = $reflectionConstant->getValue();
            self::$constants[static::class][$reflectionConstant->name] = [++$ordinal, \is_array($value) ? $value : []];
        }
        return self::$constants[static::class];
    }
    /**
     * Returns the name of this enum constant, exactly as declared in its enum declaration.
     *
     * Most programmers should use the {@see self::__toString()} method in preference to this one, as the toString
     * method may return a more user-friendly name. This method is designed primarily for use in specialized situations
     * where correctness depends on getting the exact name, which will not vary from release to release.
     */
    public final function name() : string
    {
        return $this->name;
    }
    /**
     * Returns the ordinal of this enumeration constant (its position in its enum declaration, where the initial
     * constant is assigned an ordinal of zero).
     *
     * Most programmers will have no use for this method. It is designed for use by sophisticated enum-based data
     * structures.
     */
    public final function ordinal() : int
    {
        return $this->ordinal;
    }
    /**
     * Compares this enum with the specified object for order.
     *
     * Returns negative integer, zero or positive integer as this object is less than, equal to or greater than the
     * specified object.
     *
     * Enums are only comparable to other enums of the same type. The natural order implemented by this method is the
     * order in which the constants are declared.
     *
     * @throws MismatchException if the passed enum is not of the same type
     */
    public final function compareTo(self $other) : int
    {
        if (!$other instanceof static) {
            throw new MismatchException(\sprintf('The passed enum %s is not of the same type as %s', \get_class($other), static::class));
        }
        return $this->ordinal - $other->ordinal;
    }
    /**
     * Forbid cloning enums.
     *
     * @throws CloneNotSupportedException
     */
    public final function __clone()
    {
        throw new CloneNotSupportedException();
    }
    /**
     * Forbid serializing enums.
     *
     * @throws SerializeNotSupportedException
     */
    public final function __sleep() : array
    {
        throw new SerializeNotSupportedException();
    }
    /**
     * Forbid unserializing enums.
     *
     * @throws UnserializeNotSupportedException
     */
    public final function __wakeup() : void
    {
        throw new UnserializeNotSupportedException();
    }
    /**
     * Turns the enum into a string representation.
     *
     * You may override this method to give a more user-friendly version.
     */
    public function __toString() : string
    {
        return $this->name;
    }
}
vendor/bacon/bacon-qr-code/src/Exception/WriterException.php000064400000000250150755130600020125 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Exception;

final class WriterException extends \RuntimeException implements ExceptionInterface
{
}
vendor/bacon/bacon-qr-code/src/Exception/RuntimeException.php000064400000000251150755130600020275 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Exception;

final class RuntimeException extends \RuntimeException implements ExceptionInterface
{
}
vendor/bacon/bacon-qr-code/src/Exception/OutOfBoundsException.php000064400000000261150755130600021062 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Exception;

final class OutOfBoundsException extends \OutOfBoundsException implements ExceptionInterface
{
}
vendor/bacon/bacon-qr-code/src/Exception/ExceptionInterface.php000064400000000222150755130600020550 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Exception;

use Throwable;
interface ExceptionInterface extends Throwable
{
}
vendor/bacon/bacon-qr-code/src/Exception/UnexpectedValueException.php000064400000000271150755130600021755 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Exception;

final class UnexpectedValueException extends \UnexpectedValueException implements ExceptionInterface
{
}
vendor/bacon/bacon-qr-code/src/Exception/InvalidArgumentException.php000064400000000271150755130600021745 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Exception;

final class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
{
}
vendor/bacon/bacon-qr-code/src/Renderer/RendererInterface.php000064400000000321150755130600020170 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Renderer;

use WP2FA_Vendor\BaconQrCode\Encoder\QrCode;
interface RendererInterface
{
    public function render(QrCode $qrCode) : string;
}
vendor/bacon/bacon-qr-code/src/Renderer/PlainTextRenderer.php000064400000004207150755130600020207 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Renderer;

use WP2FA_Vendor\BaconQrCode\Encoder\QrCode;
use WP2FA_Vendor\BaconQrCode\Exception\InvalidArgumentException;
final class PlainTextRenderer implements RendererInterface
{
    /**
     * UTF-8 full block (U+2588)
     */
    private const FULL_BLOCK = "█";
    /**
     * UTF-8 upper half block (U+2580)
     */
    private const UPPER_HALF_BLOCK = "▀";
    /**
     * UTF-8 lower half block (U+2584)
     */
    private const LOWER_HALF_BLOCK = "▄";
    /**
     * UTF-8 no-break space (U+00A0)
     */
    private const EMPTY_BLOCK = " ";
    /**
     * @var int
     */
    private $margin;
    public function __construct(int $margin = 2)
    {
        $this->margin = $margin;
    }
    /**
     * @throws InvalidArgumentException if matrix width doesn't match height
     */
    public function render(QrCode $qrCode) : string
    {
        $matrix = $qrCode->getMatrix();
        $matrixSize = $matrix->getWidth();
        if ($matrixSize !== $matrix->getHeight()) {
            throw new InvalidArgumentException('Matrix must have the same width and height');
        }
        $rows = $matrix->getArray()->toArray();
        if (0 !== $matrixSize % 2) {
            $rows[] = \array_fill(0, $matrixSize, 0);
        }
        $horizontalMargin = \str_repeat(self::EMPTY_BLOCK, $this->margin);
        $result = \str_repeat("\n", (int) \ceil($this->margin / 2));
        for ($i = 0; $i < $matrixSize; $i += 2) {
            $result .= $horizontalMargin;
            $upperRow = $rows[$i];
            $lowerRow = $rows[$i + 1];
            for ($j = 0; $j < $matrixSize; ++$j) {
                $upperBit = $upperRow[$j];
                $lowerBit = $lowerRow[$j];
                if ($upperBit) {
                    $result .= $lowerBit ? self::FULL_BLOCK : self::UPPER_HALF_BLOCK;
                } else {
                    $result .= $lowerBit ? self::LOWER_HALF_BLOCK : self::EMPTY_BLOCK;
                }
            }
            $result .= $horizontalMargin . "\n";
        }
        $result .= \str_repeat("\n", (int) \ceil($this->margin / 2));
        return $result;
    }
}
vendor/bacon/bacon-qr-code/src/Renderer/ImageRenderer.php000064400000010170150755130600017315 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Renderer;

use WP2FA_Vendor\BaconQrCode\Encoder\MatrixUtil;
use WP2FA_Vendor\BaconQrCode\Encoder\QrCode;
use WP2FA_Vendor\BaconQrCode\Exception\InvalidArgumentException;
use WP2FA_Vendor\BaconQrCode\Renderer\Image\ImageBackEndInterface;
use WP2FA_Vendor\BaconQrCode\Renderer\Path\Path;
use WP2FA_Vendor\BaconQrCode\Renderer\RendererStyle\EyeFill;
use WP2FA_Vendor\BaconQrCode\Renderer\RendererStyle\RendererStyle;
final class ImageRenderer implements RendererInterface
{
    /**
     * @var RendererStyle
     */
    private $rendererStyle;
    /**
     * @var ImageBackEndInterface
     */
    private $imageBackEnd;
    public function __construct(RendererStyle $rendererStyle, ImageBackEndInterface $imageBackEnd)
    {
        $this->rendererStyle = $rendererStyle;
        $this->imageBackEnd = $imageBackEnd;
    }
    /**
     * @throws InvalidArgumentException if matrix width doesn't match height
     */
    public function render(QrCode $qrCode) : string
    {
        $size = $this->rendererStyle->getSize();
        $margin = $this->rendererStyle->getMargin();
        $matrix = $qrCode->getMatrix();
        $matrixSize = $matrix->getWidth();
        if ($matrixSize !== $matrix->getHeight()) {
            throw new InvalidArgumentException('Matrix must have the same width and height');
        }
        $totalSize = $matrixSize + $margin * 2;
        $moduleSize = $size / $totalSize;
        $fill = $this->rendererStyle->getFill();
        $this->imageBackEnd->new($size, $fill->getBackgroundColor());
        $this->imageBackEnd->scale((float) $moduleSize);
        $this->imageBackEnd->translate((float) $margin, (float) $margin);
        $module = $this->rendererStyle->getModule();
        $moduleMatrix = clone $matrix;
        MatrixUtil::removePositionDetectionPatterns($moduleMatrix);
        $modulePath = $this->drawEyes($matrixSize, $module->createPath($moduleMatrix));
        if ($fill->hasGradientFill()) {
            $this->imageBackEnd->drawPathWithGradient($modulePath, $fill->getForegroundGradient(), 0, 0, $matrixSize, $matrixSize);
        } else {
            $this->imageBackEnd->drawPathWithColor($modulePath, $fill->getForegroundColor());
        }
        return $this->imageBackEnd->done();
    }
    private function drawEyes(int $matrixSize, Path $modulePath) : Path
    {
        $fill = $this->rendererStyle->getFill();
        $eye = $this->rendererStyle->getEye();
        $externalPath = $eye->getExternalPath();
        $internalPath = $eye->getInternalPath();
        $modulePath = $this->drawEye($externalPath, $internalPath, $fill->getTopLeftEyeFill(), 3.5, 3.5, 0, $modulePath);
        $modulePath = $this->drawEye($externalPath, $internalPath, $fill->getTopRightEyeFill(), $matrixSize - 3.5, 3.5, 90, $modulePath);
        $modulePath = $this->drawEye($externalPath, $internalPath, $fill->getBottomLeftEyeFill(), 3.5, $matrixSize - 3.5, -90, $modulePath);
        return $modulePath;
    }
    private function drawEye(Path $externalPath, Path $internalPath, EyeFill $fill, float $xTranslation, float $yTranslation, int $rotation, Path $modulePath) : Path
    {
        if ($fill->inheritsBothColors()) {
            return $modulePath->append($externalPath->translate($xTranslation, $yTranslation))->append($internalPath->translate($xTranslation, $yTranslation));
        }
        $this->imageBackEnd->push();
        $this->imageBackEnd->translate($xTranslation, $yTranslation);
        if (0 !== $rotation) {
            $this->imageBackEnd->rotate($rotation);
        }
        if ($fill->inheritsExternalColor()) {
            $modulePath = $modulePath->append($externalPath->translate($xTranslation, $yTranslation));
        } else {
            $this->imageBackEnd->drawPathWithColor($externalPath, $fill->getExternalColor());
        }
        if ($fill->inheritsInternalColor()) {
            $modulePath = $modulePath->append($internalPath->translate($xTranslation, $yTranslation));
        } else {
            $this->imageBackEnd->drawPathWithColor($internalPath, $fill->getInternalColor());
        }
        $this->imageBackEnd->pop();
        return $modulePath;
    }
}
vendor/bacon/bacon-qr-code/src/Renderer/Path/Close.php000064400000001003150755130600016540 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Renderer\Path;

final class Close implements OperationInterface
{
    /**
     * @var self|null
     */
    private static $instance;
    private function __construct()
    {
    }
    public static function instance() : self
    {
        return self::$instance ?: (self::$instance = new self());
    }
    /**
     * @return self
     */
    public function translate(float $x, float $y) : OperationInterface
    {
        return $this;
    }
}
vendor/bacon/bacon-qr-code/src/Renderer/Path/Curve.php000064400000002552150755130600016571 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Renderer\Path;

final class Curve implements OperationInterface
{
    /**
     * @var float
     */
    private $x1;
    /**
     * @var float
     */
    private $y1;
    /**
     * @var float
     */
    private $x2;
    /**
     * @var float
     */
    private $y2;
    /**
     * @var float
     */
    private $x3;
    /**
     * @var float
     */
    private $y3;
    public function __construct(float $x1, float $y1, float $x2, float $y2, float $x3, float $y3)
    {
        $this->x1 = $x1;
        $this->y1 = $y1;
        $this->x2 = $x2;
        $this->y2 = $y2;
        $this->x3 = $x3;
        $this->y3 = $y3;
    }
    public function getX1() : float
    {
        return $this->x1;
    }
    public function getY1() : float
    {
        return $this->y1;
    }
    public function getX2() : float
    {
        return $this->x2;
    }
    public function getY2() : float
    {
        return $this->y2;
    }
    public function getX3() : float
    {
        return $this->x3;
    }
    public function getY3() : float
    {
        return $this->y3;
    }
    /**
     * @return self
     */
    public function translate(float $x, float $y) : OperationInterface
    {
        return new self($this->x1 + $x, $this->y1 + $y, $this->x2 + $x, $this->y2 + $y, $this->x3 + $x, $this->y3 + $y);
    }
}
vendor/bacon/bacon-qr-code/src/Renderer/Path/OperationInterface.php000064400000000356150755130600021266 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Renderer\Path;

interface OperationInterface
{
    /**
     * Translates the operation's coordinates.
     */
    public function translate(float $x, float $y) : self;
}
vendor/bacon/bacon-qr-code/src/Renderer/Path/Path.php000064400000004556150755130600016407 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Renderer\Path;

use IteratorAggregate;
use Traversable;
/**
 * Internal Representation of a vector path.
 */
final class Path implements IteratorAggregate
{
    /**
     * @var OperationInterface[]
     */
    private $operations = [];
    /**
     * Moves the drawing operation to a certain position.
     */
    public function move(float $x, float $y) : self
    {
        $path = clone $this;
        $path->operations[] = new Move($x, $y);
        return $path;
    }
    /**
     * Draws a line from the current position to another position.
     */
    public function line(float $x, float $y) : self
    {
        $path = clone $this;
        $path->operations[] = new Line($x, $y);
        return $path;
    }
    /**
     * Draws an elliptic arc from the current position to another position.
     */
    public function ellipticArc(float $xRadius, float $yRadius, float $xAxisRotation, bool $largeArc, bool $sweep, float $x, float $y) : self
    {
        $path = clone $this;
        $path->operations[] = new EllipticArc($xRadius, $yRadius, $xAxisRotation, $largeArc, $sweep, $x, $y);
        return $path;
    }
    /**
     * Draws a curve from the current position to another position.
     */
    public function curve(float $x1, float $y1, float $x2, float $y2, float $x3, float $y3) : self
    {
        $path = clone $this;
        $path->operations[] = new Curve($x1, $y1, $x2, $y2, $x3, $y3);
        return $path;
    }
    /**
     * Closes a sub-path.
     */
    public function close() : self
    {
        $path = clone $this;
        $path->operations[] = Close::instance();
        return $path;
    }
    /**
     * Appends another path to this one.
     */
    public function append(self $other) : self
    {
        $path = clone $this;
        $path->operations = \array_merge($this->operations, $other->operations);
        return $path;
    }
    public function translate(float $x, float $y) : self
    {
        $path = new self();
        foreach ($this->operations as $operation) {
            $path->operations[] = $operation->translate($x, $y);
        }
        return $path;
    }
    /**
     * @return OperationInterface[]|Traversable
     */
    public function getIterator() : Traversable
    {
        foreach ($this->operations as $operation) {
            (yield $operation);
        }
    }
}
vendor/bacon/bacon-qr-code/src/Renderer/Path/Move.php000064400000001227150755130600016411 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Renderer\Path;

final class Move implements OperationInterface
{
    /**
     * @var float
     */
    private $x;
    /**
     * @var float
     */
    private $y;
    public function __construct(float $x, float $y)
    {
        $this->x = $x;
        $this->y = $y;
    }
    public function getX() : float
    {
        return $this->x;
    }
    public function getY() : float
    {
        return $this->y;
    }
    /**
     * @return self
     */
    public function translate(float $x, float $y) : OperationInterface
    {
        return new self($this->x + $x, $this->y + $y);
    }
}
vendor/bacon/bacon-qr-code/src/Renderer/Path/Line.php000064400000001227150755130600016372 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Renderer\Path;

final class Line implements OperationInterface
{
    /**
     * @var float
     */
    private $x;
    /**
     * @var float
     */
    private $y;
    public function __construct(float $x, float $y)
    {
        $this->x = $x;
        $this->y = $y;
    }
    public function getX() : float
    {
        return $this->x;
    }
    public function getY() : float
    {
        return $this->y;
    }
    /**
     * @return self
     */
    public function translate(float $x, float $y) : OperationInterface
    {
        return new self($this->x + $x, $this->y + $y);
    }
}
vendor/bacon/bacon-qr-code/src/Renderer/Path/EllipticArc.php000064400000014721150755130600017701 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Renderer\Path;

final class EllipticArc implements OperationInterface
{
    private const ZERO_TOLERANCE = 1.0E-5;
    /**
     * @var float
     */
    private $xRadius;
    /**
     * @var float
     */
    private $yRadius;
    /**
     * @var float
     */
    private $xAxisAngle;
    /**
     * @var bool
     */
    private $largeArc;
    /**
     * @var bool
     */
    private $sweep;
    /**
     * @var float
     */
    private $x;
    /**
     * @var float
     */
    private $y;
    public function __construct(float $xRadius, float $yRadius, float $xAxisAngle, bool $largeArc, bool $sweep, float $x, float $y)
    {
        $this->xRadius = \abs($xRadius);
        $this->yRadius = \abs($yRadius);
        $this->xAxisAngle = $xAxisAngle % 360;
        $this->largeArc = $largeArc;
        $this->sweep = $sweep;
        $this->x = $x;
        $this->y = $y;
    }
    public function getXRadius() : float
    {
        return $this->xRadius;
    }
    public function getYRadius() : float
    {
        return $this->yRadius;
    }
    public function getXAxisAngle() : float
    {
        return $this->xAxisAngle;
    }
    public function isLargeArc() : bool
    {
        return $this->largeArc;
    }
    public function isSweep() : bool
    {
        return $this->sweep;
    }
    public function getX() : float
    {
        return $this->x;
    }
    public function getY() : float
    {
        return $this->y;
    }
    /**
     * @return self
     */
    public function translate(float $x, float $y) : OperationInterface
    {
        return new self($this->xRadius, $this->yRadius, $this->xAxisAngle, $this->largeArc, $this->sweep, $this->x + $x, $this->y + $y);
    }
    /**
     * Converts the elliptic arc to multiple curves.
     *
     * Since not all image back ends support elliptic arcs, this method allows to convert the arc into multiple curves
     * resembling the same result.
     *
     * @see https://mortoray.com/2017/02/16/rendering-an-svg-elliptical-arc-as-bezier-curves/
     * @return array<Curve|Line>
     */
    public function toCurves(float $fromX, float $fromY) : array
    {
        if (\sqrt(($fromX - $this->x) ** 2 + ($fromY - $this->y) ** 2) < self::ZERO_TOLERANCE) {
            return [];
        }
        if ($this->xRadius < self::ZERO_TOLERANCE || $this->yRadius < self::ZERO_TOLERANCE) {
            return [new Line($this->x, $this->y)];
        }
        return $this->createCurves($fromX, $fromY);
    }
    /**
     * @return Curve[]
     */
    private function createCurves(float $fromX, float $fromY) : array
    {
        $xAngle = \deg2rad($this->xAxisAngle);
        list($centerX, $centerY, $radiusX, $radiusY, $startAngle, $deltaAngle) = $this->calculateCenterPointParameters($fromX, $fromY, $xAngle);
        $s = $startAngle;
        $e = $s + $deltaAngle;
        $sign = $e < $s ? -1 : 1;
        $remain = \abs($e - $s);
        $p1 = self::point($centerX, $centerY, $radiusX, $radiusY, $xAngle, $s);
        $curves = [];
        while ($remain > self::ZERO_TOLERANCE) {
            $step = \min($remain, \pi() / 2);
            $signStep = $step * $sign;
            $p2 = self::point($centerX, $centerY, $radiusX, $radiusY, $xAngle, $s + $signStep);
            $alphaT = \tan($signStep / 2);
            $alpha = \sin($signStep) * (\sqrt(4 + 3 * $alphaT ** 2) - 1) / 3;
            $d1 = self::derivative($radiusX, $radiusY, $xAngle, $s);
            $d2 = self::derivative($radiusX, $radiusY, $xAngle, $s + $signStep);
            $curves[] = new Curve($p1[0] + $alpha * $d1[0], $p1[1] + $alpha * $d1[1], $p2[0] - $alpha * $d2[0], $p2[1] - $alpha * $d2[1], $p2[0], $p2[1]);
            $s += $signStep;
            $remain -= $step;
            $p1 = $p2;
        }
        return $curves;
    }
    /**
     * @return float[]
     */
    private function calculateCenterPointParameters(float $fromX, float $fromY, float $xAngle)
    {
        $rX = $this->xRadius;
        $rY = $this->yRadius;
        // F.6.5.1
        $dx2 = ($fromX - $this->x) / 2;
        $dy2 = ($fromY - $this->y) / 2;
        $x1p = \cos($xAngle) * $dx2 + \sin($xAngle) * $dy2;
        $y1p = -\sin($xAngle) * $dx2 + \cos($xAngle) * $dy2;
        // F.6.5.2
        $rxs = $rX ** 2;
        $rys = $rY ** 2;
        $x1ps = $x1p ** 2;
        $y1ps = $y1p ** 2;
        $cr = $x1ps / $rxs + $y1ps / $rys;
        if ($cr > 1) {
            $s = \sqrt($cr);
            $rX *= $s;
            $rY *= $s;
            $rxs = $rX ** 2;
            $rys = $rY ** 2;
        }
        $dq = $rxs * $y1ps + $rys * $x1ps;
        $pq = ($rxs * $rys - $dq) / $dq;
        $q = \sqrt(\max(0, $pq));
        if ($this->largeArc === $this->sweep) {
            $q = -$q;
        }
        $cxp = $q * $rX * $y1p / $rY;
        $cyp = -$q * $rY * $x1p / $rX;
        // F.6.5.3
        $cx = \cos($xAngle) * $cxp - \sin($xAngle) * $cyp + ($fromX + $this->x) / 2;
        $cy = \sin($xAngle) * $cxp + \cos($xAngle) * $cyp + ($fromY + $this->y) / 2;
        // F.6.5.5
        $theta = self::angle(1, 0, ($x1p - $cxp) / $rX, ($y1p - $cyp) / $rY);
        // F.6.5.6
        $delta = self::angle(($x1p - $cxp) / $rX, ($y1p - $cyp) / $rY, (-$x1p - $cxp) / $rX, (-$y1p - $cyp) / $rY);
        $delta = \fmod($delta, \pi() * 2);
        if (!$this->sweep) {
            $delta -= 2 * \pi();
        }
        return [$cx, $cy, $rX, $rY, $theta, $delta];
    }
    private static function angle(float $ux, float $uy, float $vx, float $vy) : float
    {
        // F.6.5.4
        $dot = $ux * $vx + $uy * $vy;
        $length = \sqrt($ux ** 2 + $uy ** 2) * \sqrt($vx ** 2 + $vy ** 2);
        $angle = \acos(\min(1, \max(-1, $dot / $length)));
        if ($ux * $vy - $uy * $vx < 0) {
            return -$angle;
        }
        return $angle;
    }
    /**
     * @return float[]
     */
    private static function point(float $centerX, float $centerY, float $radiusX, float $radiusY, float $xAngle, float $angle) : array
    {
        return [$centerX + $radiusX * \cos($xAngle) * \cos($angle) - $radiusY * \sin($xAngle) * \sin($angle), $centerY + $radiusX * \sin($xAngle) * \cos($angle) + $radiusY * \cos($xAngle) * \sin($angle)];
    }
    /**
     * @return float[]
     */
    private static function derivative(float $radiusX, float $radiusY, float $xAngle, float $angle) : array
    {
        return [-$radiusX * \cos($xAngle) * \sin($angle) - $radiusY * \sin($xAngle) * \cos($angle), -$radiusX * \sin($xAngle) * \sin($angle) + $radiusY * \cos($xAngle) * \cos($angle)];
    }
}
vendor/bacon/bacon-qr-code/src/Renderer/Image/EpsImageBackEnd.php000064400000025113150755130600020533 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Renderer\Image;

use WP2FA_Vendor\BaconQrCode\Exception\RuntimeException;
use WP2FA_Vendor\BaconQrCode\Renderer\Color\Alpha;
use WP2FA_Vendor\BaconQrCode\Renderer\Color\Cmyk;
use WP2FA_Vendor\BaconQrCode\Renderer\Color\ColorInterface;
use WP2FA_Vendor\BaconQrCode\Renderer\Color\Gray;
use WP2FA_Vendor\BaconQrCode\Renderer\Color\Rgb;
use WP2FA_Vendor\BaconQrCode\Renderer\Path\Close;
use WP2FA_Vendor\BaconQrCode\Renderer\Path\Curve;
use WP2FA_Vendor\BaconQrCode\Renderer\Path\EllipticArc;
use WP2FA_Vendor\BaconQrCode\Renderer\Path\Line;
use WP2FA_Vendor\BaconQrCode\Renderer\Path\Move;
use WP2FA_Vendor\BaconQrCode\Renderer\Path\Path;
use WP2FA_Vendor\BaconQrCode\Renderer\RendererStyle\Gradient;
use WP2FA_Vendor\BaconQrCode\Renderer\RendererStyle\GradientType;
final class EpsImageBackEnd implements ImageBackEndInterface
{
    private const PRECISION = 3;
    /**
     * @var string|null
     */
    private $eps;
    public function new(int $size, ColorInterface $backgroundColor) : void
    {
        $this->eps = "%!PS-Adobe-3.0 EPSF-3.0\n" . "%%Creator: BaconQrCode\n" . \sprintf("%%%%BoundingBox: 0 0 %d %d \n", $size, $size) . "%%BeginProlog\n" . "save\n" . "50 dict begin\n" . "/q { gsave } bind def\n" . "/Q { grestore } bind def\n" . "/s { scale } bind def\n" . "/t { translate } bind def\n" . "/r { rotate } bind def\n" . "/n { newpath } bind def\n" . "/m { moveto } bind def\n" . "/l { lineto } bind def\n" . "/c { curveto } bind def\n" . "/z { closepath } bind def\n" . "/f { eofill } bind def\n" . "/rgb { setrgbcolor } bind def\n" . "/cmyk { setcmykcolor } bind def\n" . "/gray { setgray } bind def\n" . "%%EndProlog\n" . "1 -1 s\n" . \sprintf("0 -%d t\n", $size);
        if ($backgroundColor instanceof Alpha && 0 === $backgroundColor->getAlpha()) {
            return;
        }
        $this->eps .= \wordwrap('0 0 m' . \sprintf(' %s 0 l', (string) $size) . \sprintf(' %s %s l', (string) $size, (string) $size) . \sprintf(' 0 %s l', (string) $size) . ' z' . ' ' . $this->getColorSetString($backgroundColor) . " f\n", 75, "\n ");
    }
    public function scale(float $size) : void
    {
        if (null === $this->eps) {
            throw new RuntimeException('No image has been started');
        }
        $this->eps .= \sprintf("%1\$s %1\$s s\n", \round($size, self::PRECISION));
    }
    public function translate(float $x, float $y) : void
    {
        if (null === $this->eps) {
            throw new RuntimeException('No image has been started');
        }
        $this->eps .= \sprintf("%s %s t\n", \round($x, self::PRECISION), \round($y, self::PRECISION));
    }
    public function rotate(int $degrees) : void
    {
        if (null === $this->eps) {
            throw new RuntimeException('No image has been started');
        }
        $this->eps .= \sprintf("%d r\n", $degrees);
    }
    public function push() : void
    {
        if (null === $this->eps) {
            throw new RuntimeException('No image has been started');
        }
        $this->eps .= "q\n";
    }
    public function pop() : void
    {
        if (null === $this->eps) {
            throw new RuntimeException('No image has been started');
        }
        $this->eps .= "Q\n";
    }
    public function drawPathWithColor(Path $path, ColorInterface $color) : void
    {
        if (null === $this->eps) {
            throw new RuntimeException('No image has been started');
        }
        $fromX = 0;
        $fromY = 0;
        $this->eps .= \wordwrap('n ' . $this->drawPathOperations($path, $fromX, $fromY) . ' ' . $this->getColorSetString($color) . " f\n", 75, "\n ");
    }
    public function drawPathWithGradient(Path $path, Gradient $gradient, float $x, float $y, float $width, float $height) : void
    {
        if (null === $this->eps) {
            throw new RuntimeException('No image has been started');
        }
        $fromX = 0;
        $fromY = 0;
        $this->eps .= \wordwrap('q n ' . $this->drawPathOperations($path, $fromX, $fromY) . "\n", 75, "\n ");
        $this->createGradientFill($gradient, $x, $y, $width, $height);
    }
    public function done() : string
    {
        if (null === $this->eps) {
            throw new RuntimeException('No image has been started');
        }
        $this->eps .= "%%TRAILER\nend restore\n%%EOF";
        $blob = $this->eps;
        $this->eps = null;
        return $blob;
    }
    private function drawPathOperations(iterable $ops, &$fromX, &$fromY) : string
    {
        $pathData = [];
        foreach ($ops as $op) {
            switch (\true) {
                case $op instanceof Move:
                    $fromX = $toX = \round($op->getX(), self::PRECISION);
                    $fromY = $toY = \round($op->getY(), self::PRECISION);
                    $pathData[] = \sprintf('%s %s m', $toX, $toY);
                    break;
                case $op instanceof Line:
                    $fromX = $toX = \round($op->getX(), self::PRECISION);
                    $fromY = $toY = \round($op->getY(), self::PRECISION);
                    $pathData[] = \sprintf('%s %s l', $toX, $toY);
                    break;
                case $op instanceof EllipticArc:
                    $pathData[] = $this->drawPathOperations($op->toCurves($fromX, $fromY), $fromX, $fromY);
                    break;
                case $op instanceof Curve:
                    $x1 = \round($op->getX1(), self::PRECISION);
                    $y1 = \round($op->getY1(), self::PRECISION);
                    $x2 = \round($op->getX2(), self::PRECISION);
                    $y2 = \round($op->getY2(), self::PRECISION);
                    $fromX = $x3 = \round($op->getX3(), self::PRECISION);
                    $fromY = $y3 = \round($op->getY3(), self::PRECISION);
                    $pathData[] = \sprintf('%s %s %s %s %s %s c', $x1, $y1, $x2, $y2, $x3, $y3);
                    break;
                case $op instanceof Close:
                    $pathData[] = 'z';
                    break;
                default:
                    throw new RuntimeException('Unexpected draw operation: ' . \get_class($op));
            }
        }
        return \implode(' ', $pathData);
    }
    private function createGradientFill(Gradient $gradient, float $x, float $y, float $width, float $height) : void
    {
        $startColor = $gradient->getStartColor();
        $endColor = $gradient->getEndColor();
        if ($startColor instanceof Alpha) {
            $startColor = $startColor->getBaseColor();
        }
        $startColorType = \get_class($startColor);
        if (!\in_array($startColorType, [Rgb::class, Cmyk::class, Gray::class])) {
            $startColorType = Cmyk::class;
            $startColor = $startColor->toCmyk();
        }
        if (\get_class($endColor) !== $startColorType) {
            switch ($startColorType) {
                case Cmyk::class:
                    $endColor = $endColor->toCmyk();
                    break;
                case Rgb::class:
                    $endColor = $endColor->toRgb();
                    break;
                case Gray::class:
                    $endColor = $endColor->toGray();
                    break;
            }
        }
        $this->eps .= "eoclip\n<<\n";
        if ($gradient->getType() === GradientType::RADIAL()) {
            $this->eps .= " /ShadingType 3\n";
        } else {
            $this->eps .= " /ShadingType 2\n";
        }
        $this->eps .= " /Extend [ true true ]\n" . " /AntiAlias true\n";
        switch ($startColorType) {
            case Cmyk::class:
                $this->eps .= " /ColorSpace /DeviceCMYK\n";
                break;
            case Rgb::class:
                $this->eps .= " /ColorSpace /DeviceRGB\n";
                break;
            case Gray::class:
                $this->eps .= " /ColorSpace /DeviceGray\n";
                break;
        }
        switch ($gradient->getType()) {
            case GradientType::HORIZONTAL():
                $this->eps .= \sprintf(" /Coords [ %s %s %s %s ]\n", \round($x, self::PRECISION), \round($y, self::PRECISION), \round($x + $width, self::PRECISION), \round($y, self::PRECISION));
                break;
            case GradientType::VERTICAL():
                $this->eps .= \sprintf(" /Coords [ %s %s %s %s ]\n", \round($x, self::PRECISION), \round($y, self::PRECISION), \round($x, self::PRECISION), \round($y + $height, self::PRECISION));
                break;
            case GradientType::DIAGONAL():
                $this->eps .= \sprintf(" /Coords [ %s %s %s %s ]\n", \round($x, self::PRECISION), \round($y, self::PRECISION), \round($x + $width, self::PRECISION), \round($y + $height, self::PRECISION));
                break;
            case GradientType::INVERSE_DIAGONAL():
                $this->eps .= \sprintf(" /Coords [ %s %s %s %s ]\n", \round($x, self::PRECISION), \round($y + $height, self::PRECISION), \round($x + $width, self::PRECISION), \round($y, self::PRECISION));
                break;
            case GradientType::RADIAL():
                $centerX = ($x + $width) / 2;
                $centerY = ($y + $height) / 2;
                $this->eps .= \sprintf(" /Coords [ %s %s 0 %s %s %s ]\n", \round($centerX, self::PRECISION), \round($centerY, self::PRECISION), \round($centerX, self::PRECISION), \round($centerY, self::PRECISION), \round(\max($width, $height) / 2, self::PRECISION));
                break;
        }
        $this->eps .= " /Function\n" . " <<\n" . "  /FunctionType 2\n" . "  /Domain [ 0 1 ]\n" . \sprintf("  /C0 [ %s ]\n", $this->getColorString($startColor)) . \sprintf("  /C1 [ %s ]\n", $this->getColorString($endColor)) . "  /N 1\n" . " >>\n>>\nshfill\nQ\n";
    }
    private function getColorSetString(ColorInterface $color) : string
    {
        if ($color instanceof Rgb) {
            return $this->getColorString($color) . ' rgb';
        }
        if ($color instanceof Cmyk) {
            return $this->getColorString($color) . ' cmyk';
        }
        if ($color instanceof Gray) {
            return $this->getColorString($color) . ' gray';
        }
        return $this->getColorSetString($color->toCmyk());
    }
    private function getColorString(ColorInterface $color) : string
    {
        if ($color instanceof Rgb) {
            return \sprintf('%s %s %s', $color->getRed() / 255, $color->getGreen() / 255, $color->getBlue() / 255);
        }
        if ($color instanceof Cmyk) {
            return \sprintf('%s %s %s %s', $color->getCyan() / 100, $color->getMagenta() / 100, $color->getYellow() / 100, $color->getBlack() / 100);
        }
        if ($color instanceof Gray) {
            return \sprintf('%s', $color->getGray() / 100);
        }
        return $this->getColorString($color->toCmyk());
    }
}
vendor/bacon/bacon-qr-code/src/Renderer/Image/ImageBackEndInterface.php000064400000004725150755130600021712 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Renderer\Image;

use WP2FA_Vendor\BaconQrCode\Exception\RuntimeException;
use WP2FA_Vendor\BaconQrCode\Renderer\Color\ColorInterface;
use WP2FA_Vendor\BaconQrCode\Renderer\Path\Path;
use WP2FA_Vendor\BaconQrCode\Renderer\RendererStyle\Gradient;
/**
 * Interface for back ends able to to produce path based images.
 */
interface ImageBackEndInterface
{
    /**
     * Starts a new image.
     *
     * If a previous image was already started, previous data get erased.
     */
    public function new(int $size, ColorInterface $backgroundColor) : void;
    /**
     * Transforms all following drawing operation coordinates by scaling them by a given factor.
     *
     * @throws RuntimeException if no image was started yet.
     */
    public function scale(float $size) : void;
    /**
     * Transforms all following drawing operation coordinates by translating them by a given amount.
     *
     * @throws RuntimeException if no image was started yet.
     */
    public function translate(float $x, float $y) : void;
    /**
     * Transforms all following drawing operation coordinates by rotating them by a given amount.
     *
     * @throws RuntimeException if no image was started yet.
     */
    public function rotate(int $degrees) : void;
    /**
     * Pushes the current coordinate transformation onto a stack.
     *
     * @throws RuntimeException if no image was started yet.
     */
    public function push() : void;
    /**
     * Pops the last coordinate transformation from a stack.
     *
     * @throws RuntimeException if no image was started yet.
     */
    public function pop() : void;
    /**
     * Draws a path with a given color.
     *
     * @throws RuntimeException if no image was started yet.
     */
    public function drawPathWithColor(Path $path, ColorInterface $color) : void;
    /**
     * Draws a path with a given gradient which spans the box described by the position and size.
     *
     * @throws RuntimeException if no image was started yet.
     */
    public function drawPathWithGradient(Path $path, Gradient $gradient, float $x, float $y, float $width, float $height) : void;
    /**
     * Ends the image drawing operation and returns the resulting blob.
     *
     * This should reset the state of the back end and thus this method should only be callable once per image.
     *
     * @throws RuntimeException if no image was started yet.
     */
    public function done() : string;
}
vendor/bacon/bacon-qr-code/src/Renderer/Image/ImagickImageBackEnd.php000064400000022261150755130600021351 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Renderer\Image;

use WP2FA_Vendor\BaconQrCode\Exception\RuntimeException;
use WP2FA_Vendor\BaconQrCode\Renderer\Color\Alpha;
use WP2FA_Vendor\BaconQrCode\Renderer\Color\Cmyk;
use WP2FA_Vendor\BaconQrCode\Renderer\Color\ColorInterface;
use WP2FA_Vendor\BaconQrCode\Renderer\Color\Gray;
use WP2FA_Vendor\BaconQrCode\Renderer\Color\Rgb;
use WP2FA_Vendor\BaconQrCode\Renderer\Path\Close;
use WP2FA_Vendor\BaconQrCode\Renderer\Path\Curve;
use WP2FA_Vendor\BaconQrCode\Renderer\Path\EllipticArc;
use WP2FA_Vendor\BaconQrCode\Renderer\Path\Line;
use WP2FA_Vendor\BaconQrCode\Renderer\Path\Move;
use WP2FA_Vendor\BaconQrCode\Renderer\Path\Path;
use WP2FA_Vendor\BaconQrCode\Renderer\RendererStyle\Gradient;
use WP2FA_Vendor\BaconQrCode\Renderer\RendererStyle\GradientType;
use Imagick;
use ImagickDraw;
use ImagickPixel;
final class ImagickImageBackEnd implements ImageBackEndInterface
{
    /**
     * @var string
     */
    private $imageFormat;
    /**
     * @var int
     */
    private $compressionQuality;
    /**
     * @var Imagick|null
     */
    private $image;
    /**
     * @var ImagickDraw|null
     */
    private $draw;
    /**
     * @var int|null
     */
    private $gradientCount;
    /**
     * @var TransformationMatrix[]|null
     */
    private $matrices;
    /**
     * @var int|null
     */
    private $matrixIndex;
    public function __construct(string $imageFormat = 'png', int $compressionQuality = 100)
    {
        if (!\class_exists(Imagick::class)) {
            throw new RuntimeException('You need to install the imagick extension to use this back end');
        }
        $this->imageFormat = $imageFormat;
        $this->compressionQuality = $compressionQuality;
    }
    public function new(int $size, ColorInterface $backgroundColor) : void
    {
        $this->image = new Imagick();
        $this->image->newImage($size, $size, $this->getColorPixel($backgroundColor));
        $this->image->setImageFormat($this->imageFormat);
        $this->image->setCompressionQuality($this->compressionQuality);
        $this->draw = new ImagickDraw();
        $this->gradientCount = 0;
        $this->matrices = [new TransformationMatrix()];
        $this->matrixIndex = 0;
    }
    public function scale(float $size) : void
    {
        if (null === $this->draw) {
            throw new RuntimeException('No image has been started');
        }
        $this->draw->scale($size, $size);
        $this->matrices[$this->matrixIndex] = $this->matrices[$this->matrixIndex]->multiply(TransformationMatrix::scale($size));
    }
    public function translate(float $x, float $y) : void
    {
        if (null === $this->draw) {
            throw new RuntimeException('No image has been started');
        }
        $this->draw->translate($x, $y);
        $this->matrices[$this->matrixIndex] = $this->matrices[$this->matrixIndex]->multiply(TransformationMatrix::translate($x, $y));
    }
    public function rotate(int $degrees) : void
    {
        if (null === $this->draw) {
            throw new RuntimeException('No image has been started');
        }
        $this->draw->rotate($degrees);
        $this->matrices[$this->matrixIndex] = $this->matrices[$this->matrixIndex]->multiply(TransformationMatrix::rotate($degrees));
    }
    public function push() : void
    {
        if (null === $this->draw) {
            throw new RuntimeException('No image has been started');
        }
        $this->draw->push();
        $this->matrices[++$this->matrixIndex] = $this->matrices[$this->matrixIndex - 1];
    }
    public function pop() : void
    {
        if (null === $this->draw) {
            throw new RuntimeException('No image has been started');
        }
        $this->draw->pop();
        unset($this->matrices[$this->matrixIndex--]);
    }
    public function drawPathWithColor(Path $path, ColorInterface $color) : void
    {
        if (null === $this->draw) {
            throw new RuntimeException('No image has been started');
        }
        $this->draw->setFillColor($this->getColorPixel($color));
        $this->drawPath($path);
    }
    public function drawPathWithGradient(Path $path, Gradient $gradient, float $x, float $y, float $width, float $height) : void
    {
        if (null === $this->draw) {
            throw new RuntimeException('No image has been started');
        }
        $this->draw->setFillPatternURL('#' . $this->createGradientFill($gradient, $x, $y, $width, $height));
        $this->drawPath($path);
    }
    public function done() : string
    {
        if (null === $this->draw) {
            throw new RuntimeException('No image has been started');
        }
        $this->image->drawImage($this->draw);
        $blob = $this->image->getImageBlob();
        $this->draw->clear();
        $this->image->clear();
        $this->draw = null;
        $this->image = null;
        $this->gradientCount = null;
        return $blob;
    }
    private function drawPath(Path $path) : void
    {
        $this->draw->pathStart();
        foreach ($path as $op) {
            switch (\true) {
                case $op instanceof Move:
                    $this->draw->pathMoveToAbsolute($op->getX(), $op->getY());
                    break;
                case $op instanceof Line:
                    $this->draw->pathLineToAbsolute($op->getX(), $op->getY());
                    break;
                case $op instanceof EllipticArc:
                    $this->draw->pathEllipticArcAbsolute($op->getXRadius(), $op->getYRadius(), $op->getXAxisAngle(), $op->isLargeArc(), $op->isSweep(), $op->getX(), $op->getY());
                    break;
                case $op instanceof Curve:
                    $this->draw->pathCurveToAbsolute($op->getX1(), $op->getY1(), $op->getX2(), $op->getY2(), $op->getX3(), $op->getY3());
                    break;
                case $op instanceof Close:
                    $this->draw->pathClose();
                    break;
                default:
                    throw new RuntimeException('Unexpected draw operation: ' . \get_class($op));
            }
        }
        $this->draw->pathFinish();
    }
    private function createGradientFill(Gradient $gradient, float $x, float $y, float $width, float $height) : string
    {
        list($width, $height) = $this->matrices[$this->matrixIndex]->apply($width, $height);
        $startColor = $this->getColorPixel($gradient->getStartColor())->getColorAsString();
        $endColor = $this->getColorPixel($gradient->getEndColor())->getColorAsString();
        $gradientImage = new Imagick();
        switch ($gradient->getType()) {
            case GradientType::HORIZONTAL():
                $gradientImage->newPseudoImage((int) $height, (int) $width, \sprintf('gradient:%s-%s', $startColor, $endColor));
                $gradientImage->rotateImage('transparent', -90);
                break;
            case GradientType::VERTICAL():
                $gradientImage->newPseudoImage((int) $width, (int) $height, \sprintf('gradient:%s-%s', $startColor, $endColor));
                break;
            case GradientType::DIAGONAL():
            case GradientType::INVERSE_DIAGONAL():
                $gradientImage->newPseudoImage((int) ($width * \sqrt(2)), (int) ($height * \sqrt(2)), \sprintf('gradient:%s-%s', $startColor, $endColor));
                if (GradientType::DIAGONAL() === $gradient->getType()) {
                    $gradientImage->rotateImage('transparent', -45);
                } else {
                    $gradientImage->rotateImage('transparent', -135);
                }
                $rotatedWidth = $gradientImage->getImageWidth();
                $rotatedHeight = $gradientImage->getImageHeight();
                $gradientImage->setImagePage($rotatedWidth, $rotatedHeight, 0, 0);
                $gradientImage->cropImage(\intdiv($rotatedWidth, 2) - 2, \intdiv($rotatedHeight, 2) - 2, \intdiv($rotatedWidth, 4) + 1, \intdiv($rotatedWidth, 4) + 1);
                break;
            case GradientType::RADIAL():
                $gradientImage->newPseudoImage((int) $width, (int) $height, \sprintf('radial-gradient:%s-%s', $startColor, $endColor));
                break;
        }
        $id = \sprintf('g%d', ++$this->gradientCount);
        $this->draw->pushPattern($id, 0, 0, $width, $height);
        $this->draw->composite(Imagick::COMPOSITE_COPY, 0, 0, $width, $height, $gradientImage);
        $this->draw->popPattern();
        return $id;
    }
    private function getColorPixel(ColorInterface $color) : ImagickPixel
    {
        $alpha = 100;
        if ($color instanceof Alpha) {
            $alpha = $color->getAlpha();
            $color = $color->getBaseColor();
        }
        if ($color instanceof Rgb) {
            return new ImagickPixel(\sprintf('rgba(%d, %d, %d, %F)', $color->getRed(), $color->getGreen(), $color->getBlue(), $alpha / 100));
        }
        if ($color instanceof Cmyk) {
            return new ImagickPixel(\sprintf('cmyka(%d, %d, %d, %d, %F)', $color->getCyan(), $color->getMagenta(), $color->getYellow(), $color->getBlack(), $alpha / 100));
        }
        if ($color instanceof Gray) {
            return new ImagickPixel(\sprintf('graya(%d%%, %F)', $color->getGray(), $alpha / 100));
        }
        return $this->getColorPixel(new Alpha($alpha, $color->toRgb()));
    }
}
vendor/bacon/bacon-qr-code/src/Renderer/Image/TransformationMatrix.php000064400000003707150755130600022031 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Renderer\Image;

final class TransformationMatrix
{
    /**
     * @var float[]
     */
    private $values;
    public function __construct()
    {
        $this->values = [1, 0, 0, 1, 0, 0];
    }
    public function multiply(self $other) : self
    {
        $matrix = new self();
        $matrix->values[0] = $this->values[0] * $other->values[0] + $this->values[2] * $other->values[1];
        $matrix->values[1] = $this->values[1] * $other->values[0] + $this->values[3] * $other->values[1];
        $matrix->values[2] = $this->values[0] * $other->values[2] + $this->values[2] * $other->values[3];
        $matrix->values[3] = $this->values[1] * $other->values[2] + $this->values[3] * $other->values[3];
        $matrix->values[4] = $this->values[0] * $other->values[4] + $this->values[2] * $other->values[5] + $this->values[4];
        $matrix->values[5] = $this->values[1] * $other->values[4] + $this->values[3] * $other->values[5] + $this->values[5];
        return $matrix;
    }
    public static function scale(float $size) : self
    {
        $matrix = new self();
        $matrix->values = [$size, 0, 0, $size, 0, 0];
        return $matrix;
    }
    public static function translate(float $x, float $y) : self
    {
        $matrix = new self();
        $matrix->values = [1, 0, 0, 1, $x, $y];
        return $matrix;
    }
    public static function rotate(int $degrees) : self
    {
        $matrix = new self();
        $rad = \deg2rad($degrees);
        $matrix->values = [\cos($rad), \sin($rad), -\sin($rad), \cos($rad), 0, 0];
        return $matrix;
    }
    /**
     * Applies this matrix onto a point and returns the resulting viewport point.
     *
     * @return float[]
     */
    public function apply(float $x, float $y) : array
    {
        return [$x * $this->values[0] + $y * $this->values[2] + $this->values[4], $x * $this->values[1] + $y * $this->values[3] + $this->values[5]];
    }
}
vendor/bacon/bacon-qr-code/src/Renderer/Image/SvgImageBackEnd.php000064400000027342150755130600020551 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Renderer\Image;

use WP2FA_Vendor\BaconQrCode\Exception\RuntimeException;
use WP2FA_Vendor\BaconQrCode\Renderer\Color\Alpha;
use WP2FA_Vendor\BaconQrCode\Renderer\Color\ColorInterface;
use WP2FA_Vendor\BaconQrCode\Renderer\Path\Close;
use WP2FA_Vendor\BaconQrCode\Renderer\Path\Curve;
use WP2FA_Vendor\BaconQrCode\Renderer\Path\EllipticArc;
use WP2FA_Vendor\BaconQrCode\Renderer\Path\Line;
use WP2FA_Vendor\BaconQrCode\Renderer\Path\Move;
use WP2FA_Vendor\BaconQrCode\Renderer\Path\Path;
use WP2FA_Vendor\BaconQrCode\Renderer\RendererStyle\Gradient;
use WP2FA_Vendor\BaconQrCode\Renderer\RendererStyle\GradientType;
use XMLWriter;
final class SvgImageBackEnd implements ImageBackEndInterface
{
    private const PRECISION = 3;
    /**
     * @var XMLWriter|null
     */
    private $xmlWriter;
    /**
     * @var int[]|null
     */
    private $stack;
    /**
     * @var int|null
     */
    private $currentStack;
    /**
     * @var int|null
     */
    private $gradientCount;
    public function __construct()
    {
        if (!\class_exists(XMLWriter::class)) {
            throw new RuntimeException('You need to install the libxml extension to use this back end');
        }
    }
    public function new(int $size, ColorInterface $backgroundColor) : void
    {
        $this->xmlWriter = new XMLWriter();
        $this->xmlWriter->openMemory();
        $this->xmlWriter->startDocument('1.0', 'UTF-8');
        $this->xmlWriter->startElement('svg');
        $this->xmlWriter->writeAttribute('xmlns', 'http://www.w3.org/2000/svg');
        $this->xmlWriter->writeAttribute('version', '1.1');
        $this->xmlWriter->writeAttribute('width', (string) $size);
        $this->xmlWriter->writeAttribute('height', (string) $size);
        $this->xmlWriter->writeAttribute('viewBox', '0 0 ' . $size . ' ' . $size);
        $this->gradientCount = 0;
        $this->currentStack = 0;
        $this->stack[0] = 0;
        $alpha = 1;
        if ($backgroundColor instanceof Alpha) {
            $alpha = $backgroundColor->getAlpha() / 100;
        }
        if (0 === $alpha) {
            return;
        }
        $this->xmlWriter->startElement('rect');
        $this->xmlWriter->writeAttribute('x', '0');
        $this->xmlWriter->writeAttribute('y', '0');
        $this->xmlWriter->writeAttribute('width', (string) $size);
        $this->xmlWriter->writeAttribute('height', (string) $size);
        $this->xmlWriter->writeAttribute('fill', $this->getColorString($backgroundColor));
        if ($alpha < 1) {
            $this->xmlWriter->writeAttribute('fill-opacity', (string) $alpha);
        }
        $this->xmlWriter->endElement();
    }
    public function scale(float $size) : void
    {
        if (null === $this->xmlWriter) {
            throw new RuntimeException('No image has been started');
        }
        $this->xmlWriter->startElement('g');
        $this->xmlWriter->writeAttribute('transform', \sprintf('scale(%s)', \round($size, self::PRECISION)));
        ++$this->stack[$this->currentStack];
    }
    public function translate(float $x, float $y) : void
    {
        if (null === $this->xmlWriter) {
            throw new RuntimeException('No image has been started');
        }
        $this->xmlWriter->startElement('g');
        $this->xmlWriter->writeAttribute('transform', \sprintf('translate(%s,%s)', \round($x, self::PRECISION), \round($y, self::PRECISION)));
        ++$this->stack[$this->currentStack];
    }
    public function rotate(int $degrees) : void
    {
        if (null === $this->xmlWriter) {
            throw new RuntimeException('No image has been started');
        }
        $this->xmlWriter->startElement('g');
        $this->xmlWriter->writeAttribute('transform', \sprintf('rotate(%d)', $degrees));
        ++$this->stack[$this->currentStack];
    }
    public function push() : void
    {
        if (null === $this->xmlWriter) {
            throw new RuntimeException('No image has been started');
        }
        $this->xmlWriter->startElement('g');
        $this->stack[] = 1;
        ++$this->currentStack;
    }
    public function pop() : void
    {
        if (null === $this->xmlWriter) {
            throw new RuntimeException('No image has been started');
        }
        for ($i = 0; $i < $this->stack[$this->currentStack]; ++$i) {
            $this->xmlWriter->endElement();
        }
        \array_pop($this->stack);
        --$this->currentStack;
    }
    public function drawPathWithColor(Path $path, ColorInterface $color) : void
    {
        if (null === $this->xmlWriter) {
            throw new RuntimeException('No image has been started');
        }
        $alpha = 1;
        if ($color instanceof Alpha) {
            $alpha = $color->getAlpha() / 100;
        }
        $this->startPathElement($path);
        $this->xmlWriter->writeAttribute('fill', $this->getColorString($color));
        if ($alpha < 1) {
            $this->xmlWriter->writeAttribute('fill-opacity', (string) $alpha);
        }
        $this->xmlWriter->endElement();
    }
    public function drawPathWithGradient(Path $path, Gradient $gradient, float $x, float $y, float $width, float $height) : void
    {
        if (null === $this->xmlWriter) {
            throw new RuntimeException('No image has been started');
        }
        $gradientId = $this->createGradientFill($gradient, $x, $y, $width, $height);
        $this->startPathElement($path);
        $this->xmlWriter->writeAttribute('fill', 'url(#' . $gradientId . ')');
        $this->xmlWriter->endElement();
    }
    public function done() : string
    {
        if (null === $this->xmlWriter) {
            throw new RuntimeException('No image has been started');
        }
        foreach ($this->stack as $openElements) {
            for ($i = $openElements; $i > 0; --$i) {
                $this->xmlWriter->endElement();
            }
        }
        $this->xmlWriter->endDocument();
        $blob = $this->xmlWriter->outputMemory(\true);
        $this->xmlWriter = null;
        $this->stack = null;
        $this->currentStack = null;
        $this->gradientCount = null;
        return $blob;
    }
    private function startPathElement(Path $path) : void
    {
        $pathData = [];
        foreach ($path as $op) {
            switch (\true) {
                case $op instanceof Move:
                    $pathData[] = \sprintf('M%s %s', \round($op->getX(), self::PRECISION), \round($op->getY(), self::PRECISION));
                    break;
                case $op instanceof Line:
                    $pathData[] = \sprintf('L%s %s', \round($op->getX(), self::PRECISION), \round($op->getY(), self::PRECISION));
                    break;
                case $op instanceof EllipticArc:
                    $pathData[] = \sprintf('A%s %s %s %u %u %s %s', \round($op->getXRadius(), self::PRECISION), \round($op->getYRadius(), self::PRECISION), \round($op->getXAxisAngle(), self::PRECISION), $op->isLargeArc(), $op->isSweep(), \round($op->getX(), self::PRECISION), \round($op->getY(), self::PRECISION));
                    break;
                case $op instanceof Curve:
                    $pathData[] = \sprintf('C%s %s %s %s %s %s', \round($op->getX1(), self::PRECISION), \round($op->getY1(), self::PRECISION), \round($op->getX2(), self::PRECISION), \round($op->getY2(), self::PRECISION), \round($op->getX3(), self::PRECISION), \round($op->getY3(), self::PRECISION));
                    break;
                case $op instanceof Close:
                    $pathData[] = 'Z';
                    break;
                default:
                    throw new RuntimeException('Unexpected draw operation: ' . \get_class($op));
            }
        }
        $this->xmlWriter->startElement('path');
        $this->xmlWriter->writeAttribute('fill-rule', 'evenodd');
        $this->xmlWriter->writeAttribute('d', \implode('', $pathData));
    }
    private function createGradientFill(Gradient $gradient, float $x, float $y, float $width, float $height) : string
    {
        $this->xmlWriter->startElement('defs');
        $startColor = $gradient->getStartColor();
        $endColor = $gradient->getEndColor();
        if ($gradient->getType() === GradientType::RADIAL()) {
            $this->xmlWriter->startElement('radialGradient');
        } else {
            $this->xmlWriter->startElement('linearGradient');
        }
        $this->xmlWriter->writeAttribute('gradientUnits', 'userSpaceOnUse');
        switch ($gradient->getType()) {
            case GradientType::HORIZONTAL():
                $this->xmlWriter->writeAttribute('x1', (string) \round($x, self::PRECISION));
                $this->xmlWriter->writeAttribute('y1', (string) \round($y, self::PRECISION));
                $this->xmlWriter->writeAttribute('x2', (string) \round($x + $width, self::PRECISION));
                $this->xmlWriter->writeAttribute('y2', (string) \round($y, self::PRECISION));
                break;
            case GradientType::VERTICAL():
                $this->xmlWriter->writeAttribute('x1', (string) \round($x, self::PRECISION));
                $this->xmlWriter->writeAttribute('y1', (string) \round($y, self::PRECISION));
                $this->xmlWriter->writeAttribute('x2', (string) \round($x, self::PRECISION));
                $this->xmlWriter->writeAttribute('y2', (string) \round($y + $height, self::PRECISION));
                break;
            case GradientType::DIAGONAL():
                $this->xmlWriter->writeAttribute('x1', (string) \round($x, self::PRECISION));
                $this->xmlWriter->writeAttribute('y1', (string) \round($y, self::PRECISION));
                $this->xmlWriter->writeAttribute('x2', (string) \round($x + $width, self::PRECISION));
                $this->xmlWriter->writeAttribute('y2', (string) \round($y + $height, self::PRECISION));
                break;
            case GradientType::INVERSE_DIAGONAL():
                $this->xmlWriter->writeAttribute('x1', (string) \round($x, self::PRECISION));
                $this->xmlWriter->writeAttribute('y1', (string) \round($y + $height, self::PRECISION));
                $this->xmlWriter->writeAttribute('x2', (string) \round($x + $width, self::PRECISION));
                $this->xmlWriter->writeAttribute('y2', (string) \round($y, self::PRECISION));
                break;
            case GradientType::RADIAL():
                $this->xmlWriter->writeAttribute('cx', (string) \round(($x + $width) / 2, self::PRECISION));
                $this->xmlWriter->writeAttribute('cy', (string) \round(($y + $height) / 2, self::PRECISION));
                $this->xmlWriter->writeAttribute('r', (string) \round(\max($width, $height) / 2, self::PRECISION));
                break;
        }
        $id = \sprintf('g%d', ++$this->gradientCount);
        $this->xmlWriter->writeAttribute('id', $id);
        $this->xmlWriter->startElement('stop');
        $this->xmlWriter->writeAttribute('offset', '0%');
        $this->xmlWriter->writeAttribute('stop-color', $this->getColorString($startColor));
        if ($startColor instanceof Alpha) {
            $this->xmlWriter->writeAttribute('stop-opacity', (string) $startColor->getAlpha());
        }
        $this->xmlWriter->endElement();
        $this->xmlWriter->startElement('stop');
        $this->xmlWriter->writeAttribute('offset', '100%');
        $this->xmlWriter->writeAttribute('stop-color', $this->getColorString($endColor));
        if ($endColor instanceof Alpha) {
            $this->xmlWriter->writeAttribute('stop-opacity', (string) $endColor->getAlpha());
        }
        $this->xmlWriter->endElement();
        $this->xmlWriter->endElement();
        $this->xmlWriter->endElement();
        return $id;
    }
    private function getColorString(ColorInterface $color) : string
    {
        $color = $color->toRgb();
        return \sprintf('#%02x%02x%02x', $color->getRed(), $color->getGreen(), $color->getBlue());
    }
}
vendor/bacon/bacon-qr-code/src/Renderer/RendererStyle/Gradient.php000064400000001554150755130600021136 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Renderer\RendererStyle;

use WP2FA_Vendor\BaconQrCode\Renderer\Color\ColorInterface;
final class Gradient
{
    /**
     * @var ColorInterface
     */
    private $startColor;
    /**
     * @var ColorInterface
     */
    private $endColor;
    /**
     * @var GradientType
     */
    private $type;
    public function __construct(ColorInterface $startColor, ColorInterface $endColor, GradientType $type)
    {
        $this->startColor = $startColor;
        $this->endColor = $endColor;
        $this->type = $type;
    }
    public function getStartColor() : ColorInterface
    {
        return $this->startColor;
    }
    public function getEndColor() : ColorInterface
    {
        return $this->endColor;
    }
    public function getType() : GradientType
    {
        return $this->type;
    }
}
vendor/bacon/bacon-qr-code/src/Renderer/RendererStyle/RendererStyle.php000064400000003270150755130600022165 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Renderer\RendererStyle;

use WP2FA_Vendor\BaconQrCode\Renderer\Eye\EyeInterface;
use WP2FA_Vendor\BaconQrCode\Renderer\Eye\ModuleEye;
use WP2FA_Vendor\BaconQrCode\Renderer\Module\ModuleInterface;
use WP2FA_Vendor\BaconQrCode\Renderer\Module\SquareModule;
final class RendererStyle
{
    /**
     * @var int
     */
    private $size;
    /**
     * @var int
     */
    private $margin;
    /**
     * @var ModuleInterface
     */
    private $module;
    /**
     * @var EyeInterface|null
     */
    private $eye;
    /**
     * @var Fill
     */
    private $fill;
    public function __construct(int $size, int $margin = 4, ?ModuleInterface $module = null, ?EyeInterface $eye = null, ?Fill $fill = null)
    {
        $this->margin = $margin;
        $this->size = $size;
        $this->module = $module ?: SquareModule::instance();
        $this->eye = $eye ?: new ModuleEye($this->module);
        $this->fill = $fill ?: Fill::default();
    }
    public function withSize(int $size) : self
    {
        $style = clone $this;
        $style->size = $size;
        return $style;
    }
    public function withMargin(int $margin) : self
    {
        $style = clone $this;
        $style->margin = $margin;
        return $style;
    }
    public function getSize() : int
    {
        return $this->size;
    }
    public function getMargin() : int
    {
        return $this->margin;
    }
    public function getModule() : ModuleInterface
    {
        return $this->module;
    }
    public function getEye() : EyeInterface
    {
        return $this->eye;
    }
    public function getFill() : Fill
    {
        return $this->fill;
    }
}
vendor/bacon/bacon-qr-code/src/Renderer/RendererStyle/Fill.php000064400000007202150755130600020263 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Renderer\RendererStyle;

use WP2FA_Vendor\BaconQrCode\Exception\RuntimeException;
use WP2FA_Vendor\BaconQrCode\Renderer\Color\ColorInterface;
use WP2FA_Vendor\BaconQrCode\Renderer\Color\Gray;
final class Fill
{
    /**
     * @var ColorInterface
     */
    private $backgroundColor;
    /**
     * @var ColorInterface|null
     */
    private $foregroundColor;
    /**
     * @var Gradient|null
     */
    private $foregroundGradient;
    /**
     * @var EyeFill
     */
    private $topLeftEyeFill;
    /**
     * @var EyeFill
     */
    private $topRightEyeFill;
    /**
     * @var EyeFill
     */
    private $bottomLeftEyeFill;
    /**
     * @var self|null
     */
    private static $default;
    private function __construct(ColorInterface $backgroundColor, ?ColorInterface $foregroundColor, ?Gradient $foregroundGradient, EyeFill $topLeftEyeFill, EyeFill $topRightEyeFill, EyeFill $bottomLeftEyeFill)
    {
        $this->backgroundColor = $backgroundColor;
        $this->foregroundColor = $foregroundColor;
        $this->foregroundGradient = $foregroundGradient;
        $this->topLeftEyeFill = $topLeftEyeFill;
        $this->topRightEyeFill = $topRightEyeFill;
        $this->bottomLeftEyeFill = $bottomLeftEyeFill;
    }
    public static function default() : self
    {
        return self::$default ?: (self::$default = self::uniformColor(new Gray(100), new Gray(0)));
    }
    public static function withForegroundColor(ColorInterface $backgroundColor, ColorInterface $foregroundColor, EyeFill $topLeftEyeFill, EyeFill $topRightEyeFill, EyeFill $bottomLeftEyeFill) : self
    {
        return new self($backgroundColor, $foregroundColor, null, $topLeftEyeFill, $topRightEyeFill, $bottomLeftEyeFill);
    }
    public static function withForegroundGradient(ColorInterface $backgroundColor, Gradient $foregroundGradient, EyeFill $topLeftEyeFill, EyeFill $topRightEyeFill, EyeFill $bottomLeftEyeFill) : self
    {
        return new self($backgroundColor, null, $foregroundGradient, $topLeftEyeFill, $topRightEyeFill, $bottomLeftEyeFill);
    }
    public static function uniformColor(ColorInterface $backgroundColor, ColorInterface $foregroundColor) : self
    {
        return new self($backgroundColor, $foregroundColor, null, EyeFill::inherit(), EyeFill::inherit(), EyeFill::inherit());
    }
    public static function uniformGradient(ColorInterface $backgroundColor, Gradient $foregroundGradient) : self
    {
        return new self($backgroundColor, null, $foregroundGradient, EyeFill::inherit(), EyeFill::inherit(), EyeFill::inherit());
    }
    public function hasGradientFill() : bool
    {
        return null !== $this->foregroundGradient;
    }
    public function getBackgroundColor() : ColorInterface
    {
        return $this->backgroundColor;
    }
    public function getForegroundColor() : ColorInterface
    {
        if (null === $this->foregroundColor) {
            throw new RuntimeException('Fill uses a gradient, thus no foreground color is available');
        }
        return $this->foregroundColor;
    }
    public function getForegroundGradient() : Gradient
    {
        if (null === $this->foregroundGradient) {
            throw new RuntimeException('Fill uses a single color, thus no foreground gradient is available');
        }
        return $this->foregroundGradient;
    }
    public function getTopLeftEyeFill() : EyeFill
    {
        return $this->topLeftEyeFill;
    }
    public function getTopRightEyeFill() : EyeFill
    {
        return $this->topRightEyeFill;
    }
    public function getBottomLeftEyeFill() : EyeFill
    {
        return $this->bottomLeftEyeFill;
    }
}
vendor/bacon/bacon-qr-code/src/Renderer/RendererStyle/GradientType.php000064400000001066150755130600021776 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Renderer\RendererStyle;

use WP2FA_Vendor\DASPRiD\Enum\AbstractEnum;
/**
 * @method static self VERTICAL()
 * @method static self HORIZONTAL()
 * @method static self DIAGONAL()
 * @method static self INVERSE_DIAGONAL()
 * @method static self RADIAL()
 */
final class GradientType extends AbstractEnum
{
    protected const VERTICAL = null;
    protected const HORIZONTAL = null;
    protected const DIAGONAL = null;
    protected const INVERSE_DIAGONAL = null;
    protected const RADIAL = null;
}
vendor/bacon/bacon-qr-code/src/Renderer/RendererStyle/EyeFill.php000064400000003334150755130600020730 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Renderer\RendererStyle;

use WP2FA_Vendor\BaconQrCode\Exception\RuntimeException;
use WP2FA_Vendor\BaconQrCode\Renderer\Color\ColorInterface;
final class EyeFill
{
    /**
     * @var ColorInterface|null
     */
    private $externalColor;
    /**
     * @var ColorInterface|null
     */
    private $internalColor;
    /**
     * @var self|null
     */
    private static $inherit;
    public function __construct(?ColorInterface $externalColor, ?ColorInterface $internalColor)
    {
        $this->externalColor = $externalColor;
        $this->internalColor = $internalColor;
    }
    public static function uniform(ColorInterface $color) : self
    {
        return new self($color, $color);
    }
    public static function inherit() : self
    {
        return self::$inherit ?: (self::$inherit = new self(null, null));
    }
    public function inheritsBothColors() : bool
    {
        return null === $this->externalColor && null === $this->internalColor;
    }
    public function inheritsExternalColor() : bool
    {
        return null === $this->externalColor;
    }
    public function inheritsInternalColor() : bool
    {
        return null === $this->internalColor;
    }
    public function getExternalColor() : ColorInterface
    {
        if (null === $this->externalColor) {
            throw new RuntimeException('External eye color inherits foreground color');
        }
        return $this->externalColor;
    }
    public function getInternalColor() : ColorInterface
    {
        if (null === $this->internalColor) {
            throw new RuntimeException('Internal eye color inherits foreground color');
        }
        return $this->internalColor;
    }
}
vendor/bacon/bacon-qr-code/src/Renderer/Color/Gray.php000064400000001661150755130600016571 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Renderer\Color;

use WP2FA_Vendor\BaconQrCode\Exception;
final class Gray implements ColorInterface
{
    /**
     * @var int
     */
    private $gray;
    /**
     * @param int $gray the gray value between 0 (black) and 100 (white)
     */
    public function __construct(int $gray)
    {
        if ($gray < 0 || $gray > 100) {
            throw new Exception\InvalidArgumentException('Gray must be between 0 and 100');
        }
        $this->gray = (int) $gray;
    }
    public function getGray() : int
    {
        return $this->gray;
    }
    public function toRgb() : Rgb
    {
        return new Rgb((int) ($this->gray * 2.55), (int) ($this->gray * 2.55), (int) ($this->gray * 2.55));
    }
    public function toCmyk() : Cmyk
    {
        return new Cmyk(0, 0, 0, 100 - $this->gray);
    }
    public function toGray() : Gray
    {
        return $this;
    }
}
vendor/bacon/bacon-qr-code/src/Renderer/Color/Rgb.php000064400000003600150755130600016374 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Renderer\Color;

use WP2FA_Vendor\BaconQrCode\Exception;
final class Rgb implements ColorInterface
{
    /**
     * @var int
     */
    private $red;
    /**
     * @var int
     */
    private $green;
    /**
     * @var int
     */
    private $blue;
    /**
     * @param int $red the red amount of the color, 0 to 255
     * @param int $green the green amount of the color, 0 to 255
     * @param int $blue the blue amount of the color, 0 to 255
     */
    public function __construct(int $red, int $green, int $blue)
    {
        if ($red < 0 || $red > 255) {
            throw new Exception\InvalidArgumentException('Red must be between 0 and 255');
        }
        if ($green < 0 || $green > 255) {
            throw new Exception\InvalidArgumentException('Green must be between 0 and 255');
        }
        if ($blue < 0 || $blue > 255) {
            throw new Exception\InvalidArgumentException('Blue must be between 0 and 255');
        }
        $this->red = $red;
        $this->green = $green;
        $this->blue = $blue;
    }
    public function getRed() : int
    {
        return $this->red;
    }
    public function getGreen() : int
    {
        return $this->green;
    }
    public function getBlue() : int
    {
        return $this->blue;
    }
    public function toRgb() : Rgb
    {
        return $this;
    }
    public function toCmyk() : Cmyk
    {
        $c = 1 - $this->red / 255;
        $m = 1 - $this->green / 255;
        $y = 1 - $this->blue / 255;
        $k = \min($c, $m, $y);
        return new Cmyk((int) (100 * ($c - $k) / (1 - $k)), (int) (100 * ($m - $k) / (1 - $k)), (int) (100 * ($y - $k) / (1 - $k)), (int) (100 * $k));
    }
    public function toGray() : Gray
    {
        return new Gray((int) (($this->red * 0.21 + $this->green * 0.71 + $this->blue * 0.07000000000000001) / 2.55));
    }
}
vendor/bacon/bacon-qr-code/src/Renderer/Color/Alpha.php000064400000002116150755130600016710 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Renderer\Color;

use WP2FA_Vendor\BaconQrCode\Exception;
final class Alpha implements ColorInterface
{
    /**
     * @var int
     */
    private $alpha;
    /**
     * @var ColorInterface
     */
    private $baseColor;
    /**
     * @param int $alpha the alpha value, 0 to 100
     */
    public function __construct(int $alpha, ColorInterface $baseColor)
    {
        if ($alpha < 0 || $alpha > 100) {
            throw new Exception\InvalidArgumentException('Alpha must be between 0 and 100');
        }
        $this->alpha = $alpha;
        $this->baseColor = $baseColor;
    }
    public function getAlpha() : int
    {
        return $this->alpha;
    }
    public function getBaseColor() : ColorInterface
    {
        return $this->baseColor;
    }
    public function toRgb() : Rgb
    {
        return $this->baseColor->toRgb();
    }
    public function toCmyk() : Cmyk
    {
        return $this->baseColor->toCmyk();
    }
    public function toGray() : Gray
    {
        return $this->baseColor->toGray();
    }
}
vendor/bacon/bacon-qr-code/src/Renderer/Color/Cmyk.php000064400000004344150755130600016573 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Renderer\Color;

use WP2FA_Vendor\BaconQrCode\Exception;
final class Cmyk implements ColorInterface
{
    /**
     * @var int
     */
    private $cyan;
    /**
     * @var int
     */
    private $magenta;
    /**
     * @var int
     */
    private $yellow;
    /**
     * @var int
     */
    private $black;
    /**
     * @param int $cyan the cyan amount, 0 to 100
     * @param int $magenta the magenta amount, 0 to 100
     * @param int $yellow the yellow amount, 0 to 100
     * @param int $black the black amount, 0 to 100
     */
    public function __construct(int $cyan, int $magenta, int $yellow, int $black)
    {
        if ($cyan < 0 || $cyan > 100) {
            throw new Exception\InvalidArgumentException('Cyan must be between 0 and 100');
        }
        if ($magenta < 0 || $magenta > 100) {
            throw new Exception\InvalidArgumentException('Magenta must be between 0 and 100');
        }
        if ($yellow < 0 || $yellow > 100) {
            throw new Exception\InvalidArgumentException('Yellow must be between 0 and 100');
        }
        if ($black < 0 || $black > 100) {
            throw new Exception\InvalidArgumentException('Black must be between 0 and 100');
        }
        $this->cyan = $cyan;
        $this->magenta = $magenta;
        $this->yellow = $yellow;
        $this->black = $black;
    }
    public function getCyan() : int
    {
        return $this->cyan;
    }
    public function getMagenta() : int
    {
        return $this->magenta;
    }
    public function getYellow() : int
    {
        return $this->yellow;
    }
    public function getBlack() : int
    {
        return $this->black;
    }
    public function toRgb() : Rgb
    {
        $k = $this->black / 100;
        $c = (-$k * $this->cyan + $k * 100 + $this->cyan) / 100;
        $m = (-$k * $this->magenta + $k * 100 + $this->magenta) / 100;
        $y = (-$k * $this->yellow + $k * 100 + $this->yellow) / 100;
        return new Rgb((int) (-$c * 255 + 255), (int) (-$m * 255 + 255), (int) (-$y * 255 + 255));
    }
    public function toCmyk() : Cmyk
    {
        return $this;
    }
    public function toGray() : Gray
    {
        return $this->toRgb()->toGray();
    }
}
vendor/bacon/bacon-qr-code/src/Renderer/Color/ColorInterface.php000064400000000567150755130600020572 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Renderer\Color;

interface ColorInterface
{
    /**
     * Converts the color to RGB.
     */
    public function toRgb() : Rgb;
    /**
     * Converts the color to CMYK.
     */
    public function toCmyk() : Cmyk;
    /**
     * Converts the color to gray.
     */
    public function toGray() : Gray;
}
vendor/bacon/bacon-qr-code/src/Renderer/Module/RoundnessModule.php000064400000007203150755130600021162 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Renderer\Module;

use WP2FA_Vendor\BaconQrCode\Encoder\ByteMatrix;
use WP2FA_Vendor\BaconQrCode\Exception\InvalidArgumentException;
use WP2FA_Vendor\BaconQrCode\Renderer\Module\EdgeIterator\EdgeIterator;
use WP2FA_Vendor\BaconQrCode\Renderer\Path\Path;
/**
 * Rounds the corners of module groups.
 */
final class RoundnessModule implements ModuleInterface
{
    public const STRONG = 1;
    public const MEDIUM = 0.5;
    public const SOFT = 0.25;
    /**
     * @var float
     */
    private $intensity;
    public function __construct(float $intensity)
    {
        if ($intensity <= 0 || $intensity > 1) {
            throw new InvalidArgumentException('Intensity must between 0 (exclusive) and 1 (inclusive)');
        }
        $this->intensity = $intensity / 2;
    }
    public function createPath(ByteMatrix $matrix) : Path
    {
        $path = new Path();
        foreach (new EdgeIterator($matrix) as $edge) {
            $points = $edge->getSimplifiedPoints();
            $length = \count($points);
            $currentPoint = $points[0];
            $nextPoint = $points[1];
            $horizontal = $currentPoint[1] === $nextPoint[1];
            if ($horizontal) {
                $right = $nextPoint[0] > $currentPoint[0];
                $path = $path->move($currentPoint[0] + ($right ? $this->intensity : -$this->intensity), $currentPoint[1]);
            } else {
                $up = $nextPoint[0] < $currentPoint[0];
                $path = $path->move($currentPoint[0], $currentPoint[1] + ($up ? -$this->intensity : $this->intensity));
            }
            for ($i = 1; $i <= $length; ++$i) {
                if ($i === $length) {
                    $previousPoint = $points[$length - 1];
                    $currentPoint = $points[0];
                    $nextPoint = $points[1];
                } else {
                    $previousPoint = $points[(0 === $i ? $length : $i) - 1];
                    $currentPoint = $points[$i];
                    $nextPoint = $points[($length - 1 === $i ? -1 : $i) + 1];
                }
                $horizontal = $previousPoint[1] === $currentPoint[1];
                if ($horizontal) {
                    $right = $previousPoint[0] < $currentPoint[0];
                    $up = $nextPoint[1] < $currentPoint[1];
                    $sweep = ($up xor $right);
                    if ($this->intensity < 0.5 || $right && $previousPoint[0] !== $currentPoint[0] - 1 || !$right && $previousPoint[0] - 1 !== $currentPoint[0]) {
                        $path = $path->line($currentPoint[0] + ($right ? -$this->intensity : $this->intensity), $currentPoint[1]);
                    }
                    $path = $path->ellipticArc($this->intensity, $this->intensity, 0, \false, $sweep, $currentPoint[0], $currentPoint[1] + ($up ? -$this->intensity : $this->intensity));
                } else {
                    $up = $previousPoint[1] > $currentPoint[1];
                    $right = $nextPoint[0] > $currentPoint[0];
                    $sweep = !($up xor $right);
                    if ($this->intensity < 0.5 || $up && $previousPoint[1] !== $currentPoint[1] + 1 || !$up && $previousPoint[0] + 1 !== $currentPoint[0]) {
                        $path = $path->line($currentPoint[0], $currentPoint[1] + ($up ? $this->intensity : -$this->intensity));
                    }
                    $path = $path->ellipticArc($this->intensity, $this->intensity, 0, \false, $sweep, $currentPoint[0] + ($right ? $this->intensity : -$this->intensity), $currentPoint[1]);
                }
            }
            $path = $path->close();
        }
        return $path;
    }
}
vendor/bacon/bacon-qr-code/src/Renderer/Module/ModuleInterface.php000064400000001020150755130600021071 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Renderer\Module;

use WP2FA_Vendor\BaconQrCode\Encoder\ByteMatrix;
use WP2FA_Vendor\BaconQrCode\Renderer\Path\Path;
/**
 * Interface describing how modules should be rendered.
 *
 * A module always receives a byte matrix (with values either being 1 or 0). It returns a path, where the origin
 * coordinate (0, 0) equals the top left corner of the first matrix value.
 */
interface ModuleInterface
{
    public function createPath(ByteMatrix $matrix) : Path;
}
vendor/bacon/bacon-qr-code/src/Renderer/Module/SquareModule.php000064400000002124150755130600020437 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Renderer\Module;

use WP2FA_Vendor\BaconQrCode\Encoder\ByteMatrix;
use WP2FA_Vendor\BaconQrCode\Renderer\Module\EdgeIterator\EdgeIterator;
use WP2FA_Vendor\BaconQrCode\Renderer\Path\Path;
/**
 * Groups modules together to a single path.
 */
final class SquareModule implements ModuleInterface
{
    /**
     * @var self|null
     */
    private static $instance;
    private function __construct()
    {
    }
    public static function instance() : self
    {
        return self::$instance ?: (self::$instance = new self());
    }
    public function createPath(ByteMatrix $matrix) : Path
    {
        $path = new Path();
        foreach (new EdgeIterator($matrix) as $edge) {
            $points = $edge->getSimplifiedPoints();
            $length = \count($points);
            $path = $path->move($points[0][0], $points[0][1]);
            for ($i = 1; $i < $length; ++$i) {
                $path = $path->line($points[$i][0], $points[$i][1]);
            }
            $path = $path->close();
        }
        return $path;
    }
}
vendor/bacon/bacon-qr-code/src/Renderer/Module/EdgeIterator/EdgeIterator.php000064400000007026150755130600022773 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Renderer\Module\EdgeIterator;

use WP2FA_Vendor\BaconQrCode\Encoder\ByteMatrix;
use IteratorAggregate;
use Traversable;
/**
 * Edge iterator based on potrace.
 */
final class EdgeIterator implements IteratorAggregate
{
    /**
     * @var int[]
     */
    private $bytes = [];
    /**
     * @var int
     */
    private $size;
    /**
     * @var int
     */
    private $width;
    /**
     * @var int
     */
    private $height;
    public function __construct(ByteMatrix $matrix)
    {
        $this->bytes = \iterator_to_array($matrix->getBytes());
        $this->size = \count($this->bytes);
        $this->width = $matrix->getWidth();
        $this->height = $matrix->getHeight();
    }
    /**
     * @return Traversable<Edge>
     */
    public function getIterator() : Traversable
    {
        $originalBytes = $this->bytes;
        $point = $this->findNext(0, 0);
        while (null !== $point) {
            $edge = $this->findEdge($point[0], $point[1]);
            $this->xorEdge($edge);
            (yield $edge);
            $point = $this->findNext($point[0], $point[1]);
        }
        $this->bytes = $originalBytes;
    }
    /**
     * @return int[]|null
     */
    private function findNext(int $x, int $y) : ?array
    {
        $i = $this->width * $y + $x;
        while ($i < $this->size && 1 !== $this->bytes[$i]) {
            ++$i;
        }
        if ($i < $this->size) {
            return $this->pointOf($i);
        }
        return null;
    }
    private function findEdge(int $x, int $y) : Edge
    {
        $edge = new Edge($this->isSet($x, $y));
        $startX = $x;
        $startY = $y;
        $dirX = 0;
        $dirY = 1;
        while (\true) {
            $edge->addPoint($x, $y);
            $x += $dirX;
            $y += $dirY;
            if ($x === $startX && $y === $startY) {
                break;
            }
            $left = $this->isSet($x + ($dirX + $dirY - 1) / 2, $y + ($dirY - $dirX - 1) / 2);
            $right = $this->isSet($x + ($dirX - $dirY - 1) / 2, $y + ($dirY + $dirX - 1) / 2);
            if ($right && !$left) {
                $tmp = $dirX;
                $dirX = -$dirY;
                $dirY = $tmp;
            } elseif ($right) {
                $tmp = $dirX;
                $dirX = -$dirY;
                $dirY = $tmp;
            } elseif (!$left) {
                $tmp = $dirX;
                $dirX = $dirY;
                $dirY = -$tmp;
            }
        }
        return $edge;
    }
    private function xorEdge(Edge $path) : void
    {
        $points = $path->getPoints();
        $y1 = $points[0][1];
        $length = \count($points);
        $maxX = $path->getMaxX();
        for ($i = 1; $i < $length; ++$i) {
            $y = $points[$i][1];
            if ($y === $y1) {
                continue;
            }
            $x = $points[$i][0];
            $minY = \min($y1, $y);
            for ($j = $x; $j < $maxX; ++$j) {
                $this->flip($j, $minY);
            }
            $y1 = $y;
        }
    }
    private function isSet(int $x, int $y) : bool
    {
        return $x >= 0 && $x < $this->width && $y >= 0 && $y < $this->height && 1 === $this->bytes[$this->width * $y + $x];
    }
    /**
     * @return int[]
     */
    private function pointOf(int $i) : array
    {
        $y = \intdiv($i, $this->width);
        return [$i - $y * $this->width, $y];
    }
    private function flip(int $x, int $y) : void
    {
        $this->bytes[$this->width * $y + $x] = $this->isSet($x, $y) ? 0 : 1;
    }
}
vendor/bacon/bacon-qr-code/src/Renderer/Module/EdgeIterator/Edge.php000064400000003732150755130600021261 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Renderer\Module\EdgeIterator;

final class Edge
{
    /**
     * @var bool
     */
    private $positive;
    /**
     * @var array<int[]>
     */
    private $points = [];
    /**
     * @var array<int[]>|null
     */
    private $simplifiedPoints;
    /**
     * @var int
     */
    private $minX = \PHP_INT_MAX;
    /**
     * @var int
     */
    private $minY = \PHP_INT_MAX;
    /**
     * @var int
     */
    private $maxX = -1;
    /**
     * @var int
     */
    private $maxY = -1;
    public function __construct(bool $positive)
    {
        $this->positive = $positive;
    }
    public function addPoint(int $x, int $y) : void
    {
        $this->points[] = [$x, $y];
        $this->minX = \min($this->minX, $x);
        $this->minY = \min($this->minY, $y);
        $this->maxX = \max($this->maxX, $x);
        $this->maxY = \max($this->maxY, $y);
    }
    public function isPositive() : bool
    {
        return $this->positive;
    }
    /**
     * @return array<int[]>
     */
    public function getPoints() : array
    {
        return $this->points;
    }
    public function getMaxX() : int
    {
        return $this->maxX;
    }
    public function getSimplifiedPoints() : array
    {
        if (null !== $this->simplifiedPoints) {
            return $this->simplifiedPoints;
        }
        $points = [];
        $length = \count($this->points);
        for ($i = 0; $i < $length; ++$i) {
            $previousPoint = $this->points[(0 === $i ? $length : $i) - 1];
            $nextPoint = $this->points[($length - 1 === $i ? -1 : $i) + 1];
            $currentPoint = $this->points[$i];
            if ($previousPoint[0] === $currentPoint[0] && $currentPoint[0] === $nextPoint[0] || $previousPoint[1] === $currentPoint[1] && $currentPoint[1] === $nextPoint[1]) {
                continue;
            }
            $points[] = $currentPoint;
        }
        return $this->simplifiedPoints = $points;
    }
}
vendor/bacon/bacon-qr-code/src/Renderer/Module/DotsModule.php000064400000003300150755130600020105 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Renderer\Module;

use WP2FA_Vendor\BaconQrCode\Encoder\ByteMatrix;
use WP2FA_Vendor\BaconQrCode\Exception\InvalidArgumentException;
use WP2FA_Vendor\BaconQrCode\Renderer\Path\Path;
/**
 * Renders individual modules as dots.
 */
final class DotsModule implements ModuleInterface
{
    public const LARGE = 1;
    public const MEDIUM = 0.8;
    public const SMALL = 0.6;
    /**
     * @var float
     */
    private $size;
    public function __construct(float $size)
    {
        if ($size <= 0 || $size > 1) {
            throw new InvalidArgumentException('Size must between 0 (exclusive) and 1 (inclusive)');
        }
        $this->size = $size;
    }
    public function createPath(ByteMatrix $matrix) : Path
    {
        $width = $matrix->getWidth();
        $height = $matrix->getHeight();
        $path = new Path();
        $halfSize = $this->size / 2;
        $margin = (1 - $this->size) / 2;
        for ($y = 0; $y < $height; ++$y) {
            for ($x = 0; $x < $width; ++$x) {
                if (!$matrix->get($x, $y)) {
                    continue;
                }
                $pathX = $x + $margin;
                $pathY = $y + $margin;
                $path = $path->move($pathX + $this->size, $pathY + $halfSize)->ellipticArc($halfSize, $halfSize, 0, \false, \true, $pathX + $halfSize, $pathY + $this->size)->ellipticArc($halfSize, $halfSize, 0, \false, \true, $pathX, $pathY + $halfSize)->ellipticArc($halfSize, $halfSize, 0, \false, \true, $pathX + $halfSize, $pathY)->ellipticArc($halfSize, $halfSize, 0, \false, \true, $pathX + $this->size, $pathY + $halfSize)->close();
            }
        }
        return $path;
    }
}
vendor/bacon/bacon-qr-code/src/Renderer/Eye/CompositeEye.php000064400000001412150755130600017732 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Renderer\Eye;

use WP2FA_Vendor\BaconQrCode\Renderer\Path\Path;
/**
 * Combines the style of two different eyes.
 */
final class CompositeEye implements EyeInterface
{
    /**
     * @var EyeInterface
     */
    private $externalEye;
    /**
     * @var EyeInterface
     */
    private $internalEye;
    public function __construct(EyeInterface $externalEye, EyeInterface $internalEye)
    {
        $this->externalEye = $externalEye;
        $this->internalEye = $internalEye;
    }
    public function getExternalPath() : Path
    {
        return $this->externalEye->getExternalPath();
    }
    public function getInternalPath() : Path
    {
        return $this->internalEye->getInternalPath();
    }
}
vendor/bacon/bacon-qr-code/src/Renderer/Eye/SimpleCircleEye.php000064400000002013150755130600020341 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Renderer\Eye;

use WP2FA_Vendor\BaconQrCode\Renderer\Path\Path;
/**
 * Renders the inner eye as a circle.
 */
final class SimpleCircleEye implements EyeInterface
{
    /**
     * @var self|null
     */
    private static $instance;
    private function __construct()
    {
    }
    public static function instance() : self
    {
        return self::$instance ?: (self::$instance = new self());
    }
    public function getExternalPath() : Path
    {
        return (new Path())->move(-3.5, -3.5)->line(3.5, -3.5)->line(3.5, 3.5)->line(-3.5, 3.5)->close()->move(-2.5, -2.5)->line(-2.5, 2.5)->line(2.5, 2.5)->line(2.5, -2.5)->close();
    }
    public function getInternalPath() : Path
    {
        return (new Path())->move(1.5, 0)->ellipticArc(1.5, 1.5, 0.0, \false, \true, 0.0, 1.5)->ellipticArc(1.5, 1.5, 0.0, \false, \true, -1.5, 0.0)->ellipticArc(1.5, 1.5, 0.0, \false, \true, 0.0, -1.5)->ellipticArc(1.5, 1.5, 0.0, \false, \true, 1.5, 0.0)->close();
    }
}
vendor/bacon/bacon-qr-code/src/Renderer/Eye/ModuleEye.php000064400000002325150755130600017221 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Renderer\Eye;

use WP2FA_Vendor\BaconQrCode\Encoder\ByteMatrix;
use WP2FA_Vendor\BaconQrCode\Renderer\Module\ModuleInterface;
use WP2FA_Vendor\BaconQrCode\Renderer\Path\Path;
/**
 * Renders an eye based on a module renderer.
 */
final class ModuleEye implements EyeInterface
{
    /**
     * @var ModuleInterface
     */
    private $module;
    public function __construct(ModuleInterface $module)
    {
        $this->module = $module;
    }
    public function getExternalPath() : Path
    {
        $matrix = new ByteMatrix(7, 7);
        for ($x = 0; $x < 7; ++$x) {
            $matrix->set($x, 0, 1);
            $matrix->set($x, 6, 1);
        }
        for ($y = 1; $y < 6; ++$y) {
            $matrix->set(0, $y, 1);
            $matrix->set(6, $y, 1);
        }
        return $this->module->createPath($matrix)->translate(-3.5, -3.5);
    }
    public function getInternalPath() : Path
    {
        $matrix = new ByteMatrix(3, 3);
        for ($x = 0; $x < 3; ++$x) {
            for ($y = 0; $y < 3; ++$y) {
                $matrix->set($x, $y, 1);
            }
        }
        return $this->module->createPath($matrix)->translate(-1.5, -1.5);
    }
}
vendor/bacon/bacon-qr-code/src/Renderer/Eye/EyeInterface.php000064400000001153150755130600017672 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Renderer\Eye;

use WP2FA_Vendor\BaconQrCode\Renderer\Path\Path;
/**
 * Interface for describing the look of an eye.
 */
interface EyeInterface
{
    /**
     * Returns the path of the external eye element.
     *
     * The path origin point (0, 0) must be anchored at the middle of the path.
     */
    public function getExternalPath() : Path;
    /**
     * Returns the path of the internal eye element.
     *
     * The path origin point (0, 0) must be anchored at the middle of the path.
     */
    public function getInternalPath() : Path;
}
vendor/bacon/bacon-qr-code/src/Renderer/Eye/SquareEye.php000064400000001562150755130600017236 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Renderer\Eye;

use WP2FA_Vendor\BaconQrCode\Renderer\Path\Path;
/**
 * Renders the eyes in their default square shape.
 */
final class SquareEye implements EyeInterface
{
    /**
     * @var self|null
     */
    private static $instance;
    private function __construct()
    {
    }
    public static function instance() : self
    {
        return self::$instance ?: (self::$instance = new self());
    }
    public function getExternalPath() : Path
    {
        return (new Path())->move(-3.5, -3.5)->line(3.5, -3.5)->line(3.5, 3.5)->line(-3.5, 3.5)->close()->move(-2.5, -2.5)->line(-2.5, 2.5)->line(2.5, 2.5)->line(2.5, -2.5)->close();
    }
    public function getInternalPath() : Path
    {
        return (new Path())->move(-1.5, -1.5)->line(1.5, -1.5)->line(1.5, 1.5)->line(-1.5, 1.5)->close();
    }
}
vendor/bacon/bacon-qr-code/src/Encoder/QrCode.php000064400000005214150755130600015575 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Encoder;

use WP2FA_Vendor\BaconQrCode\Common\ErrorCorrectionLevel;
use WP2FA_Vendor\BaconQrCode\Common\Mode;
use WP2FA_Vendor\BaconQrCode\Common\Version;
/**
 * QR code.
 */
final class QrCode
{
    /**
     * Number of possible mask patterns.
     */
    public const NUM_MASK_PATTERNS = 8;
    /**
     * Mode of the QR code.
     *
     * @var Mode
     */
    private $mode;
    /**
     * EC level of the QR code.
     *
     * @var ErrorCorrectionLevel
     */
    private $errorCorrectionLevel;
    /**
     * Version of the QR code.
     *
     * @var Version
     */
    private $version;
    /**
     * Mask pattern of the QR code.
     *
     * @var int
     */
    private $maskPattern = -1;
    /**
     * Matrix of the QR code.
     *
     * @var ByteMatrix
     */
    private $matrix;
    public function __construct(Mode $mode, ErrorCorrectionLevel $errorCorrectionLevel, Version $version, int $maskPattern, ByteMatrix $matrix)
    {
        $this->mode = $mode;
        $this->errorCorrectionLevel = $errorCorrectionLevel;
        $this->version = $version;
        $this->maskPattern = $maskPattern;
        $this->matrix = $matrix;
    }
    /**
     * Gets the mode.
     */
    public function getMode() : Mode
    {
        return $this->mode;
    }
    /**
     * Gets the EC level.
     */
    public function getErrorCorrectionLevel() : ErrorCorrectionLevel
    {
        return $this->errorCorrectionLevel;
    }
    /**
     * Gets the version.
     */
    public function getVersion() : Version
    {
        return $this->version;
    }
    /**
     * Gets the mask pattern.
     */
    public function getMaskPattern() : int
    {
        return $this->maskPattern;
    }
    /**
     * Gets the matrix.
     *
     * @return ByteMatrix
     */
    public function getMatrix()
    {
        return $this->matrix;
    }
    /**
     * Validates whether a mask pattern is valid.
     */
    public static function isValidMaskPattern(int $maskPattern) : bool
    {
        return $maskPattern > 0 && $maskPattern < self::NUM_MASK_PATTERNS;
    }
    /**
     * Returns a string representation of the QR code.
     */
    public function __toString() : string
    {
        $result = "<<\n" . ' mode: ' . $this->mode . "\n" . ' ecLevel: ' . $this->errorCorrectionLevel . "\n" . ' version: ' . $this->version . "\n" . ' maskPattern: ' . $this->maskPattern . "\n";
        if ($this->matrix === null) {
            $result .= " matrix: null\n";
        } else {
            $result .= " matrix:\n";
            $result .= $this->matrix;
        }
        $result .= ">>\n";
        return $result;
    }
}
vendor/bacon/bacon-qr-code/src/Encoder/MaskUtil.php000064400000015706150755130600016160 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Encoder;

use WP2FA_Vendor\BaconQrCode\Common\BitUtils;
use WP2FA_Vendor\BaconQrCode\Exception\InvalidArgumentException;
/**
 * Mask utility.
 */
final class MaskUtil
{
    /**#@+
     * Penalty weights from section 6.8.2.1
     */
    const N1 = 3;
    const N2 = 3;
    const N3 = 40;
    const N4 = 10;
    /**#@-*/
    private function __construct()
    {
    }
    /**
     * Applies mask penalty rule 1 and returns the penalty.
     *
     * Finds repetitive cells with the same color and gives penalty to them.
     * Example: 00000 or 11111.
     */
    public static function applyMaskPenaltyRule1(ByteMatrix $matrix) : int
    {
        return self::applyMaskPenaltyRule1Internal($matrix, \true) + self::applyMaskPenaltyRule1Internal($matrix, \false);
    }
    /**
     * Applies mask penalty rule 2 and returns the penalty.
     *
     * Finds 2x2 blocks with the same color and gives penalty to them. This is
     * actually equivalent to the spec's rule, which is to find MxN blocks and
     * give a penalty proportional to (M-1)x(N-1), because this is the number of
     * 2x2 blocks inside such a block.
     */
    public static function applyMaskPenaltyRule2(ByteMatrix $matrix) : int
    {
        $penalty = 0;
        $array = $matrix->getArray();
        $width = $matrix->getWidth();
        $height = $matrix->getHeight();
        for ($y = 0; $y < $height - 1; ++$y) {
            for ($x = 0; $x < $width - 1; ++$x) {
                $value = $array[$y][$x];
                if ($value === $array[$y][$x + 1] && $value === $array[$y + 1][$x] && $value === $array[$y + 1][$x + 1]) {
                    ++$penalty;
                }
            }
        }
        return self::N2 * $penalty;
    }
    /**
     * Applies mask penalty rule 3 and returns the penalty.
     *
     * Finds consecutive cells of 00001011101 or 10111010000, and gives penalty
     * to them. If we find patterns like 000010111010000, we give penalties
     * twice (i.e. 40 * 2).
     */
    public static function applyMaskPenaltyRule3(ByteMatrix $matrix) : int
    {
        $penalty = 0;
        $array = $matrix->getArray();
        $width = $matrix->getWidth();
        $height = $matrix->getHeight();
        for ($y = 0; $y < $height; ++$y) {
            for ($x = 0; $x < $width; ++$x) {
                if ($x + 6 < $width && 1 === $array[$y][$x] && 0 === $array[$y][$x + 1] && 1 === $array[$y][$x + 2] && 1 === $array[$y][$x + 3] && 1 === $array[$y][$x + 4] && 0 === $array[$y][$x + 5] && 1 === $array[$y][$x + 6] && ($x + 10 < $width && 0 === $array[$y][$x + 7] && 0 === $array[$y][$x + 8] && 0 === $array[$y][$x + 9] && 0 === $array[$y][$x + 10] || $x - 4 >= 0 && 0 === $array[$y][$x - 1] && 0 === $array[$y][$x - 2] && 0 === $array[$y][$x - 3] && 0 === $array[$y][$x - 4])) {
                    $penalty += self::N3;
                }
                if ($y + 6 < $height && 1 === $array[$y][$x] && 0 === $array[$y + 1][$x] && 1 === $array[$y + 2][$x] && 1 === $array[$y + 3][$x] && 1 === $array[$y + 4][$x] && 0 === $array[$y + 5][$x] && 1 === $array[$y + 6][$x] && ($y + 10 < $height && 0 === $array[$y + 7][$x] && 0 === $array[$y + 8][$x] && 0 === $array[$y + 9][$x] && 0 === $array[$y + 10][$x] || $y - 4 >= 0 && 0 === $array[$y - 1][$x] && 0 === $array[$y - 2][$x] && 0 === $array[$y - 3][$x] && 0 === $array[$y - 4][$x])) {
                    $penalty += self::N3;
                }
            }
        }
        return $penalty;
    }
    /**
     * Applies mask penalty rule 4 and returns the penalty.
     *
     * Calculates the ratio of dark cells and gives penalty if the ratio is far
     * from 50%. It gives 10 penalty for 5% distance.
     */
    public static function applyMaskPenaltyRule4(ByteMatrix $matrix) : int
    {
        $numDarkCells = 0;
        $array = $matrix->getArray();
        $width = $matrix->getWidth();
        $height = $matrix->getHeight();
        for ($y = 0; $y < $height; ++$y) {
            $arrayY = $array[$y];
            for ($x = 0; $x < $width; ++$x) {
                if (1 === $arrayY[$x]) {
                    ++$numDarkCells;
                }
            }
        }
        $numTotalCells = $height * $width;
        $darkRatio = $numDarkCells / $numTotalCells;
        $fixedPercentVariances = (int) (\abs($darkRatio - 0.5) * 20);
        return $fixedPercentVariances * self::N4;
    }
    /**
     * Returns the mask bit for "getMaskPattern" at "x" and "y".
     *
     * See 8.8 of JISX0510:2004 for mask pattern conditions.
     *
     * @throws InvalidArgumentException if an invalid mask pattern was supplied
     */
    public static function getDataMaskBit(int $maskPattern, int $x, int $y) : bool
    {
        switch ($maskPattern) {
            case 0:
                $intermediate = $y + $x & 0x1;
                break;
            case 1:
                $intermediate = $y & 0x1;
                break;
            case 2:
                $intermediate = $x % 3;
                break;
            case 3:
                $intermediate = ($y + $x) % 3;
                break;
            case 4:
                $intermediate = BitUtils::unsignedRightShift($y, 1) + (int) ($x / 3) & 0x1;
                break;
            case 5:
                $temp = $y * $x;
                $intermediate = ($temp & 0x1) + $temp % 3;
                break;
            case 6:
                $temp = $y * $x;
                $intermediate = ($temp & 0x1) + $temp % 3 & 0x1;
                break;
            case 7:
                $temp = $y * $x;
                $intermediate = $temp % 3 + ($y + $x & 0x1) & 0x1;
                break;
            default:
                throw new InvalidArgumentException('Invalid mask pattern: ' . $maskPattern);
        }
        return 0 == $intermediate;
    }
    /**
     * Helper function for applyMaskPenaltyRule1.
     *
     * We need this for doing this calculation in both vertical and horizontal
     * orders respectively.
     */
    private static function applyMaskPenaltyRule1Internal(ByteMatrix $matrix, bool $isHorizontal) : int
    {
        $penalty = 0;
        $iLimit = $isHorizontal ? $matrix->getHeight() : $matrix->getWidth();
        $jLimit = $isHorizontal ? $matrix->getWidth() : $matrix->getHeight();
        $array = $matrix->getArray();
        for ($i = 0; $i < $iLimit; ++$i) {
            $numSameBitCells = 0;
            $prevBit = -1;
            for ($j = 0; $j < $jLimit; $j++) {
                $bit = $isHorizontal ? $array[$i][$j] : $array[$j][$i];
                if ($bit === $prevBit) {
                    ++$numSameBitCells;
                } else {
                    if ($numSameBitCells >= 5) {
                        $penalty += self::N1 + ($numSameBitCells - 5);
                    }
                    $numSameBitCells = 1;
                    $prevBit = $bit;
                }
            }
            if ($numSameBitCells >= 5) {
                $penalty += self::N1 + ($numSameBitCells - 5);
            }
        }
        return $penalty;
    }
}
vendor/bacon/bacon-qr-code/src/Encoder/ByteMatrix.php000064400000005722150755130600016514 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Encoder;

use SplFixedArray;
use Traversable;
/**
 * Byte matrix.
 */
final class ByteMatrix
{
    /**
     * Bytes in the matrix, represented as array.
     *
     * @var SplFixedArray<SplFixedArray<int>>
     */
    private $bytes;
    /**
     * Width of the matrix.
     *
     * @var int
     */
    private $width;
    /**
     * Height of the matrix.
     *
     * @var int
     */
    private $height;
    public function __construct(int $width, int $height)
    {
        $this->height = $height;
        $this->width = $width;
        $this->bytes = new SplFixedArray($height);
        for ($y = 0; $y < $height; ++$y) {
            $this->bytes[$y] = SplFixedArray::fromArray(\array_fill(0, $width, 0));
        }
    }
    /**
     * Gets the width of the matrix.
     */
    public function getWidth() : int
    {
        return $this->width;
    }
    /**
     * Gets the height of the matrix.
     */
    public function getHeight() : int
    {
        return $this->height;
    }
    /**
     * Gets the internal representation of the matrix.
     *
     * @return SplFixedArray<SplFixedArray<int>>
     */
    public function getArray() : SplFixedArray
    {
        return $this->bytes;
    }
    /**
     * @return Traversable<int>
     */
    public function getBytes() : Traversable
    {
        foreach ($this->bytes as $row) {
            foreach ($row as $byte) {
                (yield $byte);
            }
        }
    }
    /**
     * Gets the byte for a specific position.
     */
    public function get(int $x, int $y) : int
    {
        return $this->bytes[$y][$x];
    }
    /**
     * Sets the byte for a specific position.
     */
    public function set(int $x, int $y, int $value) : void
    {
        $this->bytes[$y][$x] = $value;
    }
    /**
     * Clears the matrix with a specific value.
     */
    public function clear(int $value) : void
    {
        for ($y = 0; $y < $this->height; ++$y) {
            for ($x = 0; $x < $this->width; ++$x) {
                $this->bytes[$y][$x] = $value;
            }
        }
    }
    public function __clone()
    {
        $this->bytes = clone $this->bytes;
        foreach ($this->bytes as $index => $row) {
            $this->bytes[$index] = clone $row;
        }
    }
    /**
     * Returns a string representation of the matrix.
     */
    public function __toString() : string
    {
        $result = '';
        for ($y = 0; $y < $this->height; $y++) {
            for ($x = 0; $x < $this->width; $x++) {
                switch ($this->bytes[$y][$x]) {
                    case 0:
                        $result .= ' 0';
                        break;
                    case 1:
                        $result .= ' 1';
                        break;
                    default:
                        $result .= '  ';
                        break;
                }
            }
            $result .= "\n";
        }
        return $result;
    }
}
vendor/bacon/bacon-qr-code/src/Encoder/MatrixUtil.php000064400000040231150755130600016520 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Encoder;

use WP2FA_Vendor\BaconQrCode\Common\BitArray;
use WP2FA_Vendor\BaconQrCode\Common\ErrorCorrectionLevel;
use WP2FA_Vendor\BaconQrCode\Common\Version;
use WP2FA_Vendor\BaconQrCode\Exception\RuntimeException;
use WP2FA_Vendor\BaconQrCode\Exception\WriterException;
/**
 * Matrix utility.
 */
final class MatrixUtil
{
    /**
     * Position detection pattern.
     */
    private const POSITION_DETECTION_PATTERN = [[1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 1], [1, 0, 1, 1, 1, 0, 1], [1, 0, 1, 1, 1, 0, 1], [1, 0, 1, 1, 1, 0, 1], [1, 0, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1, 1]];
    /**
     * Position adjustment pattern.
     */
    private const POSITION_ADJUSTMENT_PATTERN = [[1, 1, 1, 1, 1], [1, 0, 0, 0, 1], [1, 0, 1, 0, 1], [1, 0, 0, 0, 1], [1, 1, 1, 1, 1]];
    /**
     * Coordinates for position adjustment patterns for each version.
     */
    private const POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE = [
        [null, null, null, null, null, null, null],
        // Version 1
        [6, 18, null, null, null, null, null],
        // Version 2
        [6, 22, null, null, null, null, null],
        // Version 3
        [6, 26, null, null, null, null, null],
        // Version 4
        [6, 30, null, null, null, null, null],
        // Version 5
        [6, 34, null, null, null, null, null],
        // Version 6
        [6, 22, 38, null, null, null, null],
        // Version 7
        [6, 24, 42, null, null, null, null],
        // Version 8
        [6, 26, 46, null, null, null, null],
        // Version 9
        [6, 28, 50, null, null, null, null],
        // Version 10
        [6, 30, 54, null, null, null, null],
        // Version 11
        [6, 32, 58, null, null, null, null],
        // Version 12
        [6, 34, 62, null, null, null, null],
        // Version 13
        [6, 26, 46, 66, null, null, null],
        // Version 14
        [6, 26, 48, 70, null, null, null],
        // Version 15
        [6, 26, 50, 74, null, null, null],
        // Version 16
        [6, 30, 54, 78, null, null, null],
        // Version 17
        [6, 30, 56, 82, null, null, null],
        // Version 18
        [6, 30, 58, 86, null, null, null],
        // Version 19
        [6, 34, 62, 90, null, null, null],
        // Version 20
        [6, 28, 50, 72, 94, null, null],
        // Version 21
        [6, 26, 50, 74, 98, null, null],
        // Version 22
        [6, 30, 54, 78, 102, null, null],
        // Version 23
        [6, 28, 54, 80, 106, null, null],
        // Version 24
        [6, 32, 58, 84, 110, null, null],
        // Version 25
        [6, 30, 58, 86, 114, null, null],
        // Version 26
        [6, 34, 62, 90, 118, null, null],
        // Version 27
        [6, 26, 50, 74, 98, 122, null],
        // Version 28
        [6, 30, 54, 78, 102, 126, null],
        // Version 29
        [6, 26, 52, 78, 104, 130, null],
        // Version 30
        [6, 30, 56, 82, 108, 134, null],
        // Version 31
        [6, 34, 60, 86, 112, 138, null],
        // Version 32
        [6, 30, 58, 86, 114, 142, null],
        // Version 33
        [6, 34, 62, 90, 118, 146, null],
        // Version 34
        [6, 30, 54, 78, 102, 126, 150],
        // Version 35
        [6, 24, 50, 76, 102, 128, 154],
        // Version 36
        [6, 28, 54, 80, 106, 132, 158],
        // Version 37
        [6, 32, 58, 84, 110, 136, 162],
        // Version 38
        [6, 26, 54, 82, 110, 138, 166],
        // Version 39
        [6, 30, 58, 86, 114, 142, 170],
    ];
    /**
     * Type information coordinates.
     */
    private const TYPE_INFO_COORDINATES = [[8, 0], [8, 1], [8, 2], [8, 3], [8, 4], [8, 5], [8, 7], [8, 8], [7, 8], [5, 8], [4, 8], [3, 8], [2, 8], [1, 8], [0, 8]];
    /**
     * Version information polynomial.
     */
    private const VERSION_INFO_POLY = 0x1f25;
    /**
     * Type information polynomial.
     */
    private const TYPE_INFO_POLY = 0x537;
    /**
     * Type information mask pattern.
     */
    private const TYPE_INFO_MASK_PATTERN = 0x5412;
    /**
     * Clears a given matrix.
     */
    public static function clearMatrix(ByteMatrix $matrix) : void
    {
        $matrix->clear(-1);
    }
    /**
     * Builds a complete matrix.
     */
    public static function buildMatrix(BitArray $dataBits, ErrorCorrectionLevel $level, Version $version, int $maskPattern, ByteMatrix $matrix) : void
    {
        self::clearMatrix($matrix);
        self::embedBasicPatterns($version, $matrix);
        self::embedTypeInfo($level, $maskPattern, $matrix);
        self::maybeEmbedVersionInfo($version, $matrix);
        self::embedDataBits($dataBits, $maskPattern, $matrix);
    }
    /**
     * Removes the position detection patterns from a matrix.
     *
     * This can be useful if you need to render those patterns separately.
     */
    public static function removePositionDetectionPatterns(ByteMatrix $matrix) : void
    {
        $pdpWidth = \count(self::POSITION_DETECTION_PATTERN[0]);
        self::removePositionDetectionPattern(0, 0, $matrix);
        self::removePositionDetectionPattern($matrix->getWidth() - $pdpWidth, 0, $matrix);
        self::removePositionDetectionPattern(0, $matrix->getWidth() - $pdpWidth, $matrix);
    }
    /**
     * Embeds type information into a matrix.
     */
    private static function embedTypeInfo(ErrorCorrectionLevel $level, int $maskPattern, ByteMatrix $matrix) : void
    {
        $typeInfoBits = new BitArray();
        self::makeTypeInfoBits($level, $maskPattern, $typeInfoBits);
        $typeInfoBitsSize = $typeInfoBits->getSize();
        for ($i = 0; $i < $typeInfoBitsSize; ++$i) {
            $bit = $typeInfoBits->get($typeInfoBitsSize - 1 - $i);
            $x1 = self::TYPE_INFO_COORDINATES[$i][0];
            $y1 = self::TYPE_INFO_COORDINATES[$i][1];
            $matrix->set($x1, $y1, (int) $bit);
            if ($i < 8) {
                $x2 = $matrix->getWidth() - $i - 1;
                $y2 = 8;
            } else {
                $x2 = 8;
                $y2 = $matrix->getHeight() - 7 + ($i - 8);
            }
            $matrix->set($x2, $y2, (int) $bit);
        }
    }
    /**
     * Generates type information bits and appends them to a bit array.
     *
     * @throws RuntimeException if bit array resulted in invalid size
     */
    private static function makeTypeInfoBits(ErrorCorrectionLevel $level, int $maskPattern, BitArray $bits) : void
    {
        $typeInfo = $level->getBits() << 3 | $maskPattern;
        $bits->appendBits($typeInfo, 5);
        $bchCode = self::calculateBchCode($typeInfo, self::TYPE_INFO_POLY);
        $bits->appendBits($bchCode, 10);
        $maskBits = new BitArray();
        $maskBits->appendBits(self::TYPE_INFO_MASK_PATTERN, 15);
        $bits->xorBits($maskBits);
        if (15 !== $bits->getSize()) {
            throw new RuntimeException('Bit array resulted in invalid size: ' . $bits->getSize());
        }
    }
    /**
     * Embeds version information if required.
     */
    private static function maybeEmbedVersionInfo(Version $version, ByteMatrix $matrix) : void
    {
        if ($version->getVersionNumber() < 7) {
            return;
        }
        $versionInfoBits = new BitArray();
        self::makeVersionInfoBits($version, $versionInfoBits);
        $bitIndex = 6 * 3 - 1;
        for ($i = 0; $i < 6; ++$i) {
            for ($j = 0; $j < 3; ++$j) {
                $bit = $versionInfoBits->get($bitIndex);
                --$bitIndex;
                $matrix->set($i, $matrix->getHeight() - 11 + $j, (int) $bit);
                $matrix->set($matrix->getHeight() - 11 + $j, $i, (int) $bit);
            }
        }
    }
    /**
     * Generates version information bits and appends them to a bit array.
     *
     * @throws RuntimeException if bit array resulted in invalid size
     */
    private static function makeVersionInfoBits(Version $version, BitArray $bits) : void
    {
        $bits->appendBits($version->getVersionNumber(), 6);
        $bchCode = self::calculateBchCode($version->getVersionNumber(), self::VERSION_INFO_POLY);
        $bits->appendBits($bchCode, 12);
        if (18 !== $bits->getSize()) {
            throw new RuntimeException('Bit array resulted in invalid size: ' . $bits->getSize());
        }
    }
    /**
     * Calculates the BCH code for a value and a polynomial.
     */
    private static function calculateBchCode(int $value, int $poly) : int
    {
        $msbSetInPoly = self::findMsbSet($poly);
        $value <<= $msbSetInPoly - 1;
        while (self::findMsbSet($value) >= $msbSetInPoly) {
            $value ^= $poly << self::findMsbSet($value) - $msbSetInPoly;
        }
        return $value;
    }
    /**
     * Finds and MSB set.
     */
    private static function findMsbSet(int $value) : int
    {
        $numDigits = 0;
        while (0 !== $value) {
            $value >>= 1;
            ++$numDigits;
        }
        return $numDigits;
    }
    /**
     * Embeds basic patterns into a matrix.
     */
    private static function embedBasicPatterns(Version $version, ByteMatrix $matrix) : void
    {
        self::embedPositionDetectionPatternsAndSeparators($matrix);
        self::embedDarkDotAtLeftBottomCorner($matrix);
        self::maybeEmbedPositionAdjustmentPatterns($version, $matrix);
        self::embedTimingPatterns($matrix);
    }
    /**
     * Embeds position detection patterns and separators into a byte matrix.
     */
    private static function embedPositionDetectionPatternsAndSeparators(ByteMatrix $matrix) : void
    {
        $pdpWidth = \count(self::POSITION_DETECTION_PATTERN[0]);
        self::embedPositionDetectionPattern(0, 0, $matrix);
        self::embedPositionDetectionPattern($matrix->getWidth() - $pdpWidth, 0, $matrix);
        self::embedPositionDetectionPattern(0, $matrix->getWidth() - $pdpWidth, $matrix);
        $hspWidth = 8;
        self::embedHorizontalSeparationPattern(0, $hspWidth - 1, $matrix);
        self::embedHorizontalSeparationPattern($matrix->getWidth() - $hspWidth, $hspWidth - 1, $matrix);
        self::embedHorizontalSeparationPattern(0, $matrix->getWidth() - $hspWidth, $matrix);
        $vspSize = 7;
        self::embedVerticalSeparationPattern($vspSize, 0, $matrix);
        self::embedVerticalSeparationPattern($matrix->getHeight() - $vspSize - 1, 0, $matrix);
        self::embedVerticalSeparationPattern($vspSize, $matrix->getHeight() - $vspSize, $matrix);
    }
    /**
     * Embeds a single position detection pattern into a byte matrix.
     */
    private static function embedPositionDetectionPattern(int $xStart, int $yStart, ByteMatrix $matrix) : void
    {
        for ($y = 0; $y < 7; ++$y) {
            for ($x = 0; $x < 7; ++$x) {
                $matrix->set($xStart + $x, $yStart + $y, self::POSITION_DETECTION_PATTERN[$y][$x]);
            }
        }
    }
    private static function removePositionDetectionPattern(int $xStart, int $yStart, ByteMatrix $matrix) : void
    {
        for ($y = 0; $y < 7; ++$y) {
            for ($x = 0; $x < 7; ++$x) {
                $matrix->set($xStart + $x, $yStart + $y, 0);
            }
        }
    }
    /**
     * Embeds a single horizontal separation pattern.
     *
     * @throws RuntimeException if a byte was already set
     */
    private static function embedHorizontalSeparationPattern(int $xStart, int $yStart, ByteMatrix $matrix) : void
    {
        for ($x = 0; $x < 8; $x++) {
            if (-1 !== $matrix->get($xStart + $x, $yStart)) {
                throw new RuntimeException('Byte already set');
            }
            $matrix->set($xStart + $x, $yStart, 0);
        }
    }
    /**
     * Embeds a single vertical separation pattern.
     *
     * @throws RuntimeException if a byte was already set
     */
    private static function embedVerticalSeparationPattern(int $xStart, int $yStart, ByteMatrix $matrix) : void
    {
        for ($y = 0; $y < 7; $y++) {
            if (-1 !== $matrix->get($xStart, $yStart + $y)) {
                throw new RuntimeException('Byte already set');
            }
            $matrix->set($xStart, $yStart + $y, 0);
        }
    }
    /**
     * Embeds a dot at the left bottom corner.
     *
     * @throws RuntimeException if a byte was already set to 0
     */
    private static function embedDarkDotAtLeftBottomCorner(ByteMatrix $matrix) : void
    {
        if (0 === $matrix->get(8, $matrix->getHeight() - 8)) {
            throw new RuntimeException('Byte already set to 0');
        }
        $matrix->set(8, $matrix->getHeight() - 8, 1);
    }
    /**
     * Embeds position adjustment patterns if required.
     */
    private static function maybeEmbedPositionAdjustmentPatterns(Version $version, ByteMatrix $matrix) : void
    {
        if ($version->getVersionNumber() < 2) {
            return;
        }
        $index = $version->getVersionNumber() - 1;
        $coordinates = self::POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE[$index];
        $numCoordinates = \count($coordinates);
        for ($i = 0; $i < $numCoordinates; ++$i) {
            for ($j = 0; $j < $numCoordinates; ++$j) {
                $y = $coordinates[$i];
                $x = $coordinates[$j];
                if (null === $x || null === $y) {
                    continue;
                }
                if (-1 === $matrix->get($x, $y)) {
                    self::embedPositionAdjustmentPattern($x - 2, $y - 2, $matrix);
                }
            }
        }
    }
    /**
     * Embeds a single position adjustment pattern.
     */
    private static function embedPositionAdjustmentPattern(int $xStart, int $yStart, ByteMatrix $matrix) : void
    {
        for ($y = 0; $y < 5; $y++) {
            for ($x = 0; $x < 5; $x++) {
                $matrix->set($xStart + $x, $yStart + $y, self::POSITION_ADJUSTMENT_PATTERN[$y][$x]);
            }
        }
    }
    /**
     * Embeds timing patterns into a matrix.
     */
    private static function embedTimingPatterns(ByteMatrix $matrix) : void
    {
        $matrixWidth = $matrix->getWidth();
        for ($i = 8; $i < $matrixWidth - 8; ++$i) {
            $bit = ($i + 1) % 2;
            if (-1 === $matrix->get($i, 6)) {
                $matrix->set($i, 6, $bit);
            }
            if (-1 === $matrix->get(6, $i)) {
                $matrix->set(6, $i, $bit);
            }
        }
    }
    /**
     * Embeds "dataBits" using "getMaskPattern".
     *
     * For debugging purposes, it skips masking process if "getMaskPattern" is -1. See 8.7 of JISX0510:2004 (p.38) for
     * how to embed data bits.
     *
     * @throws WriterException if not all bits could be consumed
     */
    private static function embedDataBits(BitArray $dataBits, int $maskPattern, ByteMatrix $matrix) : void
    {
        $bitIndex = 0;
        $direction = -1;
        // Start from the right bottom cell.
        $x = $matrix->getWidth() - 1;
        $y = $matrix->getHeight() - 1;
        while ($x > 0) {
            // Skip vertical timing pattern.
            if (6 === $x) {
                --$x;
            }
            while ($y >= 0 && $y < $matrix->getHeight()) {
                for ($i = 0; $i < 2; $i++) {
                    $xx = $x - $i;
                    // Skip the cell if it's not empty.
                    if (-1 !== $matrix->get($xx, $y)) {
                        continue;
                    }
                    if ($bitIndex < $dataBits->getSize()) {
                        $bit = $dataBits->get($bitIndex);
                        ++$bitIndex;
                    } else {
                        // Padding bit. If there is no bit left, we'll fill the
                        // left cells with 0, as described in 8.4.9 of
                        // JISX0510:2004 (p. 24).
                        $bit = \false;
                    }
                    // Skip masking if maskPattern is -1.
                    if (-1 !== $maskPattern && MaskUtil::getDataMaskBit($maskPattern, $xx, $y)) {
                        $bit = !$bit;
                    }
                    $matrix->set($xx, $y, (int) $bit);
                }
                $y += $direction;
            }
            $direction = -$direction;
            $y += $direction;
            $x -= 2;
        }
        // All bits should be consumed
        if ($dataBits->getSize() !== $bitIndex) {
            throw new WriterException('Not all bits consumed (' . $bitIndex . ' out of ' . $dataBits->getSize() . ')');
        }
    }
}
vendor/bacon/bacon-qr-code/src/Encoder/Encoder.php000064400000053157150755130600016010 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Encoder;

use WP2FA_Vendor\BaconQrCode\Common\BitArray;
use WP2FA_Vendor\BaconQrCode\Common\CharacterSetEci;
use WP2FA_Vendor\BaconQrCode\Common\ErrorCorrectionLevel;
use WP2FA_Vendor\BaconQrCode\Common\Mode;
use WP2FA_Vendor\BaconQrCode\Common\ReedSolomonCodec;
use WP2FA_Vendor\BaconQrCode\Common\Version;
use WP2FA_Vendor\BaconQrCode\Exception\WriterException;
use SplFixedArray;
/**
 * Encoder.
 */
final class Encoder
{
    /**
     * Default byte encoding.
     */
    public const DEFAULT_BYTE_MODE_ECODING = 'ISO-8859-1';
    /**
     * The original table is defined in the table 5 of JISX0510:2004 (p.19).
     */
    private const ALPHANUMERIC_TABLE = [
        -1,
        -1,
        -1,
        -1,
        -1,
        -1,
        -1,
        -1,
        -1,
        -1,
        -1,
        -1,
        -1,
        -1,
        -1,
        -1,
        // 0x00-0x0f
        -1,
        -1,
        -1,
        -1,
        -1,
        -1,
        -1,
        -1,
        -1,
        -1,
        -1,
        -1,
        -1,
        -1,
        -1,
        -1,
        // 0x10-0x1f
        36,
        -1,
        -1,
        -1,
        37,
        38,
        -1,
        -1,
        -1,
        -1,
        39,
        40,
        -1,
        41,
        42,
        43,
        // 0x20-0x2f
        0,
        1,
        2,
        3,
        4,
        5,
        6,
        7,
        8,
        9,
        44,
        -1,
        -1,
        -1,
        -1,
        -1,
        // 0x30-0x3f
        -1,
        10,
        11,
        12,
        13,
        14,
        15,
        16,
        17,
        18,
        19,
        20,
        21,
        22,
        23,
        24,
        // 0x40-0x4f
        25,
        26,
        27,
        28,
        29,
        30,
        31,
        32,
        33,
        34,
        35,
        -1,
        -1,
        -1,
        -1,
        -1,
    ];
    /**
     * Codec cache.
     *
     * @var array<string,ReedSolomonCodec>
     */
    private static $codecs = [];
    /**
     * Encodes "content" with the error correction level "ecLevel".
     */
    public static function encode(string $content, ErrorCorrectionLevel $ecLevel, string $encoding = self::DEFAULT_BYTE_MODE_ECODING, ?Version $forcedVersion = null) : QrCode
    {
        // Pick an encoding mode appropriate for the content. Note that this
        // will not attempt to use multiple modes / segments even if that were
        // more efficient. Would be nice.
        $mode = self::chooseMode($content, $encoding);
        // This will store the header information, like mode and length, as well
        // as "header" segments like an ECI segment.
        $headerBits = new BitArray();
        // Append ECI segment if applicable
        if (Mode::BYTE() === $mode && self::DEFAULT_BYTE_MODE_ECODING !== $encoding) {
            $eci = CharacterSetEci::getCharacterSetEciByName($encoding);
            if (null !== $eci) {
                self::appendEci($eci, $headerBits);
            }
        }
        // (With ECI in place,) Write the mode marker
        self::appendModeInfo($mode, $headerBits);
        // Collect data within the main segment, separately, to count its size
        // if needed. Don't add it to main payload yet.
        $dataBits = new BitArray();
        self::appendBytes($content, $mode, $dataBits, $encoding);
        // Hard part: need to know version to know how many bits length takes.
        // But need to know how many bits it takes to know version. First we
        // take a guess at version by assuming version will be the minimum, 1:
        $provisionalBitsNeeded = $headerBits->getSize() + $mode->getCharacterCountBits(Version::getVersionForNumber(1)) + $dataBits->getSize();
        $provisionalVersion = self::chooseVersion($provisionalBitsNeeded, $ecLevel);
        // Use that guess to calculate the right version. I am still not sure
        // this works in 100% of cases.
        $bitsNeeded = $headerBits->getSize() + $mode->getCharacterCountBits($provisionalVersion) + $dataBits->getSize();
        $version = self::chooseVersion($bitsNeeded, $ecLevel);
        if (null !== $forcedVersion) {
            // Forced version check
            if ($version->getVersionNumber() <= $forcedVersion->getVersionNumber()) {
                // Calculated minimum version is same or equal as forced version
                $version = $forcedVersion;
            } else {
                throw new WriterException('Invalid version! Calculated version: ' . $version->getVersionNumber() . ', requested version: ' . $forcedVersion->getVersionNumber());
            }
        }
        $headerAndDataBits = new BitArray();
        $headerAndDataBits->appendBitArray($headerBits);
        // Find "length" of main segment and write it.
        $numLetters = Mode::BYTE() === $mode ? $dataBits->getSizeInBytes() : \strlen($content);
        self::appendLengthInfo($numLetters, $version, $mode, $headerAndDataBits);
        // Put data together into the overall payload.
        $headerAndDataBits->appendBitArray($dataBits);
        $ecBlocks = $version->getEcBlocksForLevel($ecLevel);
        $numDataBytes = $version->getTotalCodewords() - $ecBlocks->getTotalEcCodewords();
        // Terminate the bits properly.
        self::terminateBits($numDataBytes, $headerAndDataBits);
        // Interleave data bits with error correction code.
        $finalBits = self::interleaveWithEcBytes($headerAndDataBits, $version->getTotalCodewords(), $numDataBytes, $ecBlocks->getNumBlocks());
        // Choose the mask pattern.
        $dimension = $version->getDimensionForVersion();
        $matrix = new ByteMatrix($dimension, $dimension);
        $maskPattern = self::chooseMaskPattern($finalBits, $ecLevel, $version, $matrix);
        // Build the matrix.
        MatrixUtil::buildMatrix($finalBits, $ecLevel, $version, $maskPattern, $matrix);
        return new QrCode($mode, $ecLevel, $version, $maskPattern, $matrix);
    }
    /**
     * Gets the alphanumeric code for a byte.
     */
    private static function getAlphanumericCode(int $code) : int
    {
        if (isset(self::ALPHANUMERIC_TABLE[$code])) {
            return self::ALPHANUMERIC_TABLE[$code];
        }
        return -1;
    }
    /**
     * Chooses the best mode for a given content.
     */
    private static function chooseMode(string $content, string $encoding = null) : Mode
    {
        if (null !== $encoding && 0 === \strcasecmp($encoding, 'SHIFT-JIS')) {
            return self::isOnlyDoubleByteKanji($content) ? Mode::KANJI() : Mode::BYTE();
        }
        $hasNumeric = \false;
        $hasAlphanumeric = \false;
        $contentLength = \strlen($content);
        for ($i = 0; $i < $contentLength; ++$i) {
            $char = $content[$i];
            if (\ctype_digit($char)) {
                $hasNumeric = \true;
            } elseif (-1 !== self::getAlphanumericCode(\ord($char))) {
                $hasAlphanumeric = \true;
            } else {
                return Mode::BYTE();
            }
        }
        if ($hasAlphanumeric) {
            return Mode::ALPHANUMERIC();
        } elseif ($hasNumeric) {
            return Mode::NUMERIC();
        }
        return Mode::BYTE();
    }
    /**
     * Calculates the mask penalty for a matrix.
     */
    private static function calculateMaskPenalty(ByteMatrix $matrix) : int
    {
        return MaskUtil::applyMaskPenaltyRule1($matrix) + MaskUtil::applyMaskPenaltyRule2($matrix) + MaskUtil::applyMaskPenaltyRule3($matrix) + MaskUtil::applyMaskPenaltyRule4($matrix);
    }
    /**
     * Checks if content only consists of double-byte kanji characters.
     */
    private static function isOnlyDoubleByteKanji(string $content) : bool
    {
        $bytes = @\iconv('utf-8', 'SHIFT-JIS', $content);
        if (\false === $bytes) {
            return \false;
        }
        $length = \strlen($bytes);
        if (0 !== $length % 2) {
            return \false;
        }
        for ($i = 0; $i < $length; $i += 2) {
            $byte = $bytes[$i] & 0xff;
            if (($byte < 0x81 || $byte > 0x9f) && $byte < 0xe0 || $byte > 0xeb) {
                return \false;
            }
        }
        return \true;
    }
    /**
     * Chooses the best mask pattern for a matrix.
     */
    private static function chooseMaskPattern(BitArray $bits, ErrorCorrectionLevel $ecLevel, Version $version, ByteMatrix $matrix) : int
    {
        $minPenalty = \PHP_INT_MAX;
        $bestMaskPattern = -1;
        for ($maskPattern = 0; $maskPattern < QrCode::NUM_MASK_PATTERNS; ++$maskPattern) {
            MatrixUtil::buildMatrix($bits, $ecLevel, $version, $maskPattern, $matrix);
            $penalty = self::calculateMaskPenalty($matrix);
            if ($penalty < $minPenalty) {
                $minPenalty = $penalty;
                $bestMaskPattern = $maskPattern;
            }
        }
        return $bestMaskPattern;
    }
    /**
     * Chooses the best version for the input.
     *
     * @throws WriterException if data is too big
     */
    private static function chooseVersion(int $numInputBits, ErrorCorrectionLevel $ecLevel) : Version
    {
        for ($versionNum = 1; $versionNum <= 40; ++$versionNum) {
            $version = Version::getVersionForNumber($versionNum);
            $numBytes = $version->getTotalCodewords();
            $ecBlocks = $version->getEcBlocksForLevel($ecLevel);
            $numEcBytes = $ecBlocks->getTotalEcCodewords();
            $numDataBytes = $numBytes - $numEcBytes;
            $totalInputBytes = \intdiv($numInputBits + 8, 8);
            if ($numDataBytes >= $totalInputBytes) {
                return $version;
            }
        }
        throw new WriterException('Data too big');
    }
    /**
     * Terminates the bits in a bit array.
     *
     * @throws WriterException if data bits cannot fit in the QR code
     * @throws WriterException if bits size does not equal the capacity
     */
    private static function terminateBits(int $numDataBytes, BitArray $bits) : void
    {
        $capacity = $numDataBytes << 3;
        if ($bits->getSize() > $capacity) {
            throw new WriterException('Data bits cannot fit in the QR code');
        }
        for ($i = 0; $i < 4 && $bits->getSize() < $capacity; ++$i) {
            $bits->appendBit(\false);
        }
        $numBitsInLastByte = $bits->getSize() & 0x7;
        if ($numBitsInLastByte > 0) {
            for ($i = $numBitsInLastByte; $i < 8; ++$i) {
                $bits->appendBit(\false);
            }
        }
        $numPaddingBytes = $numDataBytes - $bits->getSizeInBytes();
        for ($i = 0; $i < $numPaddingBytes; ++$i) {
            $bits->appendBits(0 === ($i & 0x1) ? 0xec : 0x11, 8);
        }
        if ($bits->getSize() !== $capacity) {
            throw new WriterException('Bits size does not equal capacity');
        }
    }
    /**
     * Gets number of data- and EC bytes for a block ID.
     *
     * @return int[]
     * @throws WriterException if block ID is too large
     * @throws WriterException if EC bytes mismatch
     * @throws WriterException if RS blocks mismatch
     * @throws WriterException if total bytes mismatch
     */
    private static function getNumDataBytesAndNumEcBytesForBlockId(int $numTotalBytes, int $numDataBytes, int $numRsBlocks, int $blockId) : array
    {
        if ($blockId >= $numRsBlocks) {
            throw new WriterException('Block ID too large');
        }
        $numRsBlocksInGroup2 = $numTotalBytes % $numRsBlocks;
        $numRsBlocksInGroup1 = $numRsBlocks - $numRsBlocksInGroup2;
        $numTotalBytesInGroup1 = \intdiv($numTotalBytes, $numRsBlocks);
        $numTotalBytesInGroup2 = $numTotalBytesInGroup1 + 1;
        $numDataBytesInGroup1 = \intdiv($numDataBytes, $numRsBlocks);
        $numDataBytesInGroup2 = $numDataBytesInGroup1 + 1;
        $numEcBytesInGroup1 = $numTotalBytesInGroup1 - $numDataBytesInGroup1;
        $numEcBytesInGroup2 = $numTotalBytesInGroup2 - $numDataBytesInGroup2;
        if ($numEcBytesInGroup1 !== $numEcBytesInGroup2) {
            throw new WriterException('EC bytes mismatch');
        }
        if ($numRsBlocks !== $numRsBlocksInGroup1 + $numRsBlocksInGroup2) {
            throw new WriterException('RS blocks mismatch');
        }
        if ($numTotalBytes !== ($numDataBytesInGroup1 + $numEcBytesInGroup1) * $numRsBlocksInGroup1 + ($numDataBytesInGroup2 + $numEcBytesInGroup2) * $numRsBlocksInGroup2) {
            throw new WriterException('Total bytes mismatch');
        }
        if ($blockId < $numRsBlocksInGroup1) {
            return [$numDataBytesInGroup1, $numEcBytesInGroup1];
        } else {
            return [$numDataBytesInGroup2, $numEcBytesInGroup2];
        }
    }
    /**
     * Interleaves data with EC bytes.
     *
     * @throws WriterException if number of bits and data bytes does not match
     * @throws WriterException if data bytes does not match offset
     * @throws WriterException if an interleaving error occurs
     */
    private static function interleaveWithEcBytes(BitArray $bits, int $numTotalBytes, int $numDataBytes, int $numRsBlocks) : BitArray
    {
        if ($bits->getSizeInBytes() !== $numDataBytes) {
            throw new WriterException('Number of bits and data bytes does not match');
        }
        $dataBytesOffset = 0;
        $maxNumDataBytes = 0;
        $maxNumEcBytes = 0;
        $blocks = new SplFixedArray($numRsBlocks);
        for ($i = 0; $i < $numRsBlocks; ++$i) {
            list($numDataBytesInBlock, $numEcBytesInBlock) = self::getNumDataBytesAndNumEcBytesForBlockId($numTotalBytes, $numDataBytes, $numRsBlocks, $i);
            $size = $numDataBytesInBlock;
            $dataBytes = $bits->toBytes(8 * $dataBytesOffset, $size);
            $ecBytes = self::generateEcBytes($dataBytes, $numEcBytesInBlock);
            $blocks[$i] = new BlockPair($dataBytes, $ecBytes);
            $maxNumDataBytes = \max($maxNumDataBytes, $size);
            $maxNumEcBytes = \max($maxNumEcBytes, \count($ecBytes));
            $dataBytesOffset += $numDataBytesInBlock;
        }
        if ($numDataBytes !== $dataBytesOffset) {
            throw new WriterException('Data bytes does not match offset');
        }
        $result = new BitArray();
        for ($i = 0; $i < $maxNumDataBytes; ++$i) {
            foreach ($blocks as $block) {
                $dataBytes = $block->getDataBytes();
                if ($i < \count($dataBytes)) {
                    $result->appendBits($dataBytes[$i], 8);
                }
            }
        }
        for ($i = 0; $i < $maxNumEcBytes; ++$i) {
            foreach ($blocks as $block) {
                $ecBytes = $block->getErrorCorrectionBytes();
                if ($i < \count($ecBytes)) {
                    $result->appendBits($ecBytes[$i], 8);
                }
            }
        }
        if ($numTotalBytes !== $result->getSizeInBytes()) {
            throw new WriterException('Interleaving error: ' . $numTotalBytes . ' and ' . $result->getSizeInBytes() . ' differ');
        }
        return $result;
    }
    /**
     * Generates EC bytes for given data.
     *
     * @param  SplFixedArray<int> $dataBytes
     * @return SplFixedArray<int>
     */
    private static function generateEcBytes(SplFixedArray $dataBytes, int $numEcBytesInBlock) : SplFixedArray
    {
        $numDataBytes = \count($dataBytes);
        $toEncode = new SplFixedArray($numDataBytes + $numEcBytesInBlock);
        for ($i = 0; $i < $numDataBytes; $i++) {
            $toEncode[$i] = $dataBytes[$i] & 0xff;
        }
        $ecBytes = new SplFixedArray($numEcBytesInBlock);
        $codec = self::getCodec($numDataBytes, $numEcBytesInBlock);
        $codec->encode($toEncode, $ecBytes);
        return $ecBytes;
    }
    /**
     * Gets an RS codec and caches it.
     */
    private static function getCodec(int $numDataBytes, int $numEcBytesInBlock) : ReedSolomonCodec
    {
        $cacheId = $numDataBytes . '-' . $numEcBytesInBlock;
        if (isset(self::$codecs[$cacheId])) {
            return self::$codecs[$cacheId];
        }
        return self::$codecs[$cacheId] = new ReedSolomonCodec(8, 0x11d, 0, 1, $numEcBytesInBlock, 255 - $numDataBytes - $numEcBytesInBlock);
    }
    /**
     * Appends mode information to a bit array.
     */
    private static function appendModeInfo(Mode $mode, BitArray $bits) : void
    {
        $bits->appendBits($mode->getBits(), 4);
    }
    /**
     * Appends length information to a bit array.
     *
     * @throws WriterException if num letters is bigger than expected
     */
    private static function appendLengthInfo(int $numLetters, Version $version, Mode $mode, BitArray $bits) : void
    {
        $numBits = $mode->getCharacterCountBits($version);
        if ($numLetters >= 1 << $numBits) {
            throw new WriterException($numLetters . ' is bigger than ' . ((1 << $numBits) - 1));
        }
        $bits->appendBits($numLetters, $numBits);
    }
    /**
     * Appends bytes to a bit array in a specific mode.
     *
     * @throws WriterException if an invalid mode was supplied
     */
    private static function appendBytes(string $content, Mode $mode, BitArray $bits, string $encoding) : void
    {
        switch ($mode) {
            case Mode::NUMERIC():
                self::appendNumericBytes($content, $bits);
                break;
            case Mode::ALPHANUMERIC():
                self::appendAlphanumericBytes($content, $bits);
                break;
            case Mode::BYTE():
                self::append8BitBytes($content, $bits, $encoding);
                break;
            case Mode::KANJI():
                self::appendKanjiBytes($content, $bits);
                break;
            default:
                throw new WriterException('Invalid mode: ' . $mode);
        }
    }
    /**
     * Appends numeric bytes to a bit array.
     */
    private static function appendNumericBytes(string $content, BitArray $bits) : void
    {
        $length = \strlen($content);
        $i = 0;
        while ($i < $length) {
            $num1 = (int) $content[$i];
            if ($i + 2 < $length) {
                // Encode three numeric letters in ten bits.
                $num2 = (int) $content[$i + 1];
                $num3 = (int) $content[$i + 2];
                $bits->appendBits($num1 * 100 + $num2 * 10 + $num3, 10);
                $i += 3;
            } elseif ($i + 1 < $length) {
                // Encode two numeric letters in seven bits.
                $num2 = (int) $content[$i + 1];
                $bits->appendBits($num1 * 10 + $num2, 7);
                $i += 2;
            } else {
                // Encode one numeric letter in four bits.
                $bits->appendBits($num1, 4);
                ++$i;
            }
        }
    }
    /**
     * Appends alpha-numeric bytes to a bit array.
     *
     * @throws WriterException if an invalid alphanumeric code was found
     */
    private static function appendAlphanumericBytes(string $content, BitArray $bits) : void
    {
        $length = \strlen($content);
        $i = 0;
        while ($i < $length) {
            $code1 = self::getAlphanumericCode(\ord($content[$i]));
            if (-1 === $code1) {
                throw new WriterException('Invalid alphanumeric code');
            }
            if ($i + 1 < $length) {
                $code2 = self::getAlphanumericCode(\ord($content[$i + 1]));
                if (-1 === $code2) {
                    throw new WriterException('Invalid alphanumeric code');
                }
                // Encode two alphanumeric letters in 11 bits.
                $bits->appendBits($code1 * 45 + $code2, 11);
                $i += 2;
            } else {
                // Encode one alphanumeric letter in six bits.
                $bits->appendBits($code1, 6);
                ++$i;
            }
        }
    }
    /**
     * Appends regular 8-bit bytes to a bit array.
     *
     * @throws WriterException if content cannot be encoded to target encoding
     */
    private static function append8BitBytes(string $content, BitArray $bits, string $encoding) : void
    {
        $bytes = @\iconv('utf-8', $encoding, $content);
        if (\false === $bytes) {
            throw new WriterException('Could not encode content to ' . $encoding);
        }
        $length = \strlen($bytes);
        for ($i = 0; $i < $length; $i++) {
            $bits->appendBits(\ord($bytes[$i]), 8);
        }
    }
    /**
     * Appends KANJI bytes to a bit array.
     *
     * @throws WriterException if content does not seem to be encoded in SHIFT-JIS
     * @throws WriterException if an invalid byte sequence occurs
     */
    private static function appendKanjiBytes(string $content, BitArray $bits) : void
    {
        if (\strlen($content) % 2 > 0) {
            // We just do a simple length check here. The for loop will check
            // individual characters.
            throw new WriterException('Content does not seem to be encoded in SHIFT-JIS');
        }
        $length = \strlen($content);
        for ($i = 0; $i < $length; $i += 2) {
            $byte1 = \ord($content[$i]) & 0xff;
            $byte2 = \ord($content[$i + 1]) & 0xff;
            $code = $byte1 << 8 | $byte2;
            if ($code >= 0x8140 && $code <= 0x9ffc) {
                $subtracted = $code - 0x8140;
            } elseif ($code >= 0xe040 && $code <= 0xebbf) {
                $subtracted = $code - 0xc140;
            } else {
                throw new WriterException('Invalid byte sequence');
            }
            $encoded = ($subtracted >> 8) * 0xc0 + ($subtracted & 0xff);
            $bits->appendBits($encoded, 13);
        }
    }
    /**
     * Appends ECI information to a bit array.
     */
    private static function appendEci(CharacterSetEci $eci, BitArray $bits) : void
    {
        $mode = Mode::ECI();
        $bits->appendBits($mode->getBits(), 4);
        $bits->appendBits($eci->getValue(), 8);
    }
}
vendor/bacon/bacon-qr-code/src/Encoder/BlockPair.php000064400000002135150755130600016265 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Encoder;

use SplFixedArray;
/**
 * Block pair.
 */
final class BlockPair
{
    /**
     * Data bytes in the block.
     *
     * @var SplFixedArray<int>
     */
    private $dataBytes;
    /**
     * Error correction bytes in the block.
     *
     * @var SplFixedArray<int>
     */
    private $errorCorrectionBytes;
    /**
     * Creates a new block pair.
     *
     * @param SplFixedArray<int> $data
     * @param SplFixedArray<int> $errorCorrection
     */
    public function __construct(SplFixedArray $data, SplFixedArray $errorCorrection)
    {
        $this->dataBytes = $data;
        $this->errorCorrectionBytes = $errorCorrection;
    }
    /**
     * Gets the data bytes.
     *
     * @return SplFixedArray<int>
     */
    public function getDataBytes() : SplFixedArray
    {
        return $this->dataBytes;
    }
    /**
     * Gets the error correction bytes.
     *
     * @return SplFixedArray<int>
     */
    public function getErrorCorrectionBytes() : SplFixedArray
    {
        return $this->errorCorrectionBytes;
    }
}
vendor/bacon/bacon-qr-code/src/Common/EcBlock.php000064400000001760150755130600015575 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Common;

/**
 * Encapsulates the parameters for one error-correction block in one symbol version.
 *
 * This includes the number of data codewords, and the number of times a block with these parameters is used
 * consecutively in the QR code version's format.
 */
final class EcBlock
{
    /**
     * How many times the block is used.
     *
     * @var int
     */
    private $count;
    /**
     * Number of data codewords.
     *
     * @var int
     */
    private $dataCodewords;
    public function __construct(int $count, int $dataCodewords)
    {
        $this->count = $count;
        $this->dataCodewords = $dataCodewords;
    }
    /**
     * Returns how many times the block is used.
     */
    public function getCount() : int
    {
        return $this->count;
    }
    /**
     * Returns the number of data codewords.
     */
    public function getDataCodewords() : int
    {
        return $this->dataCodewords;
    }
}
vendor/bacon/bacon-qr-code/src/Common/Mode.php000064400000004106150755130600015154 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Common;

use WP2FA_Vendor\DASPRiD\Enum\AbstractEnum;
/**
 * Enum representing various modes in which data can be encoded to bits.
 *
 * @method static self TERMINATOR()
 * @method static self NUMERIC()
 * @method static self ALPHANUMERIC()
 * @method static self STRUCTURED_APPEND()
 * @method static self BYTE()
 * @method static self ECI()
 * @method static self KANJI()
 * @method static self FNC1_FIRST_POSITION()
 * @method static self FNC1_SECOND_POSITION()
 * @method static self HANZI()
 */
final class Mode extends AbstractEnum
{
    protected const TERMINATOR = [[0, 0, 0], 0x0];
    protected const NUMERIC = [[10, 12, 14], 0x1];
    protected const ALPHANUMERIC = [[9, 11, 13], 0x2];
    protected const STRUCTURED_APPEND = [[0, 0, 0], 0x3];
    protected const BYTE = [[8, 16, 16], 0x4];
    protected const ECI = [[0, 0, 0], 0x7];
    protected const KANJI = [[8, 10, 12], 0x8];
    protected const FNC1_FIRST_POSITION = [[0, 0, 0], 0x5];
    protected const FNC1_SECOND_POSITION = [[0, 0, 0], 0x9];
    protected const HANZI = [[8, 10, 12], 0xd];
    /**
     * @var int[]
     */
    private $characterCountBitsForVersions;
    /**
     * @var int
     */
    private $bits;
    /**
     * @param int[] $characterCountBitsForVersions
     */
    protected function __construct(array $characterCountBitsForVersions, int $bits)
    {
        $this->characterCountBitsForVersions = $characterCountBitsForVersions;
        $this->bits = $bits;
    }
    /**
     * Returns the number of bits used in a specific QR code version.
     */
    public function getCharacterCountBits(Version $version) : int
    {
        $number = $version->getVersionNumber();
        if ($number <= 9) {
            $offset = 0;
        } elseif ($number <= 26) {
            $offset = 1;
        } else {
            $offset = 2;
        }
        return $this->characterCountBitsForVersions[$offset];
    }
    /**
     * Returns the four bits used to encode this mode.
     */
    public function getBits() : int
    {
        return $this->bits;
    }
}
vendor/bacon/bacon-qr-code/src/Common/Version.php000064400000040416150755130600015721 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Common;

use WP2FA_Vendor\BaconQrCode\Exception\InvalidArgumentException;
use SplFixedArray;
/**
 * Version representation.
 */
final class Version
{
    private const VERSION_DECODE_INFO = [0x7c94, 0x85bc, 0x9a99, 0xa4d3, 0xbbf6, 0xc762, 0xd847, 0xe60d, 0xf928, 0x10b78, 0x1145d, 0x12a17, 0x13532, 0x149a6, 0x15683, 0x168c9, 0x177ec, 0x18ec4, 0x191e1, 0x1afab, 0x1b08e, 0x1cc1a, 0x1d33f, 0x1ed75, 0x1f250, 0x209d5, 0x216f0, 0x228ba, 0x2379f, 0x24b0b, 0x2542e, 0x26a64, 0x27541, 0x28c69];
    /**
     * Version number of this version.
     *
     * @var int
     */
    private $versionNumber;
    /**
     * Alignment pattern centers.
     *
     * @var SplFixedArray
     */
    private $alignmentPatternCenters;
    /**
     * Error correction blocks.
     *
     * @var EcBlocks[]
     */
    private $ecBlocks;
    /**
     * Total number of codewords.
     *
     * @var int
     */
    private $totalCodewords;
    /**
     * Cached version instances.
     *
     * @var array<int, self>|null
     */
    private static $versions;
    /**
     * @param int[] $alignmentPatternCenters
     */
    private function __construct(int $versionNumber, array $alignmentPatternCenters, EcBlocks ...$ecBlocks)
    {
        $this->versionNumber = $versionNumber;
        $this->alignmentPatternCenters = $alignmentPatternCenters;
        $this->ecBlocks = $ecBlocks;
        $totalCodewords = 0;
        $ecCodewords = $ecBlocks[0]->getEcCodewordsPerBlock();
        foreach ($ecBlocks[0]->getEcBlocks() as $ecBlock) {
            $totalCodewords += $ecBlock->getCount() * ($ecBlock->getDataCodewords() + $ecCodewords);
        }
        $this->totalCodewords = $totalCodewords;
    }
    /**
     * Returns the version number.
     */
    public function getVersionNumber() : int
    {
        return $this->versionNumber;
    }
    /**
     * Returns the alignment pattern centers.
     *
     * @return int[]
     */
    public function getAlignmentPatternCenters() : array
    {
        return $this->alignmentPatternCenters;
    }
    /**
     * Returns the total number of codewords.
     */
    public function getTotalCodewords() : int
    {
        return $this->totalCodewords;
    }
    /**
     * Calculates the dimension for the current version.
     */
    public function getDimensionForVersion() : int
    {
        return 17 + 4 * $this->versionNumber;
    }
    /**
     * Returns the number of EC blocks for a specific EC level.
     */
    public function getEcBlocksForLevel(ErrorCorrectionLevel $ecLevel) : EcBlocks
    {
        return $this->ecBlocks[$ecLevel->ordinal()];
    }
    /**
     * Gets a provisional version number for a specific dimension.
     *
     * @throws InvalidArgumentException if dimension is not 1 mod 4
     */
    public static function getProvisionalVersionForDimension(int $dimension) : self
    {
        if (1 !== $dimension % 4) {
            throw new InvalidArgumentException('Dimension is not 1 mod 4');
        }
        return self::getVersionForNumber(\intdiv($dimension - 17, 4));
    }
    /**
     * Gets a version instance for a specific version number.
     *
     * @throws InvalidArgumentException if version number is out of range
     */
    public static function getVersionForNumber(int $versionNumber) : self
    {
        if ($versionNumber < 1 || $versionNumber > 40) {
            throw new InvalidArgumentException('Version number must be between 1 and 40');
        }
        return self::versions()[$versionNumber - 1];
    }
    /**
     * Decodes version information from an integer and returns the version.
     */
    public static function decodeVersionInformation(int $versionBits) : ?self
    {
        $bestDifference = \PHP_INT_MAX;
        $bestVersion = 0;
        foreach (self::VERSION_DECODE_INFO as $i => $targetVersion) {
            if ($targetVersion === $versionBits) {
                return self::getVersionForNumber($i + 7);
            }
            $bitsDifference = FormatInformation::numBitsDiffering($versionBits, $targetVersion);
            if ($bitsDifference < $bestDifference) {
                $bestVersion = $i + 7;
                $bestDifference = $bitsDifference;
            }
        }
        if ($bestDifference <= 3) {
            return self::getVersionForNumber($bestVersion);
        }
        return null;
    }
    /**
     * Builds the function pattern for the current version.
     */
    public function buildFunctionPattern() : BitMatrix
    {
        $dimension = $this->getDimensionForVersion();
        $bitMatrix = new BitMatrix($dimension);
        // Top left finder pattern + separator + format
        $bitMatrix->setRegion(0, 0, 9, 9);
        // Top right finder pattern + separator + format
        $bitMatrix->setRegion($dimension - 8, 0, 8, 9);
        // Bottom left finder pattern + separator + format
        $bitMatrix->setRegion(0, $dimension - 8, 9, 8);
        // Alignment patterns
        $max = \count($this->alignmentPatternCenters);
        for ($x = 0; $x < $max; ++$x) {
            $i = $this->alignmentPatternCenters[$x] - 2;
            for ($y = 0; $y < $max; ++$y) {
                if ($x === 0 && ($y === 0 || $y === $max - 1) || $x === $max - 1 && $y === 0) {
                    // No alignment patterns near the three finder paterns
                    continue;
                }
                $bitMatrix->setRegion($this->alignmentPatternCenters[$y] - 2, $i, 5, 5);
            }
        }
        // Vertical timing pattern
        $bitMatrix->setRegion(6, 9, 1, $dimension - 17);
        // Horizontal timing pattern
        $bitMatrix->setRegion(9, 6, $dimension - 17, 1);
        if ($this->versionNumber > 6) {
            // Version info, top right
            $bitMatrix->setRegion($dimension - 11, 0, 3, 6);
            // Version info, bottom left
            $bitMatrix->setRegion(0, $dimension - 11, 6, 3);
        }
        return $bitMatrix;
    }
    /**
     * Returns a string representation for the version.
     */
    public function __toString() : string
    {
        return (string) $this->versionNumber;
    }
    /**
     * Build and cache a specific version.
     *
     * See ISO 18004:2006 6.5.1 Table 9.
     *
     * @return array<int, self>
     */
    private static function versions() : array
    {
        if (null !== self::$versions) {
            return self::$versions;
        }
        return self::$versions = [new self(1, [], new EcBlocks(7, new EcBlock(1, 19)), new EcBlocks(10, new EcBlock(1, 16)), new EcBlocks(13, new EcBlock(1, 13)), new EcBlocks(17, new EcBlock(1, 9))), new self(2, [6, 18], new EcBlocks(10, new EcBlock(1, 34)), new EcBlocks(16, new EcBlock(1, 28)), new EcBlocks(22, new EcBlock(1, 22)), new EcBlocks(28, new EcBlock(1, 16))), new self(3, [6, 22], new EcBlocks(15, new EcBlock(1, 55)), new EcBlocks(26, new EcBlock(1, 44)), new EcBlocks(18, new EcBlock(2, 17)), new EcBlocks(22, new EcBlock(2, 13))), new self(4, [6, 26], new EcBlocks(20, new EcBlock(1, 80)), new EcBlocks(18, new EcBlock(2, 32)), new EcBlocks(26, new EcBlock(3, 24)), new EcBlocks(16, new EcBlock(4, 9))), new self(5, [6, 30], new EcBlocks(26, new EcBlock(1, 108)), new EcBlocks(24, new EcBlock(2, 43)), new EcBlocks(18, new EcBlock(2, 15), new EcBlock(2, 16)), new EcBlocks(22, new EcBlock(2, 11), new EcBlock(2, 12))), new self(6, [6, 34], new EcBlocks(18, new EcBlock(2, 68)), new EcBlocks(16, new EcBlock(4, 27)), new EcBlocks(24, new EcBlock(4, 19)), new EcBlocks(28, new EcBlock(4, 15))), new self(7, [6, 22, 38], new EcBlocks(20, new EcBlock(2, 78)), new EcBlocks(18, new EcBlock(4, 31)), new EcBlocks(18, new EcBlock(2, 14), new EcBlock(4, 15)), new EcBlocks(26, new EcBlock(4, 13), new EcBlock(1, 14))), new self(8, [6, 24, 42], new EcBlocks(24, new EcBlock(2, 97)), new EcBlocks(22, new EcBlock(2, 38), new EcBlock(2, 39)), new EcBlocks(22, new EcBlock(4, 18), new EcBlock(2, 19)), new EcBlocks(26, new EcBlock(4, 14), new EcBlock(2, 15))), new self(9, [6, 26, 46], new EcBlocks(30, new EcBlock(2, 116)), new EcBlocks(22, new EcBlock(3, 36), new EcBlock(2, 37)), new EcBlocks(20, new EcBlock(4, 16), new EcBlock(4, 17)), new EcBlocks(24, new EcBlock(4, 12), new EcBlock(4, 13))), new self(10, [6, 28, 50], new EcBlocks(18, new EcBlock(2, 68), new EcBlock(2, 69)), new EcBlocks(26, new EcBlock(4, 43), new EcBlock(1, 44)), new EcBlocks(24, new EcBlock(6, 19), new EcBlock(2, 20)), new EcBlocks(28, new EcBlock(6, 15), new EcBlock(2, 16))), new self(11, [6, 30, 54], new EcBlocks(20, new EcBlock(4, 81)), new EcBlocks(30, new EcBlock(1, 50), new EcBlock(4, 51)), new EcBlocks(28, new EcBlock(4, 22), new EcBlock(4, 23)), new EcBlocks(24, new EcBlock(3, 12), new EcBlock(8, 13))), new self(12, [6, 32, 58], new EcBlocks(24, new EcBlock(2, 92), new EcBlock(2, 93)), new EcBlocks(22, new EcBlock(6, 36), new EcBlock(2, 37)), new EcBlocks(26, new EcBlock(4, 20), new EcBlock(6, 21)), new EcBlocks(28, new EcBlock(7, 14), new EcBlock(4, 15))), new self(13, [6, 34, 62], new EcBlocks(26, new EcBlock(4, 107)), new EcBlocks(22, new EcBlock(8, 37), new EcBlock(1, 38)), new EcBlocks(24, new EcBlock(8, 20), new EcBlock(4, 21)), new EcBlocks(22, new EcBlock(12, 11), new EcBlock(4, 12))), new self(14, [6, 26, 46, 66], new EcBlocks(30, new EcBlock(3, 115), new EcBlock(1, 116)), new EcBlocks(24, new EcBlock(4, 40), new EcBlock(5, 41)), new EcBlocks(20, new EcBlock(11, 16), new EcBlock(5, 17)), new EcBlocks(24, new EcBlock(11, 12), new EcBlock(5, 13))), new self(15, [6, 26, 48, 70], new EcBlocks(22, new EcBlock(5, 87), new EcBlock(1, 88)), new EcBlocks(24, new EcBlock(5, 41), new EcBlock(5, 42)), new EcBlocks(30, new EcBlock(5, 24), new EcBlock(7, 25)), new EcBlocks(24, new EcBlock(11, 12), new EcBlock(7, 13))), new self(16, [6, 26, 50, 74], new EcBlocks(24, new EcBlock(5, 98), new EcBlock(1, 99)), new EcBlocks(28, new EcBlock(7, 45), new EcBlock(3, 46)), new EcBlocks(24, new EcBlock(15, 19), new EcBlock(2, 20)), new EcBlocks(30, new EcBlock(3, 15), new EcBlock(13, 16))), new self(17, [6, 30, 54, 78], new EcBlocks(28, new EcBlock(1, 107), new EcBlock(5, 108)), new EcBlocks(28, new EcBlock(10, 46), new EcBlock(1, 47)), new EcBlocks(28, new EcBlock(1, 22), new EcBlock(15, 23)), new EcBlocks(28, new EcBlock(2, 14), new EcBlock(17, 15))), new self(18, [6, 30, 56, 82], new EcBlocks(30, new EcBlock(5, 120), new EcBlock(1, 121)), new EcBlocks(26, new EcBlock(9, 43), new EcBlock(4, 44)), new EcBlocks(28, new EcBlock(17, 22), new EcBlock(1, 23)), new EcBlocks(28, new EcBlock(2, 14), new EcBlock(19, 15))), new self(19, [6, 30, 58, 86], new EcBlocks(28, new EcBlock(3, 113), new EcBlock(4, 114)), new EcBlocks(26, new EcBlock(3, 44), new EcBlock(11, 45)), new EcBlocks(26, new EcBlock(17, 21), new EcBlock(4, 22)), new EcBlocks(26, new EcBlock(9, 13), new EcBlock(16, 14))), new self(20, [6, 34, 62, 90], new EcBlocks(28, new EcBlock(3, 107), new EcBlock(5, 108)), new EcBlocks(26, new EcBlock(3, 41), new EcBlock(13, 42)), new EcBlocks(30, new EcBlock(15, 24), new EcBlock(5, 25)), new EcBlocks(28, new EcBlock(15, 15), new EcBlock(10, 16))), new self(21, [6, 28, 50, 72, 94], new EcBlocks(28, new EcBlock(4, 116), new EcBlock(4, 117)), new EcBlocks(26, new EcBlock(17, 42)), new EcBlocks(28, new EcBlock(17, 22), new EcBlock(6, 23)), new EcBlocks(30, new EcBlock(19, 16), new EcBlock(6, 17))), new self(22, [6, 26, 50, 74, 98], new EcBlocks(28, new EcBlock(2, 111), new EcBlock(7, 112)), new EcBlocks(28, new EcBlock(17, 46)), new EcBlocks(30, new EcBlock(7, 24), new EcBlock(16, 25)), new EcBlocks(24, new EcBlock(34, 13))), new self(23, [6, 30, 54, 78, 102], new EcBlocks(30, new EcBlock(4, 121), new EcBlock(5, 122)), new EcBlocks(28, new EcBlock(4, 47), new EcBlock(14, 48)), new EcBlocks(30, new EcBlock(11, 24), new EcBlock(14, 25)), new EcBlocks(30, new EcBlock(16, 15), new EcBlock(14, 16))), new self(24, [6, 28, 54, 80, 106], new EcBlocks(30, new EcBlock(6, 117), new EcBlock(4, 118)), new EcBlocks(28, new EcBlock(6, 45), new EcBlock(14, 46)), new EcBlocks(30, new EcBlock(11, 24), new EcBlock(16, 25)), new EcBlocks(30, new EcBlock(30, 16), new EcBlock(2, 17))), new self(25, [6, 32, 58, 84, 110], new EcBlocks(26, new EcBlock(8, 106), new EcBlock(4, 107)), new EcBlocks(28, new EcBlock(8, 47), new EcBlock(13, 48)), new EcBlocks(30, new EcBlock(7, 24), new EcBlock(22, 25)), new EcBlocks(30, new EcBlock(22, 15), new EcBlock(13, 16))), new self(26, [6, 30, 58, 86, 114], new EcBlocks(28, new EcBlock(10, 114), new EcBlock(2, 115)), new EcBlocks(28, new EcBlock(19, 46), new EcBlock(4, 47)), new EcBlocks(28, new EcBlock(28, 22), new EcBlock(6, 23)), new EcBlocks(30, new EcBlock(33, 16), new EcBlock(4, 17))), new self(27, [6, 34, 62, 90, 118], new EcBlocks(30, new EcBlock(8, 122), new EcBlock(4, 123)), new EcBlocks(28, new EcBlock(22, 45), new EcBlock(3, 46)), new EcBlocks(30, new EcBlock(8, 23), new EcBlock(26, 24)), new EcBlocks(30, new EcBlock(12, 15), new EcBlock(28, 16))), new self(28, [6, 26, 50, 74, 98, 122], new EcBlocks(30, new EcBlock(3, 117), new EcBlock(10, 118)), new EcBlocks(28, new EcBlock(3, 45), new EcBlock(23, 46)), new EcBlocks(30, new EcBlock(4, 24), new EcBlock(31, 25)), new EcBlocks(30, new EcBlock(11, 15), new EcBlock(31, 16))), new self(29, [6, 30, 54, 78, 102, 126], new EcBlocks(30, new EcBlock(7, 116), new EcBlock(7, 117)), new EcBlocks(28, new EcBlock(21, 45), new EcBlock(7, 46)), new EcBlocks(30, new EcBlock(1, 23), new EcBlock(37, 24)), new EcBlocks(30, new EcBlock(19, 15), new EcBlock(26, 16))), new self(30, [6, 26, 52, 78, 104, 130], new EcBlocks(30, new EcBlock(5, 115), new EcBlock(10, 116)), new EcBlocks(28, new EcBlock(19, 47), new EcBlock(10, 48)), new EcBlocks(30, new EcBlock(15, 24), new EcBlock(25, 25)), new EcBlocks(30, new EcBlock(23, 15), new EcBlock(25, 16))), new self(31, [6, 30, 56, 82, 108, 134], new EcBlocks(30, new EcBlock(13, 115), new EcBlock(3, 116)), new EcBlocks(28, new EcBlock(2, 46), new EcBlock(29, 47)), new EcBlocks(30, new EcBlock(42, 24), new EcBlock(1, 25)), new EcBlocks(30, new EcBlock(23, 15), new EcBlock(28, 16))), new self(32, [6, 34, 60, 86, 112, 138], new EcBlocks(30, new EcBlock(17, 115)), new EcBlocks(28, new EcBlock(10, 46), new EcBlock(23, 47)), new EcBlocks(30, new EcBlock(10, 24), new EcBlock(35, 25)), new EcBlocks(30, new EcBlock(19, 15), new EcBlock(35, 16))), new self(33, [6, 30, 58, 86, 114, 142], new EcBlocks(30, new EcBlock(17, 115), new EcBlock(1, 116)), new EcBlocks(28, new EcBlock(14, 46), new EcBlock(21, 47)), new EcBlocks(30, new EcBlock(29, 24), new EcBlock(19, 25)), new EcBlocks(30, new EcBlock(11, 15), new EcBlock(46, 16))), new self(34, [6, 34, 62, 90, 118, 146], new EcBlocks(30, new EcBlock(13, 115), new EcBlock(6, 116)), new EcBlocks(28, new EcBlock(14, 46), new EcBlock(23, 47)), new EcBlocks(30, new EcBlock(44, 24), new EcBlock(7, 25)), new EcBlocks(30, new EcBlock(59, 16), new EcBlock(1, 17))), new self(35, [6, 30, 54, 78, 102, 126, 150], new EcBlocks(30, new EcBlock(12, 121), new EcBlock(7, 122)), new EcBlocks(28, new EcBlock(12, 47), new EcBlock(26, 48)), new EcBlocks(30, new EcBlock(39, 24), new EcBlock(14, 25)), new EcBlocks(30, new EcBlock(22, 15), new EcBlock(41, 16))), new self(36, [6, 24, 50, 76, 102, 128, 154], new EcBlocks(30, new EcBlock(6, 121), new EcBlock(14, 122)), new EcBlocks(28, new EcBlock(6, 47), new EcBlock(34, 48)), new EcBlocks(30, new EcBlock(46, 24), new EcBlock(10, 25)), new EcBlocks(30, new EcBlock(2, 15), new EcBlock(64, 16))), new self(37, [6, 28, 54, 80, 106, 132, 158], new EcBlocks(30, new EcBlock(17, 122), new EcBlock(4, 123)), new EcBlocks(28, new EcBlock(29, 46), new EcBlock(14, 47)), new EcBlocks(30, new EcBlock(49, 24), new EcBlock(10, 25)), new EcBlocks(30, new EcBlock(24, 15), new EcBlock(46, 16))), new self(38, [6, 32, 58, 84, 110, 136, 162], new EcBlocks(30, new EcBlock(4, 122), new EcBlock(18, 123)), new EcBlocks(28, new EcBlock(13, 46), new EcBlock(32, 47)), new EcBlocks(30, new EcBlock(48, 24), new EcBlock(14, 25)), new EcBlocks(30, new EcBlock(42, 15), new EcBlock(32, 16))), new self(39, [6, 26, 54, 82, 110, 138, 166], new EcBlocks(30, new EcBlock(20, 117), new EcBlock(4, 118)), new EcBlocks(28, new EcBlock(40, 47), new EcBlock(7, 48)), new EcBlocks(30, new EcBlock(43, 24), new EcBlock(22, 25)), new EcBlocks(30, new EcBlock(10, 15), new EcBlock(67, 16))), new self(40, [6, 30, 58, 86, 114, 142, 170], new EcBlocks(30, new EcBlock(19, 118), new EcBlock(6, 119)), new EcBlocks(28, new EcBlock(18, 47), new EcBlock(31, 48)), new EcBlocks(30, new EcBlock(34, 24), new EcBlock(34, 25)), new EcBlocks(30, new EcBlock(20, 15), new EcBlock(61, 16)))];
    }
}
vendor/bacon/bacon-qr-code/src/Common/BitArray.php000064400000021240150755130600016003 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Common;

use WP2FA_Vendor\BaconQrCode\Exception\InvalidArgumentException;
use SplFixedArray;
/**
 * A simple, fast array of bits.
 */
final class BitArray
{
    /**
     * Bits represented as an array of integers.
     *
     * @var SplFixedArray<int>
     */
    private $bits;
    /**
     * Size of the bit array in bits.
     *
     * @var int
     */
    private $size;
    /**
     * Creates a new bit array with a given size.
     */
    public function __construct(int $size = 0)
    {
        $this->size = $size;
        $this->bits = SplFixedArray::fromArray(\array_fill(0, $this->size + 31 >> 3, 0));
    }
    /**
     * Gets the size in bits.
     */
    public function getSize() : int
    {
        return $this->size;
    }
    /**
     * Gets the size in bytes.
     */
    public function getSizeInBytes() : int
    {
        return $this->size + 7 >> 3;
    }
    /**
     * Ensures that the array has a minimum capacity.
     */
    public function ensureCapacity(int $size) : void
    {
        if ($size > \count($this->bits) << 5) {
            $this->bits->setSize($size + 31 >> 5);
        }
    }
    /**
     * Gets a specific bit.
     */
    public function get(int $i) : bool
    {
        return 0 !== ($this->bits[$i >> 5] & 1 << ($i & 0x1f));
    }
    /**
     * Sets a specific bit.
     */
    public function set(int $i) : void
    {
        $this->bits[$i >> 5] = $this->bits[$i >> 5] | 1 << ($i & 0x1f);
    }
    /**
     * Flips a specific bit.
     */
    public function flip(int $i) : void
    {
        $this->bits[$i >> 5] ^= 1 << ($i & 0x1f);
    }
    /**
     * Gets the next set bit position from a given position.
     */
    public function getNextSet(int $from) : int
    {
        if ($from >= $this->size) {
            return $this->size;
        }
        $bitsOffset = $from >> 5;
        $currentBits = $this->bits[$bitsOffset];
        $bitsLength = \count($this->bits);
        $currentBits &= ~((1 << ($from & 0x1f)) - 1);
        while (0 === $currentBits) {
            if (++$bitsOffset === $bitsLength) {
                return $this->size;
            }
            $currentBits = $this->bits[$bitsOffset];
        }
        $result = ($bitsOffset << 5) + BitUtils::numberOfTrailingZeros($currentBits);
        return $result > $this->size ? $this->size : $result;
    }
    /**
     * Gets the next unset bit position from a given position.
     */
    public function getNextUnset(int $from) : int
    {
        if ($from >= $this->size) {
            return $this->size;
        }
        $bitsOffset = $from >> 5;
        $currentBits = ~$this->bits[$bitsOffset];
        $bitsLength = \count($this->bits);
        $currentBits &= ~((1 << ($from & 0x1f)) - 1);
        while (0 === $currentBits) {
            if (++$bitsOffset === $bitsLength) {
                return $this->size;
            }
            $currentBits = ~$this->bits[$bitsOffset];
        }
        $result = ($bitsOffset << 5) + BitUtils::numberOfTrailingZeros($currentBits);
        return $result > $this->size ? $this->size : $result;
    }
    /**
     * Sets a bulk of bits.
     */
    public function setBulk(int $i, int $newBits) : void
    {
        $this->bits[$i >> 5] = $newBits;
    }
    /**
     * Sets a range of bits.
     *
     * @throws InvalidArgumentException if end is smaller than start
     */
    public function setRange(int $start, int $end) : void
    {
        if ($end < $start) {
            throw new InvalidArgumentException('End must be greater or equal to start');
        }
        if ($end === $start) {
            return;
        }
        --$end;
        $firstInt = $start >> 5;
        $lastInt = $end >> 5;
        for ($i = $firstInt; $i <= $lastInt; ++$i) {
            $firstBit = $i > $firstInt ? 0 : $start & 0x1f;
            $lastBit = $i < $lastInt ? 31 : $end & 0x1f;
            if (0 === $firstBit && 31 === $lastBit) {
                $mask = 0x7fffffff;
            } else {
                $mask = 0;
                for ($j = $firstBit; $j < $lastBit; ++$j) {
                    $mask |= 1 << $j;
                }
            }
            $this->bits[$i] = $this->bits[$i] | $mask;
        }
    }
    /**
     * Clears the bit array, unsetting every bit.
     */
    public function clear() : void
    {
        $bitsLength = \count($this->bits);
        for ($i = 0; $i < $bitsLength; ++$i) {
            $this->bits[$i] = 0;
        }
    }
    /**
     * Checks if a range of bits is set or not set.
     * @throws InvalidArgumentException if end is smaller than start
     */
    public function isRange(int $start, int $end, bool $value) : bool
    {
        if ($end < $start) {
            throw new InvalidArgumentException('End must be greater or equal to start');
        }
        if ($end === $start) {
            return \true;
        }
        --$end;
        $firstInt = $start >> 5;
        $lastInt = $end >> 5;
        for ($i = $firstInt; $i <= $lastInt; ++$i) {
            $firstBit = $i > $firstInt ? 0 : $start & 0x1f;
            $lastBit = $i < $lastInt ? 31 : $end & 0x1f;
            if (0 === $firstBit && 31 === $lastBit) {
                $mask = 0x7fffffff;
            } else {
                $mask = 0;
                for ($j = $firstBit; $j <= $lastBit; ++$j) {
                    $mask |= 1 << $j;
                }
            }
            if (($this->bits[$i] & $mask) !== ($value ? $mask : 0)) {
                return \false;
            }
        }
        return \true;
    }
    /**
     * Appends a bit to the array.
     */
    public function appendBit(bool $bit) : void
    {
        $this->ensureCapacity($this->size + 1);
        if ($bit) {
            $this->bits[$this->size >> 5] = $this->bits[$this->size >> 5] | 1 << ($this->size & 0x1f);
        }
        ++$this->size;
    }
    /**
     * Appends a number of bits (up to 32) to the array.
     * @throws InvalidArgumentException if num bits is not between 0 and 32
     */
    public function appendBits(int $value, int $numBits) : void
    {
        if ($numBits < 0 || $numBits > 32) {
            throw new InvalidArgumentException('Num bits must be between 0 and 32');
        }
        $this->ensureCapacity($this->size + $numBits);
        for ($numBitsLeft = $numBits; $numBitsLeft > 0; $numBitsLeft--) {
            $this->appendBit(($value >> $numBitsLeft - 1 & 0x1) === 1);
        }
    }
    /**
     * Appends another bit array to this array.
     */
    public function appendBitArray(self $other) : void
    {
        $otherSize = $other->getSize();
        $this->ensureCapacity($this->size + $other->getSize());
        for ($i = 0; $i < $otherSize; ++$i) {
            $this->appendBit($other->get($i));
        }
    }
    /**
     * Makes an exclusive-or comparision on the current bit array.
     *
     * @throws InvalidArgumentException if sizes don't match
     */
    public function xorBits(self $other) : void
    {
        $bitsLength = \count($this->bits);
        $otherBits = $other->getBitArray();
        if ($bitsLength !== \count($otherBits)) {
            throw new InvalidArgumentException('Sizes don\'t match');
        }
        for ($i = 0; $i < $bitsLength; ++$i) {
            $this->bits[$i] = $this->bits[$i] ^ $otherBits[$i];
        }
    }
    /**
     * Converts the bit array to a byte array.
     *
     * @return SplFixedArray<int>
     */
    public function toBytes(int $bitOffset, int $numBytes) : SplFixedArray
    {
        $bytes = new SplFixedArray($numBytes);
        for ($i = 0; $i < $numBytes; ++$i) {
            $byte = 0;
            for ($j = 0; $j < 8; ++$j) {
                if ($this->get($bitOffset)) {
                    $byte |= 1 << 7 - $j;
                }
                ++$bitOffset;
            }
            $bytes[$i] = $byte;
        }
        return $bytes;
    }
    /**
     * Gets the internal bit array.
     *
     * @return SplFixedArray<int>
     */
    public function getBitArray() : SplFixedArray
    {
        return $this->bits;
    }
    /**
     * Reverses the array.
     */
    public function reverse() : void
    {
        $newBits = new SplFixedArray(\count($this->bits));
        for ($i = 0; $i < $this->size; ++$i) {
            if ($this->get($this->size - $i - 1)) {
                $newBits[$i >> 5] = $newBits[$i >> 5] | 1 << ($i & 0x1f);
            }
        }
        $this->bits = $newBits;
    }
    /**
     * Returns a string representation of the bit array.
     */
    public function __toString() : string
    {
        $result = '';
        for ($i = 0; $i < $this->size; ++$i) {
            if (0 === ($i & 0x7)) {
                $result .= ' ';
            }
            $result .= $this->get($i) ? 'X' : '.';
        }
        return $result;
    }
}
vendor/bacon/bacon-qr-code/src/Common/CharacterSetEci.php000064400000012366150755130600017270 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Common;

use WP2FA_Vendor\BaconQrCode\Exception\InvalidArgumentException;
use WP2FA_Vendor\DASPRiD\Enum\AbstractEnum;
/**
 * Encapsulates a Character Set ECI, according to "Extended Channel Interpretations" 5.3.1.1 of ISO 18004.
 *
 * @method static self CP437()
 * @method static self ISO8859_1()
 * @method static self ISO8859_2()
 * @method static self ISO8859_3()
 * @method static self ISO8859_4()
 * @method static self ISO8859_5()
 * @method static self ISO8859_6()
 * @method static self ISO8859_7()
 * @method static self ISO8859_8()
 * @method static self ISO8859_9()
 * @method static self ISO8859_10()
 * @method static self ISO8859_11()
 * @method static self ISO8859_12()
 * @method static self ISO8859_13()
 * @method static self ISO8859_14()
 * @method static self ISO8859_15()
 * @method static self ISO8859_16()
 * @method static self SJIS()
 * @method static self CP1250()
 * @method static self CP1251()
 * @method static self CP1252()
 * @method static self CP1256()
 * @method static self UNICODE_BIG_UNMARKED()
 * @method static self UTF8()
 * @method static self ASCII()
 * @method static self BIG5()
 * @method static self GB18030()
 * @method static self EUC_KR()
 */
final class CharacterSetEci extends AbstractEnum
{
    protected const CP437 = [[0, 2]];
    protected const ISO8859_1 = [[1, 3], 'ISO-8859-1'];
    protected const ISO8859_2 = [[4], 'ISO-8859-2'];
    protected const ISO8859_3 = [[5], 'ISO-8859-3'];
    protected const ISO8859_4 = [[6], 'ISO-8859-4'];
    protected const ISO8859_5 = [[7], 'ISO-8859-5'];
    protected const ISO8859_6 = [[8], 'ISO-8859-6'];
    protected const ISO8859_7 = [[9], 'ISO-8859-7'];
    protected const ISO8859_8 = [[10], 'ISO-8859-8'];
    protected const ISO8859_9 = [[11], 'ISO-8859-9'];
    protected const ISO8859_10 = [[12], 'ISO-8859-10'];
    protected const ISO8859_11 = [[13], 'ISO-8859-11'];
    protected const ISO8859_12 = [[14], 'ISO-8859-12'];
    protected const ISO8859_13 = [[15], 'ISO-8859-13'];
    protected const ISO8859_14 = [[16], 'ISO-8859-14'];
    protected const ISO8859_15 = [[17], 'ISO-8859-15'];
    protected const ISO8859_16 = [[18], 'ISO-8859-16'];
    protected const SJIS = [[20], 'Shift_JIS'];
    protected const CP1250 = [[21], 'windows-1250'];
    protected const CP1251 = [[22], 'windows-1251'];
    protected const CP1252 = [[23], 'windows-1252'];
    protected const CP1256 = [[24], 'windows-1256'];
    protected const UNICODE_BIG_UNMARKED = [[25], 'UTF-16BE', 'UnicodeBig'];
    protected const UTF8 = [[26], 'UTF-8'];
    protected const ASCII = [[27, 170], 'US-ASCII'];
    protected const BIG5 = [[28]];
    protected const GB18030 = [[29], 'GB2312', 'EUC_CN', 'GBK'];
    protected const EUC_KR = [[30], 'EUC-KR'];
    /**
     * @var int[]
     */
    private $values;
    /**
     * @var string[]
     */
    private $otherEncodingNames;
    /**
     * @var array<int, self>|null
     */
    private static $valueToEci;
    /**
     * @var array<string, self>|null
     */
    private static $nameToEci;
    /**
     * @param int[] $values
     */
    public function __construct(array $values, string ...$otherEncodingNames)
    {
        $this->values = $values;
        $this->otherEncodingNames = $otherEncodingNames;
    }
    /**
     * Returns the primary value.
     */
    public function getValue() : int
    {
        return $this->values[0];
    }
    /**
     * Gets character set ECI by value.
     *
     * Returns the representing ECI of a given value, or null if it is legal but unsupported.
     *
     * @throws InvalidArgumentException if value is not between 0 and 900
     */
    public static function getCharacterSetEciByValue(int $value) : ?self
    {
        if ($value < 0 || $value >= 900) {
            throw new InvalidArgumentException('Value must be between 0 and 900');
        }
        $valueToEci = self::valueToEci();
        if (!\array_key_exists($value, $valueToEci)) {
            return null;
        }
        return $valueToEci[$value];
    }
    /**
     * Returns character set ECI by name.
     *
     * Returns the representing ECI of a given name, or null if it is legal but unsupported
     */
    public static function getCharacterSetEciByName(string $name) : ?self
    {
        $nameToEci = self::nameToEci();
        $name = \strtolower($name);
        if (!\array_key_exists($name, $nameToEci)) {
            return null;
        }
        return $nameToEci[$name];
    }
    private static function valueToEci() : array
    {
        if (null !== self::$valueToEci) {
            return self::$valueToEci;
        }
        self::$valueToEci = [];
        foreach (self::values() as $eci) {
            foreach ($eci->values as $value) {
                self::$valueToEci[$value] = $eci;
            }
        }
        return self::$valueToEci;
    }
    private static function nameToEci() : array
    {
        if (null !== self::$nameToEci) {
            return self::$nameToEci;
        }
        self::$nameToEci = [];
        foreach (self::values() as $eci) {
            self::$nameToEci[\strtolower($eci->name())] = $eci;
            foreach ($eci->otherEncodingNames as $name) {
                self::$nameToEci[\strtolower($name)] = $eci;
            }
        }
        return self::$nameToEci;
    }
}
vendor/bacon/bacon-qr-code/src/Common/ReedSolomonCodec.php000064400000034034150755130600017457 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Common;

use WP2FA_Vendor\BaconQrCode\Exception\InvalidArgumentException;
use WP2FA_Vendor\BaconQrCode\Exception\RuntimeException;
use SplFixedArray;
/**
 * Reed-Solomon codec for 8-bit characters.
 *
 * Based on libfec by Phil Karn, KA9Q.
 */
final class ReedSolomonCodec
{
    /**
     * Symbol size in bits.
     *
     * @var int
     */
    private $symbolSize;
    /**
     * Block size in symbols.
     *
     * @var int
     */
    private $blockSize;
    /**
     * First root of RS code generator polynomial, index form.
     *
     * @var int
     */
    private $firstRoot;
    /**
     * Primitive element to generate polynomial roots, index form.
     *
     * @var int
     */
    private $primitive;
    /**
     * Prim-th root of 1, index form.
     *
     * @var int
     */
    private $iPrimitive;
    /**
     * RS code generator polynomial degree (number of roots).
     *
     * @var int
     */
    private $numRoots;
    /**
     * Padding bytes at front of shortened block.
     *
     * @var int
     */
    private $padding;
    /**
     * Log lookup table.
     *
     * @var SplFixedArray
     */
    private $alphaTo;
    /**
     * Anti-Log lookup table.
     *
     * @var SplFixedArray
     */
    private $indexOf;
    /**
     * Generator polynomial.
     *
     * @var SplFixedArray
     */
    private $generatorPoly;
    /**
     * @throws InvalidArgumentException if symbol size ist not between 0 and 8
     * @throws InvalidArgumentException if first root is invalid
     * @throws InvalidArgumentException if num roots is invalid
     * @throws InvalidArgumentException if padding is invalid
     * @throws RuntimeException if field generator polynomial is not primitive
     */
    public function __construct(int $symbolSize, int $gfPoly, int $firstRoot, int $primitive, int $numRoots, int $padding)
    {
        if ($symbolSize < 0 || $symbolSize > 8) {
            throw new InvalidArgumentException('Symbol size must be between 0 and 8');
        }
        if ($firstRoot < 0 || $firstRoot >= 1 << $symbolSize) {
            throw new InvalidArgumentException('First root must be between 0 and ' . (1 << $symbolSize));
        }
        if ($numRoots < 0 || $numRoots >= 1 << $symbolSize) {
            throw new InvalidArgumentException('Num roots must be between 0 and ' . (1 << $symbolSize));
        }
        if ($padding < 0 || $padding >= (1 << $symbolSize) - 1 - $numRoots) {
            throw new InvalidArgumentException('Padding must be between 0 and ' . ((1 << $symbolSize) - 1 - $numRoots));
        }
        $this->symbolSize = $symbolSize;
        $this->blockSize = (1 << $symbolSize) - 1;
        $this->padding = $padding;
        $this->alphaTo = SplFixedArray::fromArray(\array_fill(0, $this->blockSize + 1, 0), \false);
        $this->indexOf = SplFixedArray::fromArray(\array_fill(0, $this->blockSize + 1, 0), \false);
        // Generate galous field lookup table
        $this->indexOf[0] = $this->blockSize;
        $this->alphaTo[$this->blockSize] = 0;
        $sr = 1;
        for ($i = 0; $i < $this->blockSize; ++$i) {
            $this->indexOf[$sr] = $i;
            $this->alphaTo[$i] = $sr;
            $sr <<= 1;
            if ($sr & 1 << $symbolSize) {
                $sr ^= $gfPoly;
            }
            $sr &= $this->blockSize;
        }
        if (1 !== $sr) {
            throw new RuntimeException('Field generator polynomial is not primitive');
        }
        // Form RS code generator polynomial from its roots
        $this->generatorPoly = SplFixedArray::fromArray(\array_fill(0, $numRoots + 1, 0), \false);
        $this->firstRoot = $firstRoot;
        $this->primitive = $primitive;
        $this->numRoots = $numRoots;
        // Find prim-th root of 1, used in decoding
        for ($iPrimitive = 1; $iPrimitive % $primitive !== 0; $iPrimitive += $this->blockSize) {
        }
        $this->iPrimitive = \intdiv($iPrimitive, $primitive);
        $this->generatorPoly[0] = 1;
        for ($i = 0, $root = $firstRoot * $primitive; $i < $numRoots; ++$i, $root += $primitive) {
            $this->generatorPoly[$i + 1] = 1;
            for ($j = $i; $j > 0; $j--) {
                if ($this->generatorPoly[$j] !== 0) {
                    $this->generatorPoly[$j] = $this->generatorPoly[$j - 1] ^ $this->alphaTo[$this->modNn($this->indexOf[$this->generatorPoly[$j]] + $root)];
                } else {
                    $this->generatorPoly[$j] = $this->generatorPoly[$j - 1];
                }
            }
            $this->generatorPoly[$j] = $this->alphaTo[$this->modNn($this->indexOf[$this->generatorPoly[0]] + $root)];
        }
        // Convert generator poly to index form for quicker encoding
        for ($i = 0; $i <= $numRoots; ++$i) {
            $this->generatorPoly[$i] = $this->indexOf[$this->generatorPoly[$i]];
        }
    }
    /**
     * Encodes data and writes result back into parity array.
     */
    public function encode(SplFixedArray $data, SplFixedArray $parity) : void
    {
        for ($i = 0; $i < $this->numRoots; ++$i) {
            $parity[$i] = 0;
        }
        $iterations = $this->blockSize - $this->numRoots - $this->padding;
        for ($i = 0; $i < $iterations; ++$i) {
            $feedback = $this->indexOf[$data[$i] ^ $parity[0]];
            if ($feedback !== $this->blockSize) {
                // Feedback term is non-zero
                $feedback = $this->modNn($this->blockSize - $this->generatorPoly[$this->numRoots] + $feedback);
                for ($j = 1; $j < $this->numRoots; ++$j) {
                    $parity[$j] = $parity[$j] ^ $this->alphaTo[$this->modNn($feedback + $this->generatorPoly[$this->numRoots - $j])];
                }
            }
            for ($j = 0; $j < $this->numRoots - 1; ++$j) {
                $parity[$j] = $parity[$j + 1];
            }
            if ($feedback !== $this->blockSize) {
                $parity[$this->numRoots - 1] = $this->alphaTo[$this->modNn($feedback + $this->generatorPoly[0])];
            } else {
                $parity[$this->numRoots - 1] = 0;
            }
        }
    }
    /**
     * Decodes received data.
     */
    public function decode(SplFixedArray $data, SplFixedArray $erasures = null) : ?int
    {
        // This speeds up the initialization a bit.
        $numRootsPlusOne = SplFixedArray::fromArray(\array_fill(0, $this->numRoots + 1, 0), \false);
        $numRoots = SplFixedArray::fromArray(\array_fill(0, $this->numRoots, 0), \false);
        $lambda = clone $numRootsPlusOne;
        $b = clone $numRootsPlusOne;
        $t = clone $numRootsPlusOne;
        $omega = clone $numRootsPlusOne;
        $root = clone $numRoots;
        $loc = clone $numRoots;
        $numErasures = null !== $erasures ? \count($erasures) : 0;
        // Form the Syndromes; i.e., evaluate data(x) at roots of g(x)
        $syndromes = SplFixedArray::fromArray(\array_fill(0, $this->numRoots, $data[0]), \false);
        for ($i = 1; $i < $this->blockSize - $this->padding; ++$i) {
            for ($j = 0; $j < $this->numRoots; ++$j) {
                if ($syndromes[$j] === 0) {
                    $syndromes[$j] = $data[$i];
                } else {
                    $syndromes[$j] = $data[$i] ^ $this->alphaTo[$this->modNn($this->indexOf[$syndromes[$j]] + ($this->firstRoot + $j) * $this->primitive)];
                }
            }
        }
        // Convert syndromes to index form, checking for nonzero conditions
        $syndromeError = 0;
        for ($i = 0; $i < $this->numRoots; ++$i) {
            $syndromeError |= $syndromes[$i];
            $syndromes[$i] = $this->indexOf[$syndromes[$i]];
        }
        if (!$syndromeError) {
            // If syndrome is zero, data[] is a codeword and there are no errors to correct, so return data[]
            // unmodified.
            return 0;
        }
        $lambda[0] = 1;
        if ($numErasures > 0) {
            // Init lambda to be the erasure locator polynomial
            $lambda[1] = $this->alphaTo[$this->modNn($this->primitive * ($this->blockSize - 1 - $erasures[0]))];
            for ($i = 1; $i < $numErasures; ++$i) {
                $u = $this->modNn($this->primitive * ($this->blockSize - 1 - $erasures[$i]));
                for ($j = $i + 1; $j > 0; --$j) {
                    $tmp = $this->indexOf[$lambda[$j - 1]];
                    if ($tmp !== $this->blockSize) {
                        $lambda[$j] = $lambda[$j] ^ $this->alphaTo[$this->modNn($u + $tmp)];
                    }
                }
            }
        }
        for ($i = 0; $i <= $this->numRoots; ++$i) {
            $b[$i] = $this->indexOf[$lambda[$i]];
        }
        // Begin Berlekamp-Massey algorithm to determine error+erasure locator polynomial
        $r = $numErasures;
        $el = $numErasures;
        while (++$r <= $this->numRoots) {
            // Compute discrepancy at the r-th step in poly form
            $discrepancyR = 0;
            for ($i = 0; $i < $r; ++$i) {
                if ($lambda[$i] !== 0 && $syndromes[$r - $i - 1] !== $this->blockSize) {
                    $discrepancyR ^= $this->alphaTo[$this->modNn($this->indexOf[$lambda[$i]] + $syndromes[$r - $i - 1])];
                }
            }
            $discrepancyR = $this->indexOf[$discrepancyR];
            if ($discrepancyR === $this->blockSize) {
                $tmp = $b->toArray();
                \array_unshift($tmp, $this->blockSize);
                \array_pop($tmp);
                $b = SplFixedArray::fromArray($tmp, \false);
                continue;
            }
            $t[0] = $lambda[0];
            for ($i = 0; $i < $this->numRoots; ++$i) {
                if ($b[$i] !== $this->blockSize) {
                    $t[$i + 1] = $lambda[$i + 1] ^ $this->alphaTo[$this->modNn($discrepancyR + $b[$i])];
                } else {
                    $t[$i + 1] = $lambda[$i + 1];
                }
            }
            if (2 * $el <= $r + $numErasures - 1) {
                $el = $r + $numErasures - $el;
                for ($i = 0; $i <= $this->numRoots; ++$i) {
                    $b[$i] = $lambda[$i] === 0 ? $this->blockSize : $this->modNn($this->indexOf[$lambda[$i]] - $discrepancyR + $this->blockSize);
                }
            } else {
                $tmp = $b->toArray();
                \array_unshift($tmp, $this->blockSize);
                \array_pop($tmp);
                $b = SplFixedArray::fromArray($tmp, \false);
            }
            $lambda = clone $t;
        }
        // Convert lambda to index form and compute deg(lambda(x))
        $degLambda = 0;
        for ($i = 0; $i <= $this->numRoots; ++$i) {
            $lambda[$i] = $this->indexOf[$lambda[$i]];
            if ($lambda[$i] !== $this->blockSize) {
                $degLambda = $i;
            }
        }
        // Find roots of the error+erasure locator polynomial by Chien search.
        $reg = clone $lambda;
        $reg[0] = 0;
        $count = 0;
        $i = 1;
        for ($k = $this->iPrimitive - 1; $i <= $this->blockSize; ++$i, $k = $this->modNn($k + $this->iPrimitive)) {
            $q = 1;
            for ($j = $degLambda; $j > 0; $j--) {
                if ($reg[$j] !== $this->blockSize) {
                    $reg[$j] = $this->modNn($reg[$j] + $j);
                    $q ^= $this->alphaTo[$reg[$j]];
                }
            }
            if ($q !== 0) {
                // Not a root
                continue;
            }
            // Store root (index-form) and error location number
            $root[$count] = $i;
            $loc[$count] = $k;
            if (++$count === $degLambda) {
                break;
            }
        }
        if ($degLambda !== $count) {
            // deg(lambda) unequal to number of roots: uncorrectable error detected
            return null;
        }
        // Compute err+eras evaluate poly omega(x) = s(x)*lambda(x) (modulo x**numRoots). In index form. Also find
        // deg(omega).
        $degOmega = $degLambda - 1;
        for ($i = 0; $i <= $degOmega; ++$i) {
            $tmp = 0;
            for ($j = $i; $j >= 0; --$j) {
                if ($syndromes[$i - $j] !== $this->blockSize && $lambda[$j] !== $this->blockSize) {
                    $tmp ^= $this->alphaTo[$this->modNn($syndromes[$i - $j] + $lambda[$j])];
                }
            }
            $omega[$i] = $this->indexOf[$tmp];
        }
        // Compute error values in poly-form. num1 = omega(inv(X(l))), num2 = inv(X(l))**(firstRoot-1) and
        // den = lambda_pr(inv(X(l))) all in poly form.
        for ($j = $count - 1; $j >= 0; --$j) {
            $num1 = 0;
            for ($i = $degOmega; $i >= 0; $i--) {
                if ($omega[$i] !== $this->blockSize) {
                    $num1 ^= $this->alphaTo[$this->modNn($omega[$i] + $i * $root[$j])];
                }
            }
            $num2 = $this->alphaTo[$this->modNn($root[$j] * ($this->firstRoot - 1) + $this->blockSize)];
            $den = 0;
            // lambda[i+1] for i even is the formal derivativelambda_pr of lambda[i]
            for ($i = \min($degLambda, $this->numRoots - 1) & ~1; $i >= 0; $i -= 2) {
                if ($lambda[$i + 1] !== $this->blockSize) {
                    $den ^= $this->alphaTo[$this->modNn($lambda[$i + 1] + $i * $root[$j])];
                }
            }
            // Apply error to data
            if ($num1 !== 0 && $loc[$j] >= $this->padding) {
                $data[$loc[$j] - $this->padding] = $data[$loc[$j] - $this->padding] ^ $this->alphaTo[$this->modNn($this->indexOf[$num1] + $this->indexOf[$num2] + $this->blockSize - $this->indexOf[$den])];
            }
        }
        if (null !== $erasures) {
            if (\count($erasures) < $count) {
                $erasures->setSize($count);
            }
            for ($i = 0; $i < $count; $i++) {
                $erasures[$i] = $loc[$i];
            }
        }
        return $count;
    }
    /**
     * Computes $x % GF_SIZE, where GF_SIZE is 2**GF_BITS - 1, without a slow divide.
     */
    private function modNn(int $x) : int
    {
        while ($x >= $this->blockSize) {
            $x -= $this->blockSize;
            $x = ($x >> $this->symbolSize) + ($x & $this->blockSize);
        }
        return $x;
    }
}
vendor/bacon/bacon-qr-code/src/Common/BitMatrix.php000064400000017006150755130600016176 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Common;

use WP2FA_Vendor\BaconQrCode\Exception\InvalidArgumentException;
use SplFixedArray;
/**
 * Bit matrix.
 *
 * Represents a 2D matrix of bits. In function arguments below, and throughout
 * the common module, x is the column position, and y is the row position. The
 * ordering is always x, y. The origin is at the top-left.
 */
class BitMatrix
{
    /**
     * Width of the bit matrix.
     *
     * @var int
     */
    private $width;
    /**
     * Height of the bit matrix.
     *
     * @var int
     */
    private $height;
    /**
     * Size in bits of each individual row.
     *
     * @var int
     */
    private $rowSize;
    /**
     * Bits representation.
     *
     * @var SplFixedArray<int>
     */
    private $bits;
    /**
     * @throws InvalidArgumentException if a dimension is smaller than zero
     */
    public function __construct(int $width, int $height = null)
    {
        if (null === $height) {
            $height = $width;
        }
        if ($width < 1 || $height < 1) {
            throw new InvalidArgumentException('Both dimensions must be greater than zero');
        }
        $this->width = $width;
        $this->height = $height;
        $this->rowSize = $width + 31 >> 5;
        $this->bits = SplFixedArray::fromArray(\array_fill(0, $this->rowSize * $height, 0));
    }
    /**
     * Gets the requested bit, where true means black.
     */
    public function get(int $x, int $y) : bool
    {
        $offset = $y * $this->rowSize + ($x >> 5);
        return 0 !== (BitUtils::unsignedRightShift($this->bits[$offset], $x & 0x1f) & 1);
    }
    /**
     * Sets the given bit to true.
     */
    public function set(int $x, int $y) : void
    {
        $offset = $y * $this->rowSize + ($x >> 5);
        $this->bits[$offset] = $this->bits[$offset] | 1 << ($x & 0x1f);
    }
    /**
     * Flips the given bit.
     */
    public function flip(int $x, int $y) : void
    {
        $offset = $y * $this->rowSize + ($x >> 5);
        $this->bits[$offset] = $this->bits[$offset] ^ 1 << ($x & 0x1f);
    }
    /**
     * Clears all bits (set to false).
     */
    public function clear() : void
    {
        $max = \count($this->bits);
        for ($i = 0; $i < $max; ++$i) {
            $this->bits[$i] = 0;
        }
    }
    /**
     * Sets a square region of the bit matrix to true.
     *
     * @throws InvalidArgumentException if left or top are negative
     * @throws InvalidArgumentException if width or height are smaller than 1
     * @throws InvalidArgumentException if region does not fit into the matix
     */
    public function setRegion(int $left, int $top, int $width, int $height) : void
    {
        if ($top < 0 || $left < 0) {
            throw new InvalidArgumentException('Left and top must be non-negative');
        }
        if ($height < 1 || $width < 1) {
            throw new InvalidArgumentException('Width and height must be at least 1');
        }
        $right = $left + $width;
        $bottom = $top + $height;
        if ($bottom > $this->height || $right > $this->width) {
            throw new InvalidArgumentException('The region must fit inside the matrix');
        }
        for ($y = $top; $y < $bottom; ++$y) {
            $offset = $y * $this->rowSize;
            for ($x = $left; $x < $right; ++$x) {
                $index = $offset + ($x >> 5);
                $this->bits[$index] = $this->bits[$index] | 1 << ($x & 0x1f);
            }
        }
    }
    /**
     * A fast method to retrieve one row of data from the matrix as a BitArray.
     */
    public function getRow(int $y, BitArray $row = null) : BitArray
    {
        if (null === $row || $row->getSize() < $this->width) {
            $row = new BitArray($this->width);
        }
        $offset = $y * $this->rowSize;
        for ($x = 0; $x < $this->rowSize; ++$x) {
            $row->setBulk($x << 5, $this->bits[$offset + $x]);
        }
        return $row;
    }
    /**
     * Sets a row of data from a BitArray.
     */
    public function setRow(int $y, BitArray $row) : void
    {
        $bits = $row->getBitArray();
        for ($i = 0; $i < $this->rowSize; ++$i) {
            $this->bits[$y * $this->rowSize + $i] = $bits[$i];
        }
    }
    /**
     * This is useful in detecting the enclosing rectangle of a 'pure' barcode.
     *
     * @return int[]|null
     */
    public function getEnclosingRectangle() : ?array
    {
        $left = $this->width;
        $top = $this->height;
        $right = -1;
        $bottom = -1;
        for ($y = 0; $y < $this->height; ++$y) {
            for ($x32 = 0; $x32 < $this->rowSize; ++$x32) {
                $bits = $this->bits[$y * $this->rowSize + $x32];
                if (0 !== $bits) {
                    if ($y < $top) {
                        $top = $y;
                    }
                    if ($y > $bottom) {
                        $bottom = $y;
                    }
                    if ($x32 * 32 < $left) {
                        $bit = 0;
                        while ($bits << 31 - $bit === 0) {
                            $bit++;
                        }
                        if ($x32 * 32 + $bit < $left) {
                            $left = $x32 * 32 + $bit;
                        }
                    }
                }
                if ($x32 * 32 + 31 > $right) {
                    $bit = 31;
                    while (0 === BitUtils::unsignedRightShift($bits, $bit)) {
                        --$bit;
                    }
                    if ($x32 * 32 + $bit > $right) {
                        $right = $x32 * 32 + $bit;
                    }
                }
            }
        }
        $width = $right - $left;
        $height = $bottom - $top;
        if ($width < 0 || $height < 0) {
            return null;
        }
        return [$left, $top, $width, $height];
    }
    /**
     * Gets the most top left set bit.
     *
     * This is useful in detecting a corner of a 'pure' barcode.
     *
     * @return int[]|null
     */
    public function getTopLeftOnBit() : ?array
    {
        $bitsOffset = 0;
        while ($bitsOffset < \count($this->bits) && 0 === $this->bits[$bitsOffset]) {
            ++$bitsOffset;
        }
        if (\count($this->bits) === $bitsOffset) {
            return null;
        }
        $x = \intdiv($bitsOffset, $this->rowSize);
        $y = $bitsOffset % $this->rowSize << 5;
        $bits = $this->bits[$bitsOffset];
        $bit = 0;
        while (0 === $bits << 31 - $bit) {
            ++$bit;
        }
        $x += $bit;
        return [$x, $y];
    }
    /**
     * Gets the most bottom right set bit.
     *
     * This is useful in detecting a corner of a 'pure' barcode.
     *
     * @return int[]|null
     */
    public function getBottomRightOnBit() : ?array
    {
        $bitsOffset = \count($this->bits) - 1;
        while ($bitsOffset >= 0 && 0 === $this->bits[$bitsOffset]) {
            --$bitsOffset;
        }
        if ($bitsOffset < 0) {
            return null;
        }
        $x = \intdiv($bitsOffset, $this->rowSize);
        $y = $bitsOffset % $this->rowSize << 5;
        $bits = $this->bits[$bitsOffset];
        $bit = 0;
        while (0 === BitUtils::unsignedRightShift($bits, $bit)) {
            --$bit;
        }
        $x += $bit;
        return [$x, $y];
    }
    /**
     * Gets the width of the matrix,
     */
    public function getWidth() : int
    {
        return $this->width;
    }
    /**
     * Gets the height of the matrix.
     */
    public function getHeight() : int
    {
        return $this->height;
    }
}
vendor/bacon/bacon-qr-code/src/Common/ErrorCorrectionLevel.php000064400000002526150755130600020405 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Common;

use WP2FA_Vendor\BaconQrCode\Exception\OutOfBoundsException;
use WP2FA_Vendor\DASPRiD\Enum\AbstractEnum;
/**
 * Enum representing the four error correction levels.
 *
 * @method static self L() ~7% correction
 * @method static self M() ~15% correction
 * @method static self Q() ~25% correction
 * @method static self H() ~30% correction
 */
final class ErrorCorrectionLevel extends AbstractEnum
{
    protected const L = [0x1];
    protected const M = [0x0];
    protected const Q = [0x3];
    protected const H = [0x2];
    /**
     * @var int
     */
    private $bits;
    protected function __construct(int $bits)
    {
        $this->bits = $bits;
    }
    /**
     * @throws OutOfBoundsException if number of bits is invalid
     */
    public static function forBits(int $bits) : self
    {
        switch ($bits) {
            case 0:
                return self::M();
            case 1:
                return self::L();
            case 2:
                return self::H();
            case 3:
                return self::Q();
        }
        throw new OutOfBoundsException('Invalid number of bits');
    }
    /**
     * Returns the two bits used to encode this error correction level.
     */
    public function getBits() : int
    {
        return $this->bits;
    }
}
vendor/bacon/bacon-qr-code/src/Common/EcBlocks.php000064400000003216150755130600015756 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Common;

/**
 * Encapsulates a set of error-correction blocks in one symbol version.
 *
 * Most versions will use blocks of differing sizes within one version, so, this encapsulates the parameters for each
 * set of blocks. It also holds the number of error-correction codewords per block since it will be the same across all
 * blocks within one version.
 */
final class EcBlocks
{
    /**
     * Number of EC codewords per block.
     *
     * @var int
     */
    private $ecCodewordsPerBlock;
    /**
     * List of EC blocks.
     *
     * @var EcBlock[]
     */
    private $ecBlocks;
    public function __construct(int $ecCodewordsPerBlock, EcBlock ...$ecBlocks)
    {
        $this->ecCodewordsPerBlock = $ecCodewordsPerBlock;
        $this->ecBlocks = $ecBlocks;
    }
    /**
     * Returns the number of EC codewords per block.
     */
    public function getEcCodewordsPerBlock() : int
    {
        return $this->ecCodewordsPerBlock;
    }
    /**
     * Returns the total number of EC block appearances.
     */
    public function getNumBlocks() : int
    {
        $total = 0;
        foreach ($this->ecBlocks as $ecBlock) {
            $total += $ecBlock->getCount();
        }
        return $total;
    }
    /**
     * Returns the total count of EC codewords.
     */
    public function getTotalEcCodewords() : int
    {
        return $this->ecCodewordsPerBlock * $this->getNumBlocks();
    }
    /**
     * Returns the EC blocks included in this collection.
     *
     * @return EcBlock[]
     */
    public function getEcBlocks() : array
    {
        return $this->ecBlocks;
    }
}
vendor/bacon/bacon-qr-code/src/Common/FormatInformation.php000064400000012272150755130600017731 0ustar00<?php

/**
 * BaconQrCode
 *
 * @link      http://github.com/Bacon/BaconQrCode For the canonical source repository
 * @copyright 2013 Ben 'DASPRiD' Scholzen
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */
namespace WP2FA_Vendor\BaconQrCode\Common;

/**
 * Encapsulates a QR Code's format information, including the data mask used and error correction level.
 */
class FormatInformation
{
    /**
     * Mask for format information.
     */
    private const FORMAT_INFO_MASK_QR = 0x5412;
    /**
     * Lookup table for decoding format information.
     *
     * See ISO 18004:2006, Annex C, Table C.1
     */
    private const FORMAT_INFO_DECODE_LOOKUP = [[0x5412, 0x0], [0x5125, 0x1], [0x5e7c, 0x2], [0x5b4b, 0x3], [0x45f9, 0x4], [0x40ce, 0x5], [0x4f97, 0x6], [0x4aa0, 0x7], [0x77c4, 0x8], [0x72f3, 0x9], [0x7daa, 0xa], [0x789d, 0xb], [0x662f, 0xc], [0x6318, 0xd], [0x6c41, 0xe], [0x6976, 0xf], [0x1689, 0x10], [0x13be, 0x11], [0x1ce7, 0x12], [0x19d0, 0x13], [0x762, 0x14], [0x255, 0x15], [0xd0c, 0x16], [0x83b, 0x17], [0x355f, 0x18], [0x3068, 0x19], [0x3f31, 0x1a], [0x3a06, 0x1b], [0x24b4, 0x1c], [0x2183, 0x1d], [0x2eda, 0x1e], [0x2bed, 0x1f]];
    /**
     * Offset i holds the number of 1 bits in the binary representation of i.
     *
     * @var int[]
     */
    private const BITS_SET_IN_HALF_BYTE = [0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4];
    /**
     * Error correction level.
     *
     * @var ErrorCorrectionLevel
     */
    private $ecLevel;
    /**
     * Data mask.
     *
     * @var int
     */
    private $dataMask;
    protected function __construct(int $formatInfo)
    {
        $this->ecLevel = ErrorCorrectionLevel::forBits($formatInfo >> 3 & 0x3);
        $this->dataMask = $formatInfo & 0x7;
    }
    /**
     * Checks how many bits are different between two integers.
     */
    public static function numBitsDiffering(int $a, int $b) : int
    {
        $a ^= $b;
        return self::BITS_SET_IN_HALF_BYTE[$a & 0xf] + self::BITS_SET_IN_HALF_BYTE[BitUtils::unsignedRightShift($a, 4) & 0xf] + self::BITS_SET_IN_HALF_BYTE[BitUtils::unsignedRightShift($a, 8) & 0xf] + self::BITS_SET_IN_HALF_BYTE[BitUtils::unsignedRightShift($a, 12) & 0xf] + self::BITS_SET_IN_HALF_BYTE[BitUtils::unsignedRightShift($a, 16) & 0xf] + self::BITS_SET_IN_HALF_BYTE[BitUtils::unsignedRightShift($a, 20) & 0xf] + self::BITS_SET_IN_HALF_BYTE[BitUtils::unsignedRightShift($a, 24) & 0xf] + self::BITS_SET_IN_HALF_BYTE[BitUtils::unsignedRightShift($a, 28) & 0xf];
    }
    /**
     * Decodes format information.
     */
    public static function decodeFormatInformation(int $maskedFormatInfo1, int $maskedFormatInfo2) : ?self
    {
        $formatInfo = self::doDecodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2);
        if (null !== $formatInfo) {
            return $formatInfo;
        }
        // Should return null, but, some QR codes apparently do not mask this info. Try again by actually masking the
        // pattern first.
        return self::doDecodeFormatInformation($maskedFormatInfo1 ^ self::FORMAT_INFO_MASK_QR, $maskedFormatInfo2 ^ self::FORMAT_INFO_MASK_QR);
    }
    /**
     * Internal method for decoding format information.
     */
    private static function doDecodeFormatInformation(int $maskedFormatInfo1, int $maskedFormatInfo2) : ?self
    {
        $bestDifference = \PHP_INT_MAX;
        $bestFormatInfo = 0;
        foreach (self::FORMAT_INFO_DECODE_LOOKUP as $decodeInfo) {
            $targetInfo = $decodeInfo[0];
            if ($targetInfo === $maskedFormatInfo1 || $targetInfo === $maskedFormatInfo2) {
                // Found an exact match
                return new self($decodeInfo[1]);
            }
            $bitsDifference = self::numBitsDiffering($maskedFormatInfo1, $targetInfo);
            if ($bitsDifference < $bestDifference) {
                $bestFormatInfo = $decodeInfo[1];
                $bestDifference = $bitsDifference;
            }
            if ($maskedFormatInfo1 !== $maskedFormatInfo2) {
                // Also try the other option
                $bitsDifference = self::numBitsDiffering($maskedFormatInfo2, $targetInfo);
                if ($bitsDifference < $bestDifference) {
                    $bestFormatInfo = $decodeInfo[1];
                    $bestDifference = $bitsDifference;
                }
            }
        }
        // Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits differing means we found a match.
        if ($bestDifference <= 3) {
            return new self($bestFormatInfo);
        }
        return null;
    }
    /**
     * Returns the error correction level.
     */
    public function getErrorCorrectionLevel() : ErrorCorrectionLevel
    {
        return $this->ecLevel;
    }
    /**
     * Returns the data mask.
     */
    public function getDataMask() : int
    {
        return $this->dataMask;
    }
    /**
     * Hashes the code of the EC level.
     */
    public function hashCode() : int
    {
        return $this->ecLevel->getBits() << 3 | $this->dataMask;
    }
    /**
     * Verifies if this instance equals another one.
     */
    public function equals(self $other) : bool
    {
        return $this->ecLevel === $other->ecLevel && $this->dataMask === $other->dataMask;
    }
}
vendor/bacon/bacon-qr-code/src/Common/BitUtils.php000064400000001543150755130600016031 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode\Common;

/**
 * General bit utilities.
 *
 * All utility methods are based on 32-bit integers and also work on 64-bit
 * systems.
 */
final class BitUtils
{
    private function __construct()
    {
    }
    /**
     * Performs an unsigned right shift.
     *
     * This is the same as the unsigned right shift operator ">>>" in other
     * languages.
     */
    public static function unsignedRightShift(int $a, int $b) : int
    {
        return $a >= 0 ? $a >> $b : ($a & 0x7fffffff) >> $b | 0x40000000 >> $b - 1;
    }
    /**
     * Gets the number of trailing zeros.
     */
    public static function numberOfTrailingZeros(int $i) : int
    {
        $lastPos = \strrpos(\str_pad(\decbin($i), 32, '0', \STR_PAD_LEFT), '1');
        return $lastPos === \false ? 32 : 31 - $lastPos;
    }
}
vendor/bacon/bacon-qr-code/src/Writer.php000064400000003475150755130600014324 0ustar00<?php

declare (strict_types=1);
namespace WP2FA_Vendor\BaconQrCode;

use WP2FA_Vendor\BaconQrCode\Common\ErrorCorrectionLevel;
use WP2FA_Vendor\BaconQrCode\Common\Version;
use WP2FA_Vendor\BaconQrCode\Encoder\Encoder;
use WP2FA_Vendor\BaconQrCode\Exception\InvalidArgumentException;
use WP2FA_Vendor\BaconQrCode\Renderer\RendererInterface;
/**
 * QR code writer.
 */
final class Writer
{
    /**
     * Renderer instance.
     *
     * @var RendererInterface
     */
    private $renderer;
    /**
     * Creates a new writer with a specific renderer.
     */
    public function __construct(RendererInterface $renderer)
    {
        $this->renderer = $renderer;
    }
    /**
     * Writes QR code and returns it as string.
     *
     * Content is a string which *should* be encoded in UTF-8, in case there are
     * non ASCII-characters present.
     *
     * @throws InvalidArgumentException if the content is empty
     */
    public function writeString(string $content, string $encoding = Encoder::DEFAULT_BYTE_MODE_ECODING, ?ErrorCorrectionLevel $ecLevel = null, ?Version $forcedVersion = null) : string
    {
        if (\strlen($content) === 0) {
            throw new InvalidArgumentException('Found empty contents');
        }
        if (null === $ecLevel) {
            $ecLevel = ErrorCorrectionLevel::L();
        }
        return $this->renderer->render(Encoder::encode($content, $ecLevel, $encoding, $forcedVersion));
    }
    /**
     * Writes QR code to a file.
     *
     * @see Writer::writeString()
     */
    public function writeFile(string $content, string $filename, string $encoding = Encoder::DEFAULT_BYTE_MODE_ECODING, ?ErrorCorrectionLevel $ecLevel = null, ?Version $forcedVersion = null) : void
    {
        \file_put_contents($filename, $this->writeString($content, $encoding, $ecLevel, $forcedVersion));
    }
}

Youez - 2016 - github.com/yon3zu
LinuXploit