emails = $emails; } protected function get_nonce_name() { return self::ACTION_NAME; } public function handle() { if ( ! $this->validate() ) { wp_send_json_error( __( 'Missing required parameters.', 'gravitysmtp' ), 400 ); } $event_id = filter_input( INPUT_GET, self::PARAM_EVENT_ID, FILTER_SANITIZE_NUMBER_INT ); $email = $this->emails->find( array( array( 'id', '=', $event_id ) ) ); if ( empty( $email[0] ) ) { header( 'Content-Type: text/html' ); /* translators: %d: email ID */ echo sprintf( __( 'Could not get content for email ID: %d.', 'gravitysmtp' ), $email ); wp_die(); } header( 'Content-Type: text/html' ); echo $this->format_email_content( $email[0]['message'] ); wp_die(); } protected function format_email_content( $content ) { if ( $content !== strip_tags( $content ) ) { // Open links in a new tab (target) and harden with rel=noopener noreferrer where needed. $content = preg_replace_callback( '/]*?)>/i', function ( $matches ) { $attrs = $matches[1]; $trimmed = trim( $attrs ); if ( '' === $trimmed ) { return $matches[0]; } $attrs = $this->ensure_anchor_target_blank( $attrs ); $attrs = $this->ensure_anchor_rel_noopener_noreferrer( $attrs ); return ''; }, $content ); return $content; } else { return '
' . htmlspecialchars( $content ) . '
'; } } /** * Ensures anchor tags open in a new tab when attributes are present. * * @param string $attrs Raw attribute string from the opening <a> tag. * * @return string */ protected function ensure_anchor_target_blank( $attrs ) { if ( preg_match( '/\btarget\s*=\s*(["\']?)_blank\1/i', $attrs ) ) { return $attrs; } if ( preg_match( '/\btarget\s*=\s*("|\')(.*?)\1/is', $attrs ) ) { return preg_replace( '/\btarget\s*=\s*("|\')(.*?)\1/is', 'target="_blank"', $attrs, 1 ); } if ( preg_match( '/\btarget\s*=\s*[^\s>]+/i', $attrs ) ) { return preg_replace( '/\btarget\s*=\s*[^\s>]+/i', 'target="_blank"', $attrs, 1 ); } return rtrim( $attrs ) . ' target="_blank"'; } /** * Ensures rel includes noopener and noreferrer (safe external links with target _blank). * * @param string $attrs Raw attribute string from the opening <a> tag. * * @return string */ protected function ensure_anchor_rel_noopener_noreferrer( $attrs ) { $rel_quoted = '/\brel\s*=\s*(["\'])(.*?)\1/is'; if ( preg_match( $rel_quoted, $attrs, $rel_match ) ) { $tokens = preg_split( '/\s+/', trim( $rel_match[2] ), -1, PREG_SPLIT_NO_EMPTY ); if ( $this->rel_has_noopener_and_noreferrer( $tokens ) ) { return $attrs; } $tokens = $this->merge_rel_noopener_noreferrer_tokens( $tokens ); $new_rel = implode( ' ', $tokens ); return preg_replace( $rel_quoted, 'rel="' . $new_rel . '"', $attrs, 1 ); } if ( preg_match( '/\brel\s*=\s*([^\s>]+)/i', $attrs, $rel_match ) ) { $tokens = preg_split( '/\s+/', trim( $rel_match[1] ), -1, PREG_SPLIT_NO_EMPTY ); if ( $this->rel_has_noopener_and_noreferrer( $tokens ) ) { return $attrs; } $tokens = $this->merge_rel_noopener_noreferrer_tokens( $tokens ); $new_rel = implode( ' ', $tokens ); return preg_replace( '/\brel\s*=\s*[^\s>]+/i', 'rel="' . $new_rel . '"', $attrs, 1 ); } return rtrim( $attrs ) . ' rel="noopener noreferrer"'; } /** * @param array $tokens rel attribute tokens. * * @return bool */ protected function rel_has_noopener_and_noreferrer( array $tokens ) { $lower = array_map( 'strtolower', $tokens ); return in_array( 'noopener', $lower, true ) && in_array( 'noreferrer', $lower, true ); } /** * @param array $tokens rel attribute tokens. * * @return array */ protected function merge_rel_noopener_noreferrer_tokens( array $tokens ) { $lower = array_map( 'strtolower', $tokens ); if ( ! in_array( 'noopener', $lower, true ) ) { $tokens[] = 'noopener'; } if ( ! in_array( 'noreferrer', $lower, true ) ) { $tokens[] = 'noreferrer'; } return $tokens; } protected function validate() { if ( ! parent::validate() ) { return false; } if ( empty( $_REQUEST[ self::PARAM_EVENT_ID ] ) ) { return false; } return true; } }