PluginProbe ʕ •ᴥ•ʔ
Email Encoder – Protect Email Addresses and Phone Numbers / 2.4.2
Email Encoder – Protect Email Addresses and Phone Numbers v2.4.2
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 / Filters.php
email-encoder-bundle / src / Validate Last commit date
EncoderForm.php 5 months ago Encoding.php 5 months ago Filters.php 5 months ago Validate.php 5 months ago
Filters.php
476 lines
1 <?php
2
3 namespace OnlineOptimisation\EmailEncoderBundle\Validate;
4
5 use DOMDocument;
6 use OnlineOptimisation\EmailEncoderBundle\Traits\PluginHelper;
7
8 class Filters
9 {
10 use PluginHelper;
11
12 public function boot(): void
13 {
14 }
15
16
17 /**
18 * ######################
19 * ###
20 * #### FILTERS
21 * ###
22 * ######################
23 */
24
25 /**
26 * The main page filter function
27 *
28 * @param string $content - the content that needs to be filtered
29 * @param string $protect_using
30 * @return string - The filtered content
31 */
32 public function filter_page( $content, $protect_using )
33 {
34
35 //Added in 2.0.6
36 $content = apply_filters( 'eeb/validate/filter_page_content', $content, $protect_using );
37
38 $content = $this->filter_soft_dom_attributes( $content, 'char_encode' );
39
40 $htmlSplit = preg_split( '/(<body(([^>]*)>))/is', $content, -1, PREG_SPLIT_DELIM_CAPTURE );
41
42 if ( count( $htmlSplit ) < 4 ) {
43 return $content;
44 }
45
46 switch ( $protect_using ) {
47 case 'with_javascript':
48 case 'without_javascript':
49 case 'char_encode':
50 $head_encoding_method = 'char_encode';
51 break;
52 default:
53 $head_encoding_method = 'default';
54 break;
55 }
56
57 //Filter head area
58 $filtered_head = $this->filter_plain_emails( $htmlSplit[0], null, $head_encoding_method );
59
60 //Filter body
61 //Soft attributes always need to be protected using only the char encode method since otherwise the logic breaks
62 $filtered_body = $this->filter_soft_attributes( $htmlSplit[4], 'char_encode' );
63 $filtered_body = $this->filter_content( $filtered_body, $protect_using );
64
65 $filtered_content = $filtered_head . $htmlSplit[1] . $filtered_body;
66
67 //Revalidate filtered emails that should not bbe encoded
68 $filtered_content = $this->tempEncodeAtSymbol( $filtered_content, true );
69
70 return $filtered_content;
71 }
72
73 /**
74 * Filter content
75 *
76 * @param string $content
77 * @param string $protect_using
78 * @return string
79 */
80 public function filter_content( $content, $protect_using )
81 {
82 $filtered = $content;
83 $self = $this;
84 $encode_mailtos = (bool) $this->getSetting( 'encode_mailtos', true, 'filter_body' );
85 $convert_plain_to_image = (bool) $this->getSetting( 'convert_plain_to_image', true, 'filter_body' );
86
87 //Added in 2.0.6
88 $filtered = apply_filters( 'eeb/validate/filter_content_content', $filtered, $protect_using );
89
90 //Soft attributes always need to be protected using only the char encode method since otherwise the logic breaks
91 $filtered = $this->filter_soft_attributes( $filtered, 'char_encode' );
92
93 switch ( $protect_using ) {
94 case 'char_encode':
95 $filtered = $this->filter_plain_emails( $filtered, null, 'char_encode' );
96 break;
97 case 'strong_method':
98 $filtered = $this->filter_plain_emails( $filtered );
99 break;
100 case 'without_javascript':
101 $filtered = $this->filter_input_fields( $filtered, $protect_using );
102 $filtered = $this->filter_mailto_links( $filtered, 'without_javascript' );
103 $filtered = $this->filter_custom_links( $filtered, 'without_javascript' );
104
105 if ( $convert_plain_to_image ) {
106 $replace_by = 'convert_image';
107 } else {
108 $replace_by = 'use_css';
109 }
110
111 if ( $encode_mailtos ) {
112 if ( ! ( function_exists( 'et_fb_enabled' ) && et_fb_enabled() ) ) {
113 $filtered = $this->filter_plain_emails( $filtered, function ( $match ) use ( $self ) {
114 return $self->createProtectedMailto( $match[0], array( 'href' => 'mailto:' . $match[0] ), 'without_javascript' );
115 }, $replace_by);
116 } else {
117 $filtered = $this->filter_plain_emails( $filtered, null, $replace_by );
118 }
119 } else {
120 $filtered = $this->filter_plain_emails( $filtered, null, $replace_by );
121 }
122
123 break;
124 case 'with_javascript':
125 $filtered = $this->filter_input_fields( $filtered, $protect_using );
126 $filtered = $this->filter_mailto_links( $filtered );
127 $filtered = $this->filter_custom_links( $filtered );
128
129 if ( $convert_plain_to_image ) {
130 $replace_by = 'convert_image';
131 } else {
132 $replace_by = 'use_javascript';
133 }
134
135 if ( $encode_mailtos ) {
136 if ( ! ( function_exists( 'et_fb_enabled' ) && et_fb_enabled() ) ) {
137 $filtered = $this->filter_plain_emails( $filtered, function ( $match ) use ( $self ) {
138 return $self->createProtectedMailto( $match[0], array( 'href' => 'mailto:' . $match[0] ), 'with_javascript' );
139 }, $replace_by);
140 } else {
141 $filtered = $this->filter_plain_emails( $filtered, null, $replace_by );
142 }
143 } else {
144 $filtered = $this->filter_plain_emails( $filtered, null, $replace_by );
145 }
146
147 break;
148 }
149
150 //Revalidate filtered emails that should not be encoded
151 $filtered = $this->tempEncodeAtSymbol( $filtered, true );
152
153 return $filtered;
154 }
155
156 /**
157 * Emails will be replaced by '*protected email*'
158 * @param string $content
159 * @param string|callable $replace_by Optional
160 * @param string $protection_method Optional
161 * @param mixed $show_encoded_check Optional
162 * @return string
163 */
164 public function filter_plain_emails( $content, $replace_by = null, $protection_method = 'default', $show_encoded_check = 'default' )
165 {
166
167 if ( $show_encoded_check === 'default' ) {
168 $show_encoded_check = (bool) $this->getSetting( 'show_encoded_check', true );
169 }
170
171 if ( $replace_by === null ) {
172 $replace_by = __( $this->getSetting( 'protection_text', true ), 'email-encoder-bundle' );
173 }
174
175 $self = $this;
176
177 return preg_replace_callback( $this->settings()->get_email_regex(), function ( $matches ) use ( $replace_by, $protection_method, $show_encoded_check, $self ) {
178 // workaround to skip responsive image names containing @
179 $extention = strtolower( $matches[4] );
180 $excludedList = array(
181 '.jpg',
182 '.jpeg',
183 '.png',
184 '.gif',
185 '.svg',
186 '.webp',
187 '.bmp',
188 '.tiff',
189 '.avif',
190 );
191
192 //Added in 2.1.1
193 $excludedList = apply_filters( 'eeb/validate/excluded_image_urls', $excludedList );
194
195 if ( in_array( $extention, $excludedList ) ) {
196 return $matches[0];
197 }
198
199 if ( is_callable( $replace_by ) ) {
200 return call_user_func( $replace_by, $matches, $protection_method );
201 }
202
203 if ( $protection_method === 'char_encode' ) {
204 $protected_return = antispambot( $matches[0] );
205 } elseif ( $protection_method === 'convert_image' ) {
206
207 $image_link = $self->generateEmailImageUrl( $matches[0] );
208 if ( ! empty( $image_link ) ) {
209 $protected_return = '<img src="' . $image_link . '" />';
210 } else {
211 $protected_return = antispambot( $matches[0] );
212 }
213
214 } elseif ( $protection_method === 'use_javascript' ) {
215 $protection_text = __( $this->getSetting( 'protection_text', true ), 'email-encoder-bundle' );
216 $protected_return = $this->dynamicJsEmailEncoding( $matches[0], $protection_text );
217 } elseif ( $protection_method === 'use_css' ) {
218 $protection_text = __( $this->getSetting( 'protection_text', true ), 'email-encoder-bundle' );
219 // $protected_return = $this->validate()->encoding->encode_email_css( $matches[0], $protection_text );
220 $protected_return = $this->validate()->encoding->encode_email_css( $matches[0] );
221 } elseif ( $protection_method === 'no_encoding' ) {
222 $protected_return = $matches[0];
223 } else {
224 $protected_return = $replace_by;
225 }
226
227 // mark link as successfully encoded (for admin users)
228 if ( current_user_can( $this->getAdminCap( 'frontend-display-security-check' ) ) && $show_encoded_check ) {
229 $protected_return .= $this->getEncodedEmailIcon();
230 }
231
232 return $protected_return;
233 }, $content );
234 }
235
236 /**
237 * Filter passed input fields
238 *
239 * @param string $content
240 * @return string
241 */
242 public function filter_input_fields( $content, $encoding_method = 'default' )
243 {
244 $strong_encoding = (bool) $this->getSetting( 'input_strong_protection', true, 'filter_body' );
245
246 $callback_encode_input_fields = function ( $match ) use ( $encoding_method, $strong_encoding ) {
247 $input = $match[0];
248 $email = $match[2];
249
250 //Only allow strong encoding if javascript is supported
251 if ( $encoding_method === 'without_javascript' ) {
252 $strong_encoding = false;
253 }
254
255 return $this->validate()->encoding->encode_input_field( $input, $email, $strong_encoding );
256 };
257
258 $regexpInputField = '/<input([^>]*)value=["\'][\s+]*' . $this->settings()->get_email_regex( true ) . '[\s+]*["\']([^>]*)>/is';
259
260 return preg_replace_callback( $regexpInputField, $callback_encode_input_fields, $content );
261 }
262
263 /**
264 * @param string $content
265 * @return string
266 */
267 public function filter_mailto_links( $content, $protection_method = null )
268 {
269 $self = $this;
270
271 $callbackEncodeMailtoLinks = function ( $match ) use ( $self, $protection_method ) {
272 $attrs = $this->helper()->parse_html_attributes( $match[1] );
273 return $self->createProtectedMailto( $match[4], $attrs, $protection_method );
274 };
275
276 $regexpMailtoLink = '/<a[\s+]*(([^>]*)href=["\']mailto\:([^>]*)["\' ])>(.*?)<\/a[\s+]*>/is';
277
278 return preg_replace_callback( $regexpMailtoLink, $callbackEncodeMailtoLinks, $content );
279 }
280
281 /**
282 * @param string $content
283 * @return string
284 */
285 public function filter_custom_links( $content, $protection_method = null )
286 {
287 $self = $this;
288 $custom_href_attr = (string) $this->getSetting( 'custom_href_attr', true );
289
290 if ( ! empty( $custom_href_attr ) ) {
291 $custom_attr_list = explode( ',', $custom_href_attr );
292 foreach ( $custom_attr_list as $s_attr ) {
293 $attr_name = trim( $s_attr );
294
295 $callbackEncodeCustomLinks = function ( $match ) use ( $self, $protection_method ) {
296 $attrs = shortcode_parse_atts( $match[1] );
297 return $self->createProtectedHrefAtt( $match[4], $attrs, $protection_method );
298 };
299
300 $regexpMailtoLink = '/<a[\s+]*(([^>]*)href=["\']' . addslashes( $attr_name ) . '\:([^>]*)["\' ])>(.*?)<\/a[\s+]*>/is';
301
302 $content = preg_replace_callback( $regexpMailtoLink, $callbackEncodeCustomLinks, $content );
303 }
304 }
305
306 return $content;
307 }
308
309 /**
310 * Emails will be replaced by '*protected email*'
311 *
312 * @param string $content
313 * @return string
314 */
315 public function filter_rss( $content, $protection_type )
316 {
317
318 if ( $protection_type === 'strong_method' ) {
319 $filtered = $this->filter_plain_emails( $content );
320 } else {
321 $filtered = $this->filter_plain_emails( $content, null, 'char_encode' );
322 }
323
324 return $filtered;
325 }
326
327 /**
328 * Filter plain emails using soft attributes
329 *
330 * @param string $content - the content that should be soft filtered
331 * @param string $protection_method - The method (E.g. char_encode)
332 * @return string
333 */
334 public function filter_soft_attributes( $content, $protection_method )
335 {
336 $soft_attributes = (array) $this->settings()->get_soft_attribute_regex();
337
338 foreach ( $soft_attributes as $ident => $regex ) {
339
340 $attributes = array();
341 preg_match_all( $regex, $content, $attributes );
342
343 if ( is_array( $attributes ) && isset( $attributes[0] ) ) {
344 foreach ( $attributes[0] as $single ) {
345
346 if ( empty( $single ) ) {
347 continue;
348 }
349
350 $content = str_replace( $single, $this->filter_plain_emails( $single, null, $protection_method, false ), $content );
351 }
352 }
353
354 }
355
356 return $content;
357 }
358
359 /**
360 * Filter plain emails using soft dom attributes
361 *
362 * @param string $content - the content that should be soft filtered
363 * @param string $protection_method - The method (E.g. char_encode)
364 * @return string
365 */
366 public function filter_soft_dom_attributes( $content, $protection_method )
367 {
368
369 $no_script_tags = (bool) $this->getSetting( 'no_script_tags', true, 'filter_body' );
370 $no_attribute_validation = (bool) $this->getSetting( 'no_attribute_validation', true, 'filter_body' );
371
372 if ( ! empty( $content ) && is_string( $content ) ) {
373
374 if ( class_exists( 'DOMDocument' ) ) {
375 $dom = new DOMDocument();
376 @$dom->loadHTML($content);
377
378 //Filter html attributes
379 if ( ! $no_attribute_validation ) {
380 $allNodes = $dom->getElementsByTagName('*');
381 foreach ( $allNodes as $snote ) {
382 if ( $snote->hasAttributes() ) {
383 foreach ( $snote->attributes as $attr ) {
384 if ( $attr->nodeName == 'href' || $attr->nodeName == 'src' ) {
385 continue;
386 }
387
388 if ( strpos( $attr->nodeValue, '@' ) !== false ) {
389 $single_tags = array();
390 preg_match_all( '/' . $attr->nodeName . '=["\']([^"]*)["\']/i', $content, $single_tags );
391
392 if ( is_array( $single_tags ) && isset( $single_tags[0] ) ) {
393 foreach ( $single_tags[0] as $single ) {
394
395 if ( empty( $single ) ) {
396 continue;
397 }
398
399 $content = str_replace( $single, $this->filter_plain_emails( $single, null, $protection_method, false ), $content );
400 }
401 }
402
403 }
404 }
405 }
406 }
407 }
408
409 //Keep for now
410 //Soft-encode scripts
411 // $script = $dom->getElementsByTagName('script');
412 // if ( ! empty( $script ) ) {
413 // $scripts_encoded = true;
414
415 // if ( ! $no_script_tags ) {
416 // foreach( $script as $item ) {
417 // $content = str_replace( $item->nodeValue, $this->filter_plain_emails( $item->nodeValue, null, $protection_method, false ), $content );
418 // }
419 // } else {
420 // foreach( $script as $item ) {
421 // $content = str_replace( $item->nodeValue, $this->temp_encode_at_symbol( $item->nodeValue ), $content );
422 // }
423 // }
424 // }
425
426 }
427
428 //Validate script tags for better encoding
429 $pattern = '/<script\b[^>]*>(.*?)<\/script>/is';
430
431 preg_match_all($pattern, $content, $matches);
432 if (
433 isset( $matches[1] )
434 && ! empty( $matches[1] )
435 ) {
436 if ( ! $no_script_tags ) {
437 foreach ( $matches[1] as $key => $item ) {
438
439 //Don't do anything if something doesn't add up
440 if ( ! isset( $matches[0][ $key ] ) ) {
441 continue;
442 }
443
444 $org_script = $matches[0][ $key ];
445
446 //Only encode emails when a CDATA is given to not cause any break within the scripts
447 if ( strpos( $item, '<![CDATA' ) !== false ) {
448 $validated_script = str_replace( $item, $this->filter_plain_emails( $item, null, $protection_method, false ), $org_script );
449 } else {
450 $validated_script = str_replace( $item, $this->tempEncodeAtSymbol( $item ), $org_script );
451 }
452
453 $content = str_replace( $org_script, $validated_script, $content );
454 }
455 } else {
456 foreach ( $matches[1] as $key => $item ) {
457
458 //Don't do anything if something doesn't add up
459 if ( ! isset( $matches[0][ $key ] ) ) {
460 continue;
461 }
462
463 $org_script = $matches[0][ $key ];
464 $validated_script = str_replace( $item, $this->tempEncodeAtSymbol( $item ), $org_script );
465
466 $content = str_replace( $org_script, $validated_script, $content );
467 }
468 }
469 }
470
471 }
472
473 return $content;
474 }
475 }
476