'php', 'from_email' => '', 'force_from_email' => 1, 'from_name' => '', 'force_from_name' => 0, 'return_path' => 1, 'reply_to' => '', 'smtp_host' => '', 'smtp_port' => 465, 'smtp_encryption' => 'ssl', 'smtp_autotls' => 1, 'smtp_auth' => 1, 'smtp_user' => '', 'uninstall_delete' => 0, ); } /** * Map of setting => constant name. * * @return array */ public static function constant_map() { return apply_filters( 'ajsm_constant_map', array( 'mailer' => 'AJSM_MAILER', 'from_email' => 'AJSM_FROM_EMAIL', 'from_name' => 'AJSM_FROM_NAME', 'reply_to' => 'AJSM_REPLY_TO', 'smtp_host' => 'AJSM_SMTP_HOST', 'smtp_port' => 'AJSM_SMTP_PORT', 'smtp_encryption' => 'AJSM_SMTP_ENCRYPTION', 'smtp_auth' => 'AJSM_SMTP_AUTH', 'smtp_user' => 'AJSM_SMTP_USER', 'smtp_pass' => 'AJSM_SMTP_PASS', ) ); } /** * Constant name for a key if defined. * * @param string $key Setting key. * @return string Constant name or ''. */ public static function constant_for( $key ) { $map = self::constant_map(); $name = isset( $map[ $key ] ) ? $map[ $key ] : 'AJSM_' . strtoupper( $key ); return defined( $name ) ? $name : ''; } /** * Raw saved settings merged with defaults (no constants). * * @return array */ public static function saved() { $s = get_option( self::KEY, array() ); return array_merge( self::defaults(), apply_filters( 'ajsm_default_settings', array() ), is_array( $s ) ? $s : array() ); } /** * All settings with constants applied. * * @return array */ public static function all() { $s = self::saved(); foreach ( array_keys( $s ) as $k ) { $c = self::constant_for( $k ); if ( $c ) { $s[ $k ] = constant( $c ); } } if ( defined( 'AJSM_ON' ) && ! AJSM_ON ) { $s['mailer'] = 'php'; } return $s; } /** * One setting. * * @param string $key Key. * @param mixed $fallback Fallback. * @return mixed */ public static function get( $key, $fallback = null ) { $s = self::all(); return array_key_exists( $key, $s ) ? $s[ $key ] : $fallback; } /** * Save settings. * * @param array $values Clean values. * @return bool */ public static function update( array $values ) { $new = array_merge( self::saved(), $values ); if ( get_option( self::KEY ) === $new ) { return true; } return update_option( self::KEY, $new, true ); } /** * Secret field keys (free + add-ons). * * @return string[] */ public static function secret_keys() { return (array) apply_filters( 'ajsm_secret_keys', array( 'smtp_pass' ) ); } /** * Decrypted secret, constant first. * * @param string $key Key. * @return string */ public static function secret( $key ) { $c = self::constant_for( $key ); if ( $c ) { return (string) constant( $c ); } $all = get_option( self::SECRETS, array() ); return isset( $all[ $key ] ) ? Crypto::decrypt( $all[ $key ] ) : ''; } /** * Whether a secret is stored or defined. * * @param string $key Key. * @return bool */ public static function has_secret( $key ) { if ( self::constant_for( $key ) ) { return true; } $all = get_option( self::SECRETS, array() ); return ! empty( $all[ $key ] ); } /** * Store (or clear with '') a secret. * * @param string $key Key. * @param string $value Plain value. * @return bool */ public static function set_secret( $key, $value ) { $all = get_option( self::SECRETS, array() ); $all = is_array( $all ) ? $all : array(); if ( '' === (string) $value ) { unset( $all[ $key ] ); } else { $enc = Crypto::encrypt( $value ); if ( '' === $enc ) { return false; } $all[ $key ] = $enc; } if ( get_option( self::SECRETS, null ) === null ) { return add_option( self::SECRETS, $all, '', false ); } if ( get_option( self::SECRETS ) === $all ) { return true; } return update_option( self::SECRETS, $all, false ); } /** * All secret plain values, for masking. * * @return string[] */ public static function secret_values() { $out = array(); foreach ( self::secret_keys() as $k ) { $v = self::secret( $k ); if ( strlen( $v ) >= 3 ) { $out[] = $v; } } return $out; } /** * Replace any secret (plain or base64 forms) with ****. * * @param string $text Text. * @return string */ public static function mask( $text ) { $text = (string) $text; $user = (string) self::get( 'smtp_user', '' ); foreach ( self::secret_values() as $v ) { // phpcs:disable WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode -- masking the encoded forms SMTP AUTH uses. $forms = array( $v, base64_encode( $v ), base64_encode( "\0" . $user . "\0" . $v ), base64_encode( $user . "\0" . $user . "\0" . $v ) ); // phpcs:enable foreach ( $forms as $f ) { $text = str_replace( $f, '********', $text ); } } return $text; } /** * Record an error (last 20). * * @param string $message Message. * @param array $context to/subject/mailer. */ public static function log_error( $message, array $context = array() ) { $list = get_option( self::ERRORS, array() ); $list = is_array( $list ) ? $list : array(); array_unshift( $list, array( 'time' => time(), 'message' => self::mask( wp_strip_all_tags( (string) $message ) ), 'to' => isset( $context['to'] ) ? sanitize_text_field( implode( ', ', (array) $context['to'] ) ) : '', 'subject' => isset( $context['subject'] ) ? sanitize_text_field( (string) $context['subject'] ) : '', 'mailer' => isset( $context['mailer'] ) ? sanitize_key( $context['mailer'] ) : '', ) ); $list = array_slice( $list, 0, 20 ); if ( false === get_option( self::ERRORS, false ) ) { add_option( self::ERRORS, $list, '', false ); } else { update_option( self::ERRORS, $list, false ); } } /** * Recent errors. * * @return array */ public static function errors() { $l = get_option( self::ERRORS, array() ); return is_array( $l ) ? $l : array(); } }