PluginProbe ʕ •ᴥ•ʔ
Email Encoder – Protect Email Addresses and Phone Numbers / 2.4.5
Email Encoder – Protect Email Addresses and Phone Numbers v2.4.5
2.5.0 2.4.8 trunk 0.10 0.11 0.12 0.20 0.21 0.22 0.30 0.31 0.32 0.40 0.41 0.42 0.50 0.60 0.70 0.71 0.80 1.0.0 1.0.1 1.0.2 1.1.0 1.2.0 1.2.1 1.3.0 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.5 1.5.2 1.51 1.53 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1.0 2.1.1 2.1.10 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.3.0 2.3.1 2.3.3 2.3.4 2.3.5 2.3.6 2.3.7 2.3.8 2.3.9 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.4.7
email-encoder-bundle / src / Validate / Encoding.php
email-encoder-bundle / src / Validate Last commit date
EncoderForm.php 3 months ago Encoding.php 3 months ago Filters.php 3 months ago Validate.php 5 months ago
Encoding.php
586 lines
1 <?php
2
3 namespace OnlineOptimisation\EmailEncoderBundle\Validate;
4
5 use OnlineOptimisation\EmailEncoderBundle\Traits\PluginHelper;
6
7 class Encoding
8 {
9 use PluginHelper;
10
11 private string $at_identifier;
12
13 public function boot(): void
14 {
15 $this->at_identifier = $this->settings()->get_at_identifier();
16 }
17
18
19 /**
20 * ######################
21 * ###
22 * #### ENCODINGS
23 * ###
24 * ######################
25 */
26
27 /**
28 * @param string $content
29 * @param bool $decode
30 * @return string
31 */
32 public function temp_encode_at_symbol( string $content, bool $decode = false )
33 {
34 if ( $decode ) {
35 return str_replace( $this->at_identifier, '@', $content );
36 }
37
38 return str_replace( '@', $this->at_identifier, $content );
39 }
40
41 /**
42 * ASCII method
43 *
44 * @param string $value
45 * @param string $protection_text
46 * @return string
47 */
48 public function encode_ascii( $value, $protection_text )
49 {
50 $mail_link = $value;
51
52 // first encode, so special chars can be supported
53 $mail_link = $this->helper()->encode_uri_components( $mail_link );
54
55 $mail_letters = '';
56
57 for ( $i = 0; $i < strlen( $mail_link ); $i++ ) {
58 $l = substr($mail_link, $i, 1);
59
60 if (strpos($mail_letters, $l) === false) {
61 $p = wp_rand(0, strlen($mail_letters));
62 $mail_letters = substr($mail_letters, 0, $p) .
63 $l . substr($mail_letters, $p, strlen($mail_letters));
64 }
65 }
66
67 $mail_letters_enc = str_replace( "\\", "\\\\", $mail_letters );
68 $mail_letters_enc = str_replace( "\"", "\\\"", $mail_letters_enc );
69
70 $mail_indices = '';
71 for ( $i = 0; $i < strlen( $mail_link ); $i++ ) {
72 $index = strpos( $mail_letters, substr( $mail_link, $i, 1 ) );
73 $index += 48;
74 $mail_indices .= chr( $index );
75 }
76
77 $mail_indices = str_replace("\\", "\\\\", $mail_indices);
78 $mail_indices = str_replace("\"", "\\\"", $mail_indices);
79
80 $element_id = 'eeb-' . wp_rand( 0, 1000000 ) . '-' . wp_rand(0, 1000000);
81
82 return '<span id="' . $element_id . '"></span>'
83 . '<script type="text/javascript">'
84 . '(function() {'
85 . 'var ml="' . $mail_letters_enc . '",mi="' . $mail_indices . '",o="";'
86 . 'for(var j=0,l=mi.length;j<l;j++) {'
87 . 'o+=ml.charAt(mi.charCodeAt(j)-48);'
88 . '}document.getElementById("' . $element_id . '").innerHTML = decodeURIComponent(o);' // decode at the end, this way special chars can be supported
89 . '}());'
90 . '</script><noscript>'
91 . esc_html( $protection_text )
92 . '</noscript>'
93 ;
94 }
95
96 /**
97 * Escape encoding method
98 *
99 * @param string $value
100 * @param string $protection_text
101 * @return string
102 */
103 public function encode_escape( $value, $protection_text )
104 {
105 $element_id = 'eeb-' . wp_rand( 0, 1000000 ) . '-' . wp_rand( 0, 1000000 );
106
107 //Validate escape sequences
108 $string = preg_replace('/\s+/S', " ", $value) ?? '';
109
110 // break string into array of characters, we can't use string_split because its php5 only
111 $split = preg_split( '||', $string );
112 $out = '<span id="' . esc_attr( $element_id ) . '"></span>'
113 . '<script type="text/javascript">'
114 . 'document.getElementById("' . $element_id . '").innerHTML = decodeURIComponent("';
115
116 if ( is_array( $split ) )
117 foreach ( $split as $c ) {
118 // preg split will return empty first and last characters, check for them and ignore
119 if ( ! empty( $c ) || $c === '0' ) {
120 $out .= '%' . dechex( ord( $c ) );
121 }
122 }
123
124 $out .= '");'
125 . '</script><noscript>'
126 . esc_html( $protection_text )
127 . '</noscript>';
128
129 return $out;
130 }
131
132 /**
133 * Encode email in input field
134 * @param string $input
135 * @param string $email
136 * @param bool $strongEncoding
137 * @return string
138 */
139 public function encode_input_field( $input, $email, $strongEncoding = false )
140 {
141
142 $show_encoded_check = (bool) $this->getSetting( 'show_encoded_check', true );
143
144 if ( $strongEncoding === false ) {
145 // encode email with entities (default wp method)
146 $sub_return = str_replace( $email, antispambot( $email ), $input );
147
148 if ( current_user_can( $this->getAdminCap( 'frontend-display-security-check' ) ) && $show_encoded_check ) {
149 $sub_return .= $this->get_encoded_email_icon();
150 }
151
152 return $sub_return;
153 }
154
155 // add data-enc-email after "<input"
156 $inputWithDataAttr = substr( $input, 0, 6 );
157 $inputWithDataAttr .= ' data-enc-email="' . $this->get_encoded_email( $email ) . '"';
158 $inputWithDataAttr .= substr( $input, 6 );
159
160 // mark link as successfullly encoded (for admin users)
161 if ( current_user_can( $this->getAdminCap( 'frontend-display-security-check' ) ) && $show_encoded_check ) {
162 $inputWithDataAttr .= $this->get_encoded_email_icon();
163 }
164
165 // remove email from value attribute
166 $encInput = str_replace( $email, '', $inputWithDataAttr );
167
168 return $encInput;
169 }
170
171 /**
172 * Get encoded email, used for data-attribute (translate by javascript)
173 *
174 * @param string $email
175 * @return string
176 */
177 public function get_encoded_email( $email )
178 {
179 $encEmail = $email;
180
181 // decode entities
182 $encEmail = html_entity_decode( $encEmail );
183
184 // rot13 encoding
185 // phpcs:ignore Generic.PHP.ForbiddenFunctions.Found -- str_rot13 is intentional for email obfuscation
186 $encEmail = str_rot13( $encEmail );
187
188 // replace @
189 $encEmail = str_replace( '@', '[at]', $encEmail );
190
191 return $encEmail;
192 }
193
194 /**
195 * Get the ebcoded email icon
196 *
197 * @param string $text
198 * @return string
199 */
200 public function get_encoded_email_icon( $text = 'Email encoded successfully!' )
201 {
202
203 $html = '<i class="eeb-encoded dashicons-before dashicons-lock" title="' . esc_attr( $text ) . '"></i>';
204
205 return apply_filters( 'eeb/validate/get_encoded_email_icon', $html, $text );
206 }
207
208 /**
209 * Create a protected email
210 *
211 * @param string $display
212 * @param array< string, string > $attrs
213 * @param string $protection_method
214 * @return string
215 */
216 public function create_protected_mailto( $display, $attrs = [], $protection_method = null )
217 {
218 $email = '';
219 $class_ori = ( empty( $attrs['class'] ) ) ? '' : $attrs['class'];
220 $custom_class = (string) $this->getSetting( 'class_name', true );
221 $show_encoded_check = (string) $this->getSetting( 'show_encoded_check', true );
222
223 // set user-defined class
224 if ( $custom_class !== '' && strpos( $class_ori, $custom_class ) === false ) {
225 $attrs['class'] = ( empty( $attrs['class'] ) ) ? $custom_class : $attrs['class'] . ' ' . $custom_class;
226 }
227
228 // check title for email address
229 if ( ! empty( $attrs['title'] ) ) {
230 $attrs['title'] = $this->filterPlainEmails( $attrs['title'], '{{email}}' ); // {{email}} will be replaced in javascript
231 }
232
233 // set ignore to data-attribute to prevent being processed by WPEL plugin
234 $attrs['data-wpel-link'] = 'ignore';
235
236 // create element code
237 $link = '<a ';
238
239 foreach ( $attrs as $key => $value ) {
240 if ( strtolower( $key ) === 'href' ) {
241 if ( $protection_method === 'without_javascript' ) {
242 $link .= $key . '="' . antispambot( $value ) . '" ';
243 } else {
244 // get email from href
245 $email = substr($value, 7);
246
247 $encoded_email = $this->get_encoded_email( $email );
248
249 // set attrs
250 $link .= 'href="javascript:;" ';
251 $link .= 'data-enc-email="' . $encoded_email . '" ';
252 }
253
254 } else {
255 $link .= esc_attr( $key ) . '="' . esc_attr( $value ) . '" ';
256 }
257 }
258
259 // remove last space
260 $link = substr( $link, 0, -1 );
261
262 $link .= '>';
263
264 $link .= ( preg_match( $this->settings()->get_email_regex(), $display) > 0 ) ? $this->get_protected_display( $display, $protection_method ) : $display;
265
266 $link .= '</a>';
267
268 // filter
269 $link = apply_filters( 'eeb_mailto', $link, $display, $email, $attrs );
270
271 // just in case there are still email addresses f.e. within title-tag
272 $link = $this->filterPlainEmails( $link, null, 'char_encode' );
273
274 // mark link as successfullly encoded (for admin users)
275 if ( current_user_can( $this->getAdminCap( 'frontend-display-security-check' ) ) && $show_encoded_check !== '' ) {
276 $link .= $this->get_encoded_email_icon();
277 }
278
279
280 return $link;
281 }
282
283 /**
284 * Create a protected custom attribute
285 *
286 * @param string $display
287 * @param array< string, string > $attrs Optional
288 * @param string $protection_method
289 * @return string
290 */
291 public function create_protected_href_att( $display, $attrs = [], $protection_method = null )
292 {
293 $email = '';
294 $class_ori = ( empty( $attrs['class'] ) ) ? '' : $attrs['class'];
295 $custom_class = (string) $this->getSetting( 'class_name', true );
296 $show_encoded_check = (string) $this->getSetting( 'show_encoded_check', true );
297
298 // set user-defined class
299 if ( $custom_class !== '' && strpos( $class_ori, $custom_class ) === false ) {
300 $attrs['class'] = ( empty( $attrs['class'] ) ) ? $custom_class : $attrs['class'] . ' ' . $custom_class;
301 }
302
303 // check title for email address
304 if ( ! empty( $attrs['title'] ) ) {
305 $attrs['title'] = antispambot( $attrs['title'] );
306 }
307
308 // set ignore to data-attribute to prevent being processed by WPEL plugin
309 $attrs['data-wpel-link'] = 'ignore';
310
311 // create element code
312 $link = '<a ';
313
314 foreach ( $attrs as $key => $value ) {
315 if ( strtolower( $key ) === 'href' ) {
316 $link .= $key . '="' . antispambot( $value ) . '" ';
317 } else {
318 $link .= esc_attr( $key ) . '="' . esc_attr( $value ) . '" ';
319 }
320 }
321
322 // remove last space
323 $link = substr( $link, 0, -1 );
324
325 $link .= '>';
326
327 $link .= $this->get_protected_display( $display, $protection_method );
328
329 $link .= '</a>';
330
331 // filter
332 $link = apply_filters( 'eeb_custom_href', $link, $display, $email, $attrs );
333
334 // mark link as successfullly encoded (for admin users)
335 if ( current_user_can( $this->getAdminCap( 'frontend-display-security-check' ) ) && $show_encoded_check ) {
336 $link .= $this->get_encoded_email_icon( 'Custom attribute encoded successfully!' );
337 }
338
339
340 return $link;
341 }
342
343 /**
344 * Create protected display combining these 3 methods:
345 * - reversing string
346 * - adding no-display spans with dummy values
347 * - using the wp antispambot function
348 *
349 * @param string|array<string> $display
350 * @param string $protection_method
351 * @return string Protected display
352 */
353 public function get_protected_display( $display, $protection_method = null )
354 {
355
356 $convert_plain_to_image = (bool) $this->getSetting( 'convert_plain_to_image', true, 'filter_body' );
357 $protection_text = (string) $this->getSetting( 'protection_text', true );
358 $raw_display = $display;
359
360 // get display out of array (result of preg callback)
361 if ( is_array( $display ) ) {
362 $display = $display[0];
363 }
364
365 if ( $convert_plain_to_image ) {
366 $display = '<img src="' . $this->generate_email_image_url( $display ) . '" />';
367 } elseif ( $protection_method !== 'without_javascript' ) {
368 $display = $this->dynamic_js_email_encoding( $display, $protection_text );
369 } else {
370 $display = $this->encode_email_css( $display );
371 }
372
373 return apply_filters( 'eeb/validate/get_protected_display', $display, $raw_display, $protection_method, $protection_text );
374 }
375
376 /**
377 * Dynamic email encoding with certain javascript methods
378 *
379 * @param string $email
380 * @param string $protection_text
381 * @return string the encoded email
382 */
383 public function dynamic_js_email_encoding( $email, $protection_text = '' )
384 {
385 $return = $email;
386 $rand = apply_filters( 'eeb/validate/random_encoding', wp_rand( 0, 2 ), $email, $protection_text );
387
388 switch ( $rand ) {
389 case 2:
390 $return = $this->encode_escape( $return, $protection_text );
391 break;
392 case 1:
393 $return = $this->encode_ascii( $return, $protection_text );
394 break;
395 default:
396 $return = $this->encode_ascii( $return, $protection_text );
397 break;
398 }
399
400 return $return;
401 }
402
403 /**
404 * @param string $display
405 * @return string
406 */
407 public function encode_email_css( $display )
408 {
409 $deactivate_rtl = (bool) $this->getSetting( 'deactivate_rtl', true, 'filter_body' );
410
411 // $this->log( 'display: ' . $display );
412 $stripped_display = wp_strip_all_tags( $display );
413 $stripped_display = html_entity_decode( $stripped_display );
414
415 $length = strlen( $stripped_display );
416 $interval = (int) ceil( min( 5, $length / 2 ) );
417 $offset = 0;
418 $dummy_data = time();
419 $protected = '';
420 $protection_classes = 'eeb';
421
422 if ( $deactivate_rtl ) {
423 $rev = $stripped_display;
424 $protection_classes .= ' eeb-nrtl';
425 } else {
426 // reverse string ( will be corrected with CSS )
427 $rev = strrev( $stripped_display );
428 $protection_classes .= ' eeb-rtl';
429 }
430
431
432 while ( $offset < $length ) {
433 $protected .= '<span class="eeb-sd">' . antispambot( substr( $rev, $offset, $interval ) ) . '</span>';
434
435 // setup dummy content
436 $protected .= '<span class="eeb-nodis">' . $dummy_data . '</span>';
437 $offset += $interval;
438 }
439
440 $protected = '<span class="' . $protection_classes . '">' . $protected . '</span>';
441
442 return $protected;
443 }
444
445
446 /**
447 * @return string
448 */
449 public function email_to_image( string $email, string $image_string_color = 'default', string $image_background_color = 'default', int $alpha_string = 0, int $alpha_fill = 127, int $font_size = 4 )
450 {
451
452 $setting_image_string_color = (string) $this->getSetting( 'image_color', true, 'image_settings' );
453 $setting_image_background_color = (string) $this->getSetting( 'image_background_color', true, 'image_settings' );
454 $image_text_opacity = (int) $this->getSetting( 'image_text_opacity', true, 'image_settings' );
455 $image_background_opacity = (int) $this->getSetting( 'image_background_opacity', true, 'image_settings' );
456 $image_font_size = (int) $this->getSetting( 'image_font_size', true, 'image_settings' );
457 $border_height = (int) $this->getSetting( 'image_underline', true, 'image_settings' );
458 $border_padding = 0;
459 $border_offset = 2;
460
461 if ( $image_background_color === 'default' ) {
462 $image_background_color = $setting_image_background_color;
463 } else {
464 $image_background_color = '0,0,0';
465 }
466
467 $colors = explode( ',', $image_background_color );
468 $bg_red = max( 0, min( 255, (int) $colors[0] ) );
469 $bg_green = max( 0, min( 255, (int) $colors[1] ) );
470 $bg_blue = max( 0, min( 255, (int) $colors[2] ) );
471
472 if ( $image_string_color === 'default' ) {
473 $image_string_color = $setting_image_string_color;
474 } else {
475 $image_string_color = '0,0,0';
476 }
477
478 $colors = explode( ',', $image_string_color );
479 $string_red = max( 0, min( 255, (int) $colors[0] ) );
480 $string_green = max( 0, min( 255, (int) $colors[1] ) );
481 $string_blue = max( 0, min( 255, (int) $colors[2] ) );
482
483 if (
484 ! empty( $image_text_opacity )
485 && $image_text_opacity >= 0
486 && $image_text_opacity <= 127
487 ) {
488 $alpha_string = intval( $image_text_opacity );
489 }
490 $alpha_string = max( 0, min( 127, $alpha_string ) );
491
492 if (
493 ! empty( $image_background_opacity )
494 && $image_background_opacity >= 0
495 && $image_background_opacity <= 127
496 ) {
497 $alpha_fill = intval( $image_background_opacity );
498 }
499 $alpha_fill = max( 0, min( 127, $alpha_fill ) );
500
501 if ( ! empty( $image_font_size ) && $image_font_size >= 1 && $image_font_size <= 5 ) {
502 $font_size = intval( $image_font_size );
503 }
504
505 $img_width = max( 1, imagefontwidth( $font_size ) * strlen( $email ) );
506 $img_height = imagefontheight( $font_size );
507
508 if ( ! empty( $border_height ) ) {
509 $img_real_height = max( 1, $img_height + $border_offset + $border_height );
510 } else {
511 $img_real_height = max( 1, $img_height );
512 }
513
514 $img = imagecreatetruecolor( $img_width, $img_real_height );
515 imagesavealpha( $img, true );
516 imagefill( $img, 0, 0, max( 0, imagecolorallocatealpha($img, $bg_red, $bg_green, $bg_blue, $alpha_fill ) ) );
517 imagestring(
518 $img,
519 $font_size,
520 0,
521 0,
522 $email,
523 max( 0, imagecolorallocatealpha( $img, $string_red, $string_green, $string_blue, $alpha_string ) )
524 );
525
526
527 if ( ! empty( $border_height ) ) {
528 $border_fill = imagecolorallocatealpha ($img, $string_red, $string_green, $string_blue, $alpha_string );
529 imagefilledrectangle(
530 $img,
531 0,
532 $border_offset + $img_height + $border_height - 1,
533 $border_padding + $img_width,
534 $border_offset + $img_height,
535 max( 0, $border_fill )
536 );
537 }
538
539 ob_start();
540 imagepng( $img );
541 imagedestroy( $img );
542
543 return (string) ob_get_clean();
544 }
545
546
547 /**
548 * @param string $email
549 * @param string $secret
550 * @return string|bool
551 */
552 public function generate_email_signature( string $email, string $secret )
553 {
554
555 if ( ! $secret ) {
556 return false;
557 }
558
559 $hash_signature = apply_filters( 'eeb/validate/email_signature', 'sha256', $email );
560
561 return base64_encode( hash_hmac( $hash_signature, $email, $secret, true ) );
562 }
563
564 /**
565 * @param string $email
566 * @return string|bool
567 */
568 public function generate_email_image_url( ?string $email )
569 {
570 if ( ! function_exists( 'imagefontwidth' ) || empty( $email ) || ! is_email( $email ) ) {
571 return false;
572 }
573
574 $secret = $this->settings()->get_email_image_secret();
575 $signature = (string) $this->generate_email_signature( $email, $secret );
576 $url = home_url();
577 $url .= '?eeb_mail=' . urlencode( base64_encode( $email ) );
578 $url .= '&eeb_hash=' . urlencode( $signature );
579
580 $url = apply_filters( 'eeb/validate/generate_email_image_url', $url, $email );
581
582 return $url;
583 }
584
585 }
586