HEX
Server: Apache
System: Linux d5123.usc1.stableserver.net 5.14.0-570.17.1.el9_6.x86_64 #1 SMP PREEMPT_DYNAMIC Sat May 24 12:53:17 EDT 2025 x86_64
User: d5123 (1001)
PHP: 8.4.21
Disabled: NONE
Upload Files
File: /home/d5123/myboofola_com/wp-content/plugins/unsplash/php/class-settings.php
<?php
/**
 * Settings class.
 *
 * @package Unsplash
 */

namespace Unsplash;

/**
 * Plugin Settings.
 */
class Settings {

	/**
	 * Plugin interface.
	 *
	 * @var Plugin
	 */
	protected $plugin;

	/**
	 * Key to use for encryption.
	 *
	 * @var string
	 */
	private $key;

	/**
	 * Salt to use for encryption.
	 *
	 * @var string
	 */
	private $salt;

	/**
	 * Global parent application redirect_uri.
	 *
	 * @var string
	 */
	private $auth_redirect_uri;

	/**
	 * Global parent application client_id.
	 *
	 * @var string
	 */
	private $auth_client_id = '38mSDjSiO3qXfUf_o8zHyww7e2UX-zeJV5DpWCJjHQE';

	/**
	 * Global parent application client_secret.
	 *
	 * @var string
	 */
	private $auth_client_secret = 'D-S-kaH92uWKfo956txd9zBmPz-YgmBETdo_xE3TwxA';

	/**
	 * Constructor.
	 *
	 * @param Plugin $plugin Instance of the plugin abstraction.
	 */
	public function __construct( $plugin ) {
		$this->plugin = $plugin;
		$this->key    = $this->get_default_key();
		$this->salt   = $this->get_default_salt();

		// Set the redirect_uri.
		$this->auth_redirect_uri = get_admin_url( null, 'options-general.php?page=unsplash' );
	}

	/**
	 * Initiate the class.
	 */
	public function init() {
		add_action( 'admin_menu', [ $this, 'add_admin_menu' ] );
		add_action( 'admin_init', [ $this, 'add_settings' ] );
		add_action( 'admin_init', [ $this, 'handle_auth_flow' ] );
	}

	/**
	 * Encrypts a value.
	 *
	 * If a user-based key is set, that key is used. Otherwise the default key is used.
	 *
	 * @param string $value Value to encrypt.
	 * @return string|bool Encrypted value, or false on failure.
	 */
	public function encrypt( $value ) {
		if ( ! extension_loaded( 'openssl' ) ) {
			return $value;
		}

		$method = 'aes-256-ctr';
		$ivlen  = openssl_cipher_iv_length( $method );
		$iv     = openssl_random_pseudo_bytes( $ivlen );

		$raw_value = openssl_encrypt( $value . $this->salt, $method, $this->key, 0, $iv );
		if ( ! $raw_value ) {
			return false;
		}

		return base64_encode( $iv . $raw_value ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
	}

	/**
	 * Decrypts a value.
	 *
	 * If a user-based key is set, that key is used. Otherwise the default key is used.
	 *
	 * @param string $raw_value Value to decrypt.
	 * @return string|bool Decrypted value, or false on failure.
	 */
	public function decrypt( $raw_value ) {
		if ( ! extension_loaded( 'openssl' ) ) {
			return $raw_value;
		}

		$raw_value = base64_decode( $raw_value, true ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode

		$method = 'aes-256-ctr';
		$ivlen  = openssl_cipher_iv_length( $method );
		$iv     = substr( $raw_value, 0, $ivlen );

		$raw_value = substr( $raw_value, $ivlen );

		$value = openssl_decrypt( $raw_value, $method, $this->key, 0, $iv );
		if ( ! $value || substr( $value, - strlen( $this->salt ) ) !== $this->salt ) {
			return false;
		}

		return substr( $value, 0, - strlen( $this->salt ) );
	}

	/**
	 * Gets the default encryption key to use.
	 *
	 * @return string Default (not user-based) encryption key.
	 */
	private function get_default_key() {
		if ( defined( 'UNSPLASH_ENCRYPTION_KEY' ) && '' !== UNSPLASH_ENCRYPTION_KEY ) {
			return UNSPLASH_ENCRYPTION_KEY;
		}

		if ( defined( 'LOGGED_IN_KEY' ) && '' !== LOGGED_IN_KEY ) {
			return LOGGED_IN_KEY;
		}

		// If this is reached, you're either not on a live site or have a serious security issue.
		return 'there-is-no-secret-key';
	}

	/**
	 * Gets the default encryption salt to use.
	 *
	 * @return string Encryption salt.
	 */
	private function get_default_salt() {
		if ( defined( 'UNSPLASH_ENCRYPTION_SALT' ) && '' !== UNSPLASH_ENCRYPTION_SALT ) {
			return UNSPLASH_ENCRYPTION_SALT;
		}

		if ( defined( 'LOGGED_IN_SALT' ) && '' !== LOGGED_IN_SALT ) {
			return LOGGED_IN_SALT;
		}

		// If this is reached, you're either not on a live site or have a serious security issue.
		return 'there-is-no-secret-salt';
	}

	/**
	 * Adds the Unsplash admin menu.
	 */
	public function add_admin_menu() {
		add_options_page( 'Unsplash', 'Unsplash', 'manage_options', 'unsplash', [ $this, 'settings_page_render' ] );
	}

	/**
	 * Add the Unsplash settings.
	 */
	public function add_settings() {
		$args = [
			'sanitize_callback' => [ $this, 'sanitize_settings' ],
		];
		register_setting( 'unsplash', 'unsplash_settings', $args );

		add_settings_section(
			'unsplash_section',
			esc_html__( 'Manual API Authentication', 'unsplash' ),
			[ $this, 'settings_section_render' ],
			'unsplash'
		);

		add_settings_field(
			'access_key',
			esc_html__( 'Access Key', 'unsplash' ),
			[ $this, 'access_key_render' ],
			'unsplash',
			'unsplash_section'
		);
	}

	/**
	 * Sanitize the Unsplash settings.
	 *
	 * @param array $settings Values being stored in the DB.
	 * @return array Sanitized and encrypted values.
	 */
	public function sanitize_settings( $settings ) {
		$options = get_option( 'unsplash_settings' );

		foreach ( $settings as $key => $value ) {
			$should_encrypt = (
			'access_key' === $key
			&& ! empty( $value )
			&& (
				! isset( $options[ $key ] )
				|| $options[ $key ] !== $value
			)
			);

			if ( $should_encrypt ) {
				$settings[ $key ] = $this->encrypt( $value );
			} else {
				$settings[ $key ] = sanitize_text_field( $value );
			}
		}

		return $settings;
	}

	/**
	 * Renders the entire settings page.
	 */
	public function settings_page_render() {
		$logo = $this->plugin->asset_url( 'assets/images/logo.svg' );
		$auth = get_option( 'unsplash_auth' );
		if ( ! empty( $auth['message'] ) ) {
			printf( '<div class="%s notice is-dismissible"><p>%s</p></div>', esc_attr( $auth['type'] ), esc_html( $auth['message'] ) );
			delete_option( 'unsplash_auth' );
		}
		?>
		<h1><img src="<?php echo esc_url( $logo ); ?>" height="26" />  <?php esc_html_e( 'Unsplash', 'unsplash' ); ?></h1>
		<p><i><?php esc_html_e( 'Search the internet’s source of freely usable images.', 'unsplash' ); ?></i></p><br />
		<h1><?php esc_html_e( 'General Settings', 'unsplash' ); ?></h1>
		<?php

		$credentials   = $this->get_credentials();
		$status        = $this->plugin->api->check_api_status( [], false, true );
		$register_link = sprintf(
			'https://unsplash.com/oauth/authorize?client_id=%1$s&response_type=code&scope=public&redirect_uri=%2$s',
			esc_html( $this->auth_client_id ),
			urlencode( wp_nonce_url( $this->auth_redirect_uri, 'auth' ) )
		);
		$settings      = get_option( 'unsplash_settings' );

		if ( empty( $credentials['applicationId'] ) && empty( $settings['access_key'] ) ) {
			$register = sprintf(
				'<p><a href="%1$s" class="button button-primary">%2$s</a></p>',
				esc_url( $register_link ),
				esc_html__( 'Complete setup', 'unsplash' )
			);
			printf( '<div class="notice notice-warning notice-unsplash"><p>%1$s</p> %2$s</div>', esc_html__( 'To complete setup of the Unsplash plugin you will need to add the API access key.', 'unsplash' ), wp_kses_post( $register ) );
		} elseif ( is_wp_error( $status ) ) {
			$status_data = $status->get_error_data();
			$status_code = ( isset( $status_data['status'] ) ) ? $status_data['status'] : 500;
			if ( in_array( $status_code, [ 401, 403 ], true ) ) {
				$register = sprintf(
					'<p><a href="%1$s" class="button button-primary">%2$s</a></p>',
					esc_url( $register_link ),
					esc_html__( 'Restart setup', 'unsplash' )
				);
				$dismiss  = $this->plugin->asset_url( 'assets/images/dismiss.svg' );
				printf( '<div class="notice notice-error notice-unsplash"><p>%1$s <img src="%2$s" class="unsplash-icon unsplash-icon-dismiss" alt="%3$s" /></p> %4$s</div>', esc_html__( 'Unable to authenticate due to an error with the access key.', 'unsplash' ), esc_url( $dismiss ), esc_html__( 'Dismiss icon', 'unsplash' ), wp_kses_post( $register ) );
			} else {
				$message = $status->get_error_message();
				printf( '<div class="notice notice-error"><p>%1$s</p></div>', wp_kses_post( $message ) );
			}
		} else {
			$yes = $this->plugin->asset_url( 'assets/images/yes-alt.svg' );
			printf( '<h3>%1$s <img src="%2$s" class="unsplash-icon unsplash-icon-yes-alt" alt="%3$s" /></h3>', esc_html__( 'Unsplash setup is complete', 'unsplash' ), esc_url( $yes ), esc_html__( 'Success icon', 'unsplash' ) );
			if ( ! empty( $settings['access_key'] ) ) {
				?>
					<form action='options.php' method='post'>
					<?php settings_fields( 'unsplash' ); ?>
						<input type='hidden' name='unsplash_settings[access_key]' value="">
						<p>
						<?php
						printf(
						/* translators: %s: Button to deauthenticate. */
							esc_html__( 'If you need to change the Unsplash account that is associated with this plugin, you can always %1$s the connection and start over.', 'unsplash' ),
							get_submit_button( esc_html__( 'deauthenticate', 'unsplash' ), 'button-link', 'submit', false, false ) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
						);
						?>
						</p>
						<p>
							<i><?php esc_html_e( 'Note: While this action will disconnect your current OAuth integration to Unsplash, it will not remove any images previously published or imported to your media library.', 'unsplash' ); ?></i>
						</p>
					</form>
					<?php
			}
		}

		?>
		<br />
		<form action='options.php' method='post'>
				<?php
				settings_fields( 'unsplash' );
				do_settings_sections( 'unsplash' );
				submit_button();
				?>
		</form>
		<?php
	}

	/**
	 * Redirect page with wp_safe_redirect.
	 *
	 * @codeCoverageIgnore
	 */
	public function redirect() {
		if ( wp_safe_redirect( $this->auth_redirect_uri ) ) {
			exit;
		}
	}

	/**
	 * Update auth options and redirect.
	 *
	 * @param string $message The notice message.
	 * @param string $type The notice type. Default: error.
	 */
	public function redirect_auth( $message = '', $type = 'error' ) {
		update_option(
			'unsplash_auth',
			[
				'message' => $message,
				'type'    => $type,
			]
		);

		$this->redirect();
	}

	/**
	 * Handles the authentication flow for registering a dynamic client application.
	 */
	public function handle_auth_flow() {
		$code = $this->get_code();

		if ( $code ) {
			$client_id = $this->get_client_id( $this->get_access_token( $code ) );

			if ( $client_id ) {
				remove_filter( 'sanitize_option_unsplash_settings', [ $this, 'sanitize_settings' ] );
				update_option(
					'unsplash_settings',
					[
						'access_key' => $this->encrypt( $client_id ),
					]
				);
				add_filter( 'sanitize_option_unsplash_settings', [ $this, 'sanitize_settings' ] );

				$credentials = [
					'applicationId' => $client_id,
					'utmSource'     => 'WordPress',
				];

				$api = $this->plugin->api;

				if ( true !== $api->check_api_credentials() || true !== $api->check_api_status( $credentials ) ) {
					$this->redirect_auth( esc_html__( 'Unsplash setup has failed, could not connect to the Unsplash API.', 'unsplash' ) );
					return false;
				}

				$this->redirect_auth( esc_html__( 'Your Client Application was successfully created, and is now connected to the Unsplash API.', 'unsplash' ), 'updated' );
			}
		}
	}

	/**
	 * Get the code during the auth flow.
	 *
	 * @return mixed
	 */
	public function get_code() {
		$nonce = isset( $_REQUEST['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ) : '';
		$code  = isset( $_REQUEST['code'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['code'] ) ) : '';

		if ( $nonce && wp_verify_nonce( $nonce, 'auth' ) && ! empty( $code ) ) {
			return $code;
		}

		return false;
	}

	/**
	 * Get the access_token during the auth flow.
	 *
	 * @param string $code The auth code.
	 * @return mixed
	 */
	public function get_access_token( $code ) {
		$response = wp_remote_post(
			'https://unsplash.com/oauth/token',
			[
				'body' => [
					'client_id'     => $this->auth_client_id,
					'client_secret' => $this->auth_client_secret,
					'redirect_uri'  => wp_nonce_url( $this->auth_redirect_uri, 'auth' ),
					'code'          => $code,
					'grant_type'    => 'authorization_code',
				],
			]
		);

		$error = esc_html__( 'Could not generate an Unsplash API access_token.', 'unsplash' );

		if ( ! is_array( $response ) || is_wp_error( $response ) ) {
			$this->redirect_auth( $error );
			return false;
		}

		// Setup the result from the response body.
		$result = json_decode( wp_remote_retrieve_body( $response ), true );

		if ( ! is_wp_error( $result ) && is_array( $result ) && isset( $result['access_token'] ) ) {
			return $result['access_token'];
		}

		// Handle error message from API.
		if ( is_array( $result ) && isset( $result['error_description'] ) ) {
			$this->redirect_auth( esc_html( $result['error_description'] ) );
			return false;
		}

		$this->redirect_auth( $error );
		return false;
	}

	/**
	 * Get the client_id during the auth flow.
	 *
	 * @param string $access_token The access token.
	 * @return mixed
	 */
	public function get_client_id( $access_token ) {
		$site_data = $this->get_site_data();
		$response  = wp_remote_post(
			'https://api.unsplash.com/clients',
			[
				'body'    => [
					'name'        => $site_data['name'],
					'description' => sprintf( 'Wordpress Oauth Client application for: %1$s - %2$s', $site_data['name'], $site_data['url'] ),
				],
				'headers' => [
					'Authorization' => 'Bearer ' . $access_token,
				],
			]
		);

		$error = esc_html__( 'Could not generate an Unsplash API client_id.', 'unsplash' );
		$code  = wp_remote_retrieve_response_code( $response );

		if ( ! is_array( $response ) || is_wp_error( $response ) || ! in_array( $code, [ 200, 201 ] ) ) {
			$this->redirect_auth( $error );
			return false;
		}

		// Setup the result from the response body.
		$result = json_decode( wp_remote_retrieve_body( $response ), true );

		if ( ! is_wp_error( $result ) && is_array( $result ) && isset( $result['client_id'] ) ) {
			return $result['client_id'];
		}

		$this->redirect_auth( $error );
		return false;
	}

	/**
	 * Renders the settings section.
	 */
	public function settings_section_render() {
		/* translators: %s: Link to OAuth Applications page. */
		echo '<p>' . esc_html__( 'Always use the default automated setup unless a manual authentication process is required. ', 'unsplash' ) . '</p>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
	}

	/**
	 * Renders the Access Key.
	 */
	public function access_key_render() {
		$options = get_option( 'unsplash_settings' );
		?>
		<input type='password' class="widefat" id='unsplash_access_key' name='unsplash_settings[access_key]' aria-describedby="unsplash-key-description" value='<?php echo esc_attr( isset( $options['access_key'] ) ? $options['access_key'] : '' ); ?>'>
		<p class="description" id="unsplash-key-description"><?php esc_html_e( 'Only use if you have an API key to manually enter for authentication.', 'unsplash' ); ?></p>
		<?php
	}

	/**
	 * Format the API credentials in an array and filter.
	 *
	 * @return mixed|array
	 */
	public function get_credentials() {
		$options        = get_option( 'unsplash_settings' );
		$site_data      = $this->get_site_data();
		$site_name_slug = sanitize_title_with_dashes( $site_data['name'] );

		$credentials = [
			'applicationId' => ! empty( $options['access_key'] ) ? $this->decrypt( $options['access_key'] ) : getenv( 'UNSPLASH_ACCESS_KEY' ),
			'utmSource'     => getenv( 'UNSPLASH_UTM_SOURCE' ) ? getenv( 'UNSPLASH_UTM_SOURCE' ) : $site_name_slug,
		];

		/**
		 * Filter API credentials.
		 *
		 * @param array $credentials Array of API credentials.
		 * @param array $options Unsplash settings.
		 */
		$credentials = apply_filters( 'unsplash_api_credentials', $credentials, $options );

		return $credentials;
	}

	/**
	 * Get site name and url. If site name is empty, fallback to domain with dashes.
	 *
	 * @return array
	 */
	public function get_site_data() {
		$data = [
			'url'  => get_home_url( null, '/' ),
			'name' => get_bloginfo( 'name' ),
		];
		if ( ! $data['name'] ) {
			$url = wp_parse_url( $data['url'] );
			if ( $url && array_key_exists( 'host', $url ) ) {
				$data['name'] = sanitize_title_with_dashes( $url['host'] );
			}
		}

		return $data;
	}
}