PluginProbe ʕ •ᴥ•ʔ
Email Encoder – Protect Email Addresses and Phone Numbers / 2.4.3
Email Encoder – Protect Email Addresses and Phone Numbers v2.4.3
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 4 months ago Encoding.php 4 months ago Filters.php 4 months ago Validate.php 4 months ago
Filters.php
477 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 ( ! is_array( $htmlSplit) || 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( string $content, string $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 * @param string $protection_method
266 * @return string
267 */
268 public function filter_mailto_links( string $content, ?string $protection_method = null )
269 {
270 $self = $this;
271
272 $callbackEncodeMailtoLinks = function ( $match ) use ( $self, $protection_method ) {
273 $attrs = $this->helper()->parse_html_attributes( $match[1] );
274 return $self->createProtectedMailto( $match[4], $attrs, $protection_method );
275 };
276
277 $regexpMailtoLink = '/<a[\s+]*(([^>]*)href=["\']mailto\:([^>]*)["\' ])>(.*?)<\/a[\s+]*>/is';
278
279 return preg_replace_callback( $regexpMailtoLink, $callbackEncodeMailtoLinks, $content ) ?? '';
280 }
281
282 /**
283 * @param string $content
284 * @param string $protection_method
285 * @return string
286 */
287 public function filter_custom_links( string $content, ?string $protection_method = null )
288 {
289 $self = $this;
290 $custom_href_attr = (string) $this->getSetting( 'custom_href_attr', true );
291
292 if ( ! empty( $custom_href_attr ) ) {
293 $custom_attr_list = explode( ',', $custom_href_attr );
294 foreach ( $custom_attr_list as $s_attr ) {
295 $attr_name = trim( $s_attr );
296
297 $callbackEncodeCustomLinks = function ( $match ) use ( $self, $protection_method ) {
298 $attrs = shortcode_parse_atts( $match[1] );
299 return $self->createProtectedHrefAtt( $match[4], $attrs, $protection_method );
300 };
301
302 $regexpMailtoLink = '/<a[\s+]*(([^>]*)href=["\']' . addslashes( $attr_name ) . '\:([^>]*)["\' ])>(.*?)<\/a[\s+]*>/is';
303
304 $content = preg_replace_callback( $regexpMailtoLink, $callbackEncodeCustomLinks, $content ) ?? '';
305 }
306 }
307
308 return $content;
309 }
310
311 /**
312 * Emails will be replaced by '*protected email*'
313 *
314 * @param string $content
315 * @param string $protection_type
316 * @return string
317 */
318 public function filter_rss( string $content, ?string $protection_type )
319 {
320
321 if ( $protection_type === 'strong_method' ) {
322 $filtered = $this->filter_plain_emails( $content );
323 } else {
324 $filtered = $this->filter_plain_emails( $content, null, 'char_encode' );
325 }
326
327 return $filtered;
328 }
329
330 /**
331 * Filter plain emails using soft attributes
332 *
333 * @param string $content - the content that should be soft filtered
334 * @param string $protection_method - The method (E.g. char_encode)
335 * @return string
336 */
337 public function filter_soft_attributes( string $content, string $protection_method )
338 {
339 $soft_attributes = (array) $this->settings()->get_soft_attribute_regex();
340
341 foreach ( $soft_attributes as $ident => $regex ) {
342
343 // $attributes = array();
344 preg_match_all( $regex, $content, $attributes );
345
346 // if ( is_array( $attributes ) && isset( $attributes[0] ) ) {
347 foreach ( $attributes[0] as $single ) {
348
349 if ( empty( $single ) ) {
350 continue;
351 }
352
353 $content = str_replace( $single, $this->filter_plain_emails( $single, null, $protection_method, false ), $content );
354 }
355 // }
356
357 }
358
359 return $content;
360 }
361
362 /**
363 * Filter plain emails using soft dom attributes
364 *
365 * @param string $content - the content that should be soft filtered
366 * @param string $protection_method - The method (E.g. char_encode)
367 * @return string
368 */
369 public function filter_soft_dom_attributes( $content, $protection_method )
370 {
371 $content = (string) $content;
372
373 $no_script_tags = (bool) $this->getSetting( 'no_script_tags', true, 'filter_body' );
374 $no_attribute_validation = (bool) $this->getSetting( 'no_attribute_validation', true, 'filter_body' );
375
376 if ( $content !== '' ) {
377
378 if ( class_exists( 'DOMDocument' ) ) {
379 $dom = new DOMDocument();
380 @$dom->loadHTML($content);
381
382 //Filter html attributes
383 if ( ! $no_attribute_validation ) {
384 $allNodes = $dom->getElementsByTagName('*');
385 foreach ( $allNodes as $snote ) {
386 if ( $snote->hasAttributes() ) {
387 foreach ( $snote->attributes as $attr ) {
388 if ( $attr->nodeName == 'href' || $attr->nodeName == 'src' ) {
389 continue;
390 }
391
392 if ( $attr->nodeValue !== null && strpos( $attr->nodeValue, '@' ) !== false ) {
393 // $single_tags = array();
394 preg_match_all( '/' . $attr->nodeName . '=["\']([^"]*)["\']/i', $content, $single_tags );
395
396 // if ( is_array( $single_tags ) && isset( $single_tags[0] ) ) {
397 foreach ( $single_tags[0] as $single ) {
398
399 if ( empty( $single ) ) {
400 continue;
401 }
402
403 $content = str_replace( $single, $this->filter_plain_emails( $single, null, $protection_method, false ), $content );
404 }
405 // }
406
407 }
408 }
409 }
410 }
411 }
412
413 //Keep for now
414 //Soft-encode scripts
415 // $script = $dom->getElementsByTagName('script');
416 // if ( ! empty( $script ) ) {
417 // $scripts_encoded = true;
418
419 // if ( ! $no_script_tags ) {
420 // foreach( $script as $item ) {
421 // $content = str_replace( $item->nodeValue, $this->filter_plain_emails( $item->nodeValue, null, $protection_method, false ), $content );
422 // }
423 // } else {
424 // foreach( $script as $item ) {
425 // $content = str_replace( $item->nodeValue, $this->temp_encode_at_symbol( $item->nodeValue ), $content );
426 // }
427 // }
428 // }
429
430 }
431
432 //Validate script tags for better encoding
433 $pattern = '/<script\b[^>]*>(.*?)<\/script>/is';
434
435 preg_match_all($pattern, $content, $matches);
436 if ( ! empty( $matches[1] ) ) {
437 if ( ! $no_script_tags ) {
438 foreach ( $matches[1] as $key => $item ) {
439
440 //Don't do anything if something doesn't add up
441 if ( ! isset( $matches[0][ $key ] ) ) {
442 continue;
443 }
444
445 $org_script = $matches[0][ $key ];
446
447 //Only encode emails when a CDATA is given to not cause any break within the scripts
448 if ( strpos( $item, '<![CDATA' ) !== false ) {
449 $validated_script = str_replace( $item, $this->filter_plain_emails( $item, null, $protection_method, false ), $org_script );
450 } else {
451 $validated_script = str_replace( $item, $this->tempEncodeAtSymbol( $item ), $org_script );
452 }
453
454 $content = str_replace( $org_script, $validated_script, $content );
455 }
456 } else {
457 foreach ( $matches[1] as $key => $item ) {
458
459 //Don't do anything if something doesn't add up
460 if ( ! isset( $matches[0][ $key ] ) ) {
461 continue;
462 }
463
464 $org_script = $matches[0][ $key ];
465 $validated_script = str_replace( $item, $this->tempEncodeAtSymbol( $item ), $org_script );
466
467 $content = str_replace( $org_script, $validated_script, $content );
468 }
469 }
470 }
471
472 }
473
474 return $content;
475 }
476 }
477