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