diff --git a/public/class-gdpr-requests-public.php b/public/class-gdpr-requests-public.php index c38d64f9..46761376 100644 --- a/public/class-gdpr-requests-public.php +++ b/public/class-gdpr-requests-public.php @@ -42,7 +42,7 @@ public function delete_user( $user, $index ) { if ( parent::remove_from_requests( $index ) ) { $token = GDPR::generate_pin(); - GDPR_Email::send( $user->user_email, 'delete-resolved', array( 'token' => $token ) ); + GDPR_Email::send( $this->get_escaped_user_email_address( $user ), 'delete-resolved', array( 'token' => $token ) ); GDPR_Audit_Log::log( $user->ID, esc_html__( 'User was removed from the site.', 'gdpr' ) ); GDPR_Audit_Log::export_log( $user->ID, $token ); wp_delete_user( $user->ID ); @@ -196,14 +196,15 @@ public function send_request_email() { break; } - $key = parent::add_to_requests( $user->user_email, $type, $data ); + $escaped_user_email = $this->get_escaped_user_email_address( $user ); + $key = parent::add_to_requests( $escaped_user_email, $type, $data ); if ( 'export-data' !== $type ) { $email_args['confirm_url'] = add_query_arg( array( 'type' => $type, 'key' => $key, - 'email' => $user->user_email, + 'email' => $escaped_user_email, ), home_url() ); @@ -212,7 +213,7 @@ public function send_request_email() { array( 'type' => $type, 'key' => $key, - 'email' => $user->user_email, + 'email' => $escaped_user_email, 'format' => 'xml', ), home_url() @@ -221,7 +222,7 @@ public function send_request_email() { array( 'type' => $type, 'key' => $key, - 'email' => $user->user_email, + 'email' => $escaped_user_email, 'format' => 'json', ), home_url() @@ -229,7 +230,7 @@ public function send_request_email() { } if ( GDPR_Email::send( - $user->user_email, + $escaped_user_email, "{$type}-request", $email_args ) ) { @@ -378,7 +379,7 @@ public function request_confirmed() { $format = isset( $_GET['format'] ) ? sanitize_text_field( wp_unslash( $_GET['format'] ) ) : 'xml'; // WPCS: Input var ok, CSRF ok. /* translators: File format. Can be XML or JSON */ GDPR_Audit_Log::log( $user->ID, sprintf( esc_html__( 'User downloaded all their data in %s format.', 'gdpr' ), $format ) ); - $this->file_export_data( $user->user_email, $format, $key ); + $this->file_export_data( $this->get_escaped_user_email_address( $user ), $format, $key ); break; } } @@ -406,4 +407,39 @@ private function file_export_data( $email, $format, $key ) { } die(); } + + /** + * Provides escaping for uncommon, yet valid email address characters. + * + * @param string $email_in The starting email address. + * + * @return mixed|string + */ + private function escape_email_address( $email_in = '' ) { + $email_out = ''; + $email_string_length = \strlen( $email_in ); + + for ( $i = 0; $i < $email_string_length; $i++ ) { + $hex = dechex( ord( $email_in[ $i ] ) ); + if ('' === $hex) { + $email_out .= rawurlencode( $email_in[ $i ] ); + } else { + $email_out = $email_out . '%' . ( ( 1 === strlen( $hex ) ) ? ( '0' . strtoupper( $hex ) ) : strtoupper( $hex ) ); + } + } + $email_out = str_replace( array( '+', '_', '.', '-' ), array( '%20', '%5F', '%2E', '%2D' ), $email_out ); + + return $email_out; + } + + /** + * Get the escpaed user email address. + * + * @param WP_User $user the User object. + * + * @return mixed|string + */ + private function get_escaped_user_email_address( WP_User $user ) { + return $this->escape_email_address( $user->user_email ); + } }