PluginProbe ʕ •ᴥ•ʔ
Contact Form 7 / 6.1.6
Contact Form 7 v6.1.6
6.1.6 5.0.2 5.0.3 5.0.4 5.0.5 5.1 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.1.8 5.1.9 5.2 5.2.1 5.2.2 5.3 5.3.1 5.3.2 5.4 5.4.1 5.4.2 5.5 5.5.1 5.5.2 5.5.3 5.5.4 5.5.5 5.5.6 5.5.6.1 5.6 5.6.1 5.6.2 5.6.3 5.6.4 5.7 5.7.1 5.7.2 5.7.3 5.7.4 5.7.5 5.7.5.1 5.7.6 5.7.7 5.8 5.8.1 5.8.2 5.8.3 5.8.4 5.8.5 5.8.6 5.8.7 5.9 5.9.2 5.9.3 5.9.4 5.9.5 5.9.6 5.9.7 5.9.8 6.0 6.0.1 6.0.2 6.0.3 6.0.4 6.0.5 6.0.6 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 trunk 1.1 1.10 1.10.0.1 1.10.1 1.2 1.3 1.3.1 1.3.2 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.5 1.6 1.6.1 1.7 1.7.1 1.7.2 1.7.4 1.7.5 1.7.6 1.7.6.1 1.7.7 1.7.7.1 1.7.8 1.8 1.8.0.1 1.8.0.2 1.8.0.3 1.8.0.4 1.8.1 1.8.1.1 1.9 1.9.1 1.9.2 1.9.2.1 1.9.2.2 1.9.3 1.9.4 1.9.5 1.9.5.1 2.0 2.0-beta 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.1 2.1.1 2.1.2 2.2 2.2.1 2.3 2.3.1 2.4 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 3.0 3.0-beta 3.0.1 3.0.2 3.0.2.1 3.1 3.1.1 3.1.2 3.2 3.2.1 3.3 3.3.1 3.3.2 3.3.3 3.4 3.4.1 3.4.2 3.5 3.5.1 3.5.2 3.5.3 3.5.4 3.6 3.7 3.7.1 3.7.2 3.8 3.8.1 3.9 3.9-beta 3.9.1 3.9.2 3.9.3 4.0 4.0.1 4.0.2 4.0.3 4.1 4.1-beta 4.1.1 4.1.2 4.2 4.2-beta 4.2.1 4.2.2 4.3 4.3.1 4.4 4.4.1 4.4.2 4.5 4.5.1 4.6 4.6.1 4.7 4.8 4.8.1 4.9 4.9.1 4.9.2 5.0 5.0.1
contact-form-7 / includes / formatting.php
contact-form-7 / includes Last commit date
block-editor 1 year ago config-validator 11 months ago css 2 weeks ago js 1 year ago swv 9 months ago capabilities.php 7 years ago contact-form-functions.php 11 months ago contact-form-template.php 11 months ago contact-form.php 11 months ago controller.php 11 months ago file.php 9 months ago filesystem.php 11 months ago form-tag.php 7 months ago form-tags-manager.php 11 months ago formatting.php 11 months ago functions.php 9 months ago html-formatter.php 9 months ago integration.php 11 months ago l10n.php 11 months ago mail-tag.php 11 months ago mail.php 3 months ago pipe.php 11 months ago pocket-holder.php 3 years ago rest-api.php 11 months ago shortcodes.php 11 months ago special-mail-tags.php 9 months ago submission.php 9 months ago upgrade.php 11 months ago validation-functions.php 11 months ago validation.php 11 months ago
formatting.php
654 lines
1 <?php
2
3 /**
4 * Replaces double line breaks with paragraph elements.
5 *
6 * @param string $input The text which has to be formatted.
7 * @param bool $br Optional. If set, this will convert all remaining
8 * line breaks after paragraphing. Default true.
9 * @return string Text which has been converted into correct paragraph tags.
10 */
11 function wpcf7_autop( $input, $br = true ) {
12 $placeholders = array();
13
14 // Replace non-HTML embedded elements with placeholders.
15 $input = preg_replace_callback(
16 '/<(math|svg).*?<\/\1>/is',
17 static function ( $matches ) use ( &$placeholders ) {
18 $placeholder = sprintf(
19 '<%1$s id="%2$s" />',
20 WPCF7_HTMLFormatter::placeholder_inline,
21 hash( 'sha256', $matches[0] )
22 );
23
24 list( $placeholder ) =
25 WPCF7_HTMLFormatter::normalize_start_tag( $placeholder );
26
27 $placeholders[$placeholder] = $matches[0];
28
29 return $placeholder;
30 },
31 $input
32 );
33
34 $formatter = new WPCF7_HTMLFormatter( array(
35 'auto_br' => $br,
36 'allowed_html' => null,
37 ) );
38
39 $chunks = $formatter->separate_into_chunks( $input );
40
41 $output = $formatter->format( $chunks );
42
43 // Restore from placeholders.
44 $output = str_replace(
45 array_keys( $placeholders ),
46 array_values( $placeholders ),
47 $output
48 );
49
50 return $output;
51 }
52
53
54 /**
55 * Newline preservation help function for wpcf7_autop().
56 *
57 * @deprecated 5.7 Unnecessary to use any more.
58 *
59 * @param array $matches preg_replace_callback() matches array.
60 * @return string Text including newline placeholders.
61 */
62 function wpcf7_autop_preserve_newline_callback( $matches ) {
63 return str_replace( "\n", '<WPPreserveNewline />', $matches[0] );
64 }
65
66
67 /**
68 * Sanitizes the query variables.
69 *
70 * @param string $text Query variable.
71 * @return string Text sanitized.
72 */
73 function wpcf7_sanitize_query_var( $text ) {
74 $text = wp_unslash( $text );
75 $text = wp_check_invalid_utf8( $text );
76
77 if ( false !== strpos( $text, '<' ) ) {
78 $text = wp_pre_kses_less_than( $text );
79 $text = wp_strip_all_tags( $text );
80 }
81
82 $text = preg_replace( '/%[a-f0-9]{2}/i', '', $text );
83 $text = preg_replace( '/ +/', ' ', $text );
84 $text = trim( $text, ' ' );
85
86 return $text;
87 }
88
89
90 /**
91 * Strips quote characters surrounding the input.
92 *
93 * @param string $text Input text.
94 * @return string Processed output.
95 */
96 function wpcf7_strip_quote( $text ) {
97 $text = wpcf7_strip_whitespaces( $text );
98
99 if ( preg_match( '/^"(.*)"$/s', $text, $matches ) ) {
100 $text = $matches[1];
101 } elseif ( preg_match( "/^'(.*)'$/s", $text, $matches ) ) {
102 $text = $matches[1];
103 }
104
105 return $text;
106 }
107
108
109 /**
110 * Navigates through an array, object, or scalar, and
111 * strips quote characters surrounding the each value.
112 *
113 * @param mixed $input The array or string to be processed.
114 * @return mixed Processed value.
115 */
116 function wpcf7_strip_quote_deep( $input ) {
117 if ( is_string( $input ) ) {
118 return wpcf7_strip_quote( $input );
119 }
120
121 if ( is_array( $input ) ) {
122 $result = array();
123
124 foreach ( $input as $key => $text ) {
125 $result[$key] = wpcf7_strip_quote_deep( $text );
126 }
127
128 return $result;
129 }
130 }
131
132
133 /**
134 * Normalizes newline characters.
135 *
136 * @param string $text Input text.
137 * @param string $to Optional. The newline character that is used in the output.
138 * @return string Normalized text.
139 */
140 function wpcf7_normalize_newline( $text, $to = "\n" ) {
141 if ( ! is_string( $text ) ) {
142 return $text;
143 }
144
145 $nls = array( "\r\n", "\r", "\n" );
146
147 if ( ! in_array( $to, $nls, true ) ) {
148 return $text;
149 }
150
151 return str_replace( $nls, $to, $text );
152 }
153
154
155 /**
156 * Navigates through an array, object, or scalar, and
157 * normalizes newline characters in the each value.
158 *
159 * @param mixed $input The array or string to be processed.
160 * @param string $to Optional. The newline character that is used in the output.
161 * @return mixed Processed value.
162 */
163 function wpcf7_normalize_newline_deep( $input, $to = "\n" ) {
164 if ( is_array( $input ) ) {
165 $result = array();
166
167 foreach ( $input as $key => $text ) {
168 $result[$key] = wpcf7_normalize_newline_deep( $text, $to );
169 }
170
171 return $result;
172 }
173
174 return wpcf7_normalize_newline( $input, $to );
175 }
176
177
178 /**
179 * Strips newline characters.
180 *
181 * @param string $text Input text.
182 * @return string Processed one-line text.
183 */
184 function wpcf7_strip_newline( $text ) {
185 $text = (string) $text;
186 $text = str_replace( array( "\r", "\n" ), '', $text );
187 return wpcf7_strip_whitespaces( $text );
188 }
189
190
191 /**
192 * Canonicalizes text.
193 *
194 * @param string $text Input text.
195 * @param string|array|object $options Options.
196 * @return string Canonicalized text.
197 */
198 function wpcf7_canonicalize( $text, $options = '' ) {
199 // for back-compat
200 if (
201 is_string( $options ) and
202 '' !== $options and
203 ! str_contains( $options, '=' )
204 ) {
205 $options = array(
206 'strto' => $options,
207 );
208 }
209
210 $options = wp_parse_args( $options, array(
211 'strto' => 'lower',
212 'strip_separators' => false,
213 ) );
214
215 static $charset = null;
216
217 if ( ! isset( $charset ) ) {
218 $charset = get_option( 'blog_charset' );
219
220 $is_utf8 = in_array(
221 $charset,
222 array( 'utf8', 'utf-8', 'UTF8', 'UTF-8' ),
223 true
224 );
225
226 if ( $is_utf8 ) {
227 $charset = 'UTF-8';
228 }
229 }
230
231 $text = html_entity_decode( $text, ENT_QUOTES | ENT_HTML5, $charset );
232
233 if ( function_exists( 'mb_convert_kana' ) ) {
234 $text = mb_convert_kana( $text, 'asKV', $charset );
235 }
236
237 if ( $options['strip_separators'] ) {
238 $text = preg_replace( '/[\r\n\t ]+/', '', $text );
239 } else {
240 $text = preg_replace( '/[\r\n\t ]+/', ' ', $text );
241 }
242
243 if ( 'lower' === $options['strto'] ) {
244 if ( function_exists( 'mb_strtolower' ) ) {
245 $text = mb_strtolower( $text, $charset );
246 } else {
247 $text = strtolower( $text );
248 }
249 } elseif ( 'upper' === $options['strto'] ) {
250 if ( function_exists( 'mb_strtoupper' ) ) {
251 $text = mb_strtoupper( $text, $charset );
252 } else {
253 $text = strtoupper( $text );
254 }
255 }
256
257 return wpcf7_strip_whitespaces( $text );
258 }
259
260
261 /**
262 * Returns a canonical keyword usable for a name or an ID purposes.
263 */
264 function wpcf7_canonicalize_name( $text ) {
265 return preg_replace( '/[^0-9a-z]+/i', '-', $text );
266 }
267
268
269 /**
270 * Sanitizes Contact Form 7's form unit-tag.
271 *
272 * @param string $tag Unit-tag.
273 * @return string Sanitized unit-tag.
274 */
275 function wpcf7_sanitize_unit_tag( $tag ) {
276 $tag = preg_replace( '/[^A-Za-z0-9_-]/', '', (string) $tag );
277 return $tag;
278 }
279
280
281 /**
282 * Converts a file name to one that is not executable as a script.
283 *
284 * @param string $filename File name.
285 * @return string Converted file name.
286 */
287 function wpcf7_antiscript_file_name( $filename ) {
288 $filename = wp_basename( $filename );
289
290 // Apply part of protection logic from sanitize_file_name().
291 $filename = str_replace(
292 array(
293 '?', '[', ']', '/', '\\', '=', '<', '>', ':', ';', ',', "'", '"',
294 '&', '$', '#', '*', '(', ')', '|', '~', '`', '!', '{', '}',
295 '%', '+', '', '«', '»', '', '', chr( 0 )
296 ),
297 '',
298 $filename
299 );
300
301 $filename = preg_replace( '/[\r\n\t -]+/', '-', $filename );
302 $filename = preg_replace( '/[\pC\pZ]+/iu', '', $filename );
303
304 $parts = explode( '.', $filename );
305
306 if ( count( $parts ) < 2 ) {
307 return $filename;
308 }
309
310 $script_pattern = '/^(php|phtml|pl|py|rb|cgi|asp|aspx)\d?$/i';
311
312 $filename = array_shift( $parts );
313 $extension = array_pop( $parts );
314
315 foreach ( $parts as $part ) {
316 if ( preg_match( $script_pattern, $part ) ) {
317 $filename .= '.' . $part . '_';
318 } else {
319 $filename .= '.' . $part;
320 }
321 }
322
323 if ( preg_match( $script_pattern, $extension ) ) {
324 $filename .= '.' . $extension . '_.txt';
325 } else {
326 $filename .= '.' . $extension;
327 }
328
329 return $filename;
330 }
331
332
333 /**
334 * Masks a password with asterisks (*).
335 *
336 * @param int $right Length of right-hand unmasked text. Default 0.
337 * @param int $left Length of left-hand unmasked text. Default 0.
338 * @return string Text of masked password.
339 */
340 function wpcf7_mask_password( $text, $right = 0, $left = 0 ) {
341 $length = strlen( $text );
342
343 $right = absint( $right );
344 $left = absint( $left );
345
346 if ( $length < $right + $left ) {
347 $right = $left = 0;
348 }
349
350 if ( $length <= 48 ) {
351 $masked = str_repeat( '*', $length - ( $right + $left ) );
352 } elseif ( $right + $left < 48 ) {
353 $masked = str_repeat( '*', 48 - ( $right + $left ) );
354 } else {
355 $masked = '****';
356 }
357
358 $left_unmasked = $left ? substr( $text, 0, $left ) : '';
359 $right_unmasked = $right ? substr( $text, -1 * $right ) : '';
360
361 $text = $left_unmasked . $masked . $right_unmasked;
362
363 return $text;
364 }
365
366
367 /**
368 * Returns an array of allowed HTML tags and attributes for a given context.
369 *
370 * @param string $context Context used to decide allowed tags and attributes.
371 * @return array Array of allowed HTML tags and their allowed attributes.
372 */
373 function wpcf7_kses_allowed_html( $context = 'form' ) {
374 static $allowed_tags = array();
375
376 if ( isset( $allowed_tags[$context] ) ) {
377 return apply_filters(
378 'wpcf7_kses_allowed_html',
379 $allowed_tags[$context],
380 $context
381 );
382 }
383
384 $allowed_tags[$context] = wp_kses_allowed_html( 'post' );
385
386 if ( 'form' === $context ) {
387 $additional_tags_for_form = array(
388 'button' => array(
389 'disabled' => true,
390 'name' => true,
391 'type' => true,
392 'value' => true,
393 ),
394 'datalist' => array(),
395 'fieldset' => array(
396 'disabled' => true,
397 'name' => true,
398 ),
399 'input' => array(
400 'accept' => true,
401 'alt' => true,
402 'autocomplete' => true,
403 'capture' => true,
404 'checked' => true,
405 'disabled' => true,
406 'list' => true,
407 'max' => true,
408 'maxlength' => true,
409 'min' => true,
410 'minlength' => true,
411 'multiple' => true,
412 'name' => true,
413 'pattern' => true,
414 'placeholder' => true,
415 'readonly' => true,
416 'required' => true,
417 'size' => true,
418 'step' => true,
419 'type' => true,
420 'value' => true,
421 ),
422 'label' => array(
423 'for' => true,
424 ),
425 'legend' => array(),
426 'meter' => array(
427 'value' => true,
428 'min' => true,
429 'max' => true,
430 'low' => true,
431 'high' => true,
432 'optimum' => true,
433 ),
434 'optgroup' => array(
435 'disabled' => true,
436 'label' => true,
437 ),
438 'option' => array(
439 'disabled' => true,
440 'label' => true,
441 'selected' => true,
442 'value' => true,
443 ),
444 'output' => array(
445 'for' => true,
446 'name' => true,
447 ),
448 'progress' => array(
449 'max' => true,
450 'value' => true,
451 ),
452 'select' => array(
453 'autocomplete' => true,
454 'disabled' => true,
455 'multiple' => true,
456 'name' => true,
457 'required' => true,
458 'size' => true,
459 ),
460 'textarea' => array(
461 'autocomplete' => true,
462 'cols' => true,
463 'disabled' => true,
464 'maxlength' => true,
465 'minlength' => true,
466 'name' => true,
467 'placeholder' => true,
468 'readonly' => true,
469 'required' => true,
470 'rows' => true,
471 'wrap' => true,
472 ),
473 );
474
475 $allowed_tags[$context] = array_merge(
476 $allowed_tags[$context],
477 $additional_tags_for_form
478 );
479
480 $allowed_tags[$context] = array_map(
481 static function ( $elm ) {
482 $global_attributes = array(
483 'aria-atomic' => true,
484 'aria-checked' => true,
485 'aria-controls' => true,
486 'aria-current' => true,
487 'aria-describedby' => true,
488 'aria-details' => true,
489 'aria-disabled' => true,
490 'aria-expanded' => true,
491 'aria-hidden' => true,
492 'aria-invalid' => true,
493 'aria-label' => true,
494 'aria-labelledby' => true,
495 'aria-live' => true,
496 'aria-relevant' => true,
497 'aria-required' => true,
498 'aria-selected' => true,
499 'class' => true,
500 'data-*' => true,
501 'dir' => true,
502 'hidden' => true,
503 'id' => true,
504 'inputmode' => true,
505 'lang' => true,
506 'role' => true,
507 'spellcheck' => true,
508 'style' => true,
509 'tabindex' => true,
510 'title' => true,
511 'xml:lang' => true,
512 );
513
514 return array_merge( $global_attributes, (array) $elm );
515 },
516 $allowed_tags[$context]
517 );
518 }
519
520 return apply_filters(
521 'wpcf7_kses_allowed_html',
522 $allowed_tags[$context],
523 $context
524 );
525 }
526
527
528 /**
529 * Sanitizes content for allowed HTML tags for the specified context.
530 *
531 * @param string $input Content to filter.
532 * @param string $context Context used to decide allowed tags and attributes.
533 * @return string Filtered text with allowed HTML tags and attributes intact.
534 */
535 function wpcf7_kses( $input, $context = 'form' ) {
536 $output = wp_kses(
537 $input,
538 wpcf7_kses_allowed_html( $context )
539 );
540
541 return $output;
542 }
543
544
545 /**
546 * Returns a formatted string of HTML attributes.
547 *
548 * @param array $atts Associative array of attribute name and value pairs.
549 * @return string Formatted HTML attributes.
550 */
551 function wpcf7_format_atts( $atts ) {
552 $atts_filtered = array();
553
554 foreach ( $atts as $name => $value ) {
555 $name = strtolower( trim( $name ) );
556
557 if ( ! preg_match( '/^[a-z_:][a-z_:.0-9-]*$/', $name ) ) {
558 continue;
559 }
560
561 static $boolean_attributes = array(
562 'checked',
563 'disabled',
564 'inert',
565 'multiple',
566 'readonly',
567 'required',
568 'selected',
569 );
570
571 if ( in_array( $name, $boolean_attributes, true ) and '' === $value ) {
572 $value = false;
573 }
574
575 if ( is_numeric( $value ) ) {
576 $value = (string) $value;
577 }
578
579 if ( null === $value or false === $value ) {
580 unset( $atts_filtered[$name] );
581 } elseif ( true === $value ) {
582 $atts_filtered[$name] = $name; // boolean attribute
583 } elseif ( is_string( $value ) ) {
584 $atts_filtered[$name] = trim( $value );
585 }
586 }
587
588 $output = '';
589
590 foreach ( $atts_filtered as $name => $value ) {
591 $output .= sprintf( ' %1$s="%2$s"', $name, esc_attr( $value ) );
592 }
593
594 return trim( $output );
595 }
596
597
598 /**
599 * Returns the regular expression pattern that represents
600 * whitespace characters Unicode defines.
601 *
602 * @link https://www.unicode.org/Public/UCD/latest/ucd/PropList.txt
603 *
604 * @return string Regular expression pattern.
605 */
606 function wpcf7_get_unicode_whitespaces() {
607 return '\x09-\x0D\x20\x85\xA0\x{1680}\x{2000}-\x{200A}\x{2028}\x{2029}\x{202F}\x{205F}\x{3000}';
608 }
609
610
611 /**
612 * Strips surrounding whitespaces.
613 *
614 * @link https://contactform7.com/2024/07/13/consistent-handling-policy-of-surrounding-whitespaces/
615 *
616 * @param string|array $input Input text.
617 * @param string $side The side from which whitespaces are stripped.
618 * 'start', 'end', or 'both' (default).
619 * @return string|array Output text.
620 */
621 function wpcf7_strip_whitespaces( $input, $side = 'both' ) {
622 if ( is_array( $input ) ) {
623 return array_map(
624 static function ( $i ) use ( $side ) {
625 return wpcf7_strip_whitespaces( $i, $side );
626 },
627 $input
628 );
629 }
630
631 // https://tc39.es/ecma262/multipage/ecmascript-language-lexical-grammar.html
632 $whitespaces = wpcf7_get_unicode_whitespaces() . '\x{FEFF}';
633
634 if ( 'end' !== $side ) {
635 // Strip leading whitespaces
636 $input = preg_replace(
637 sprintf( '/^[%s]+/u', $whitespaces ),
638 '',
639 $input
640 );
641 }
642
643 if ( 'start' !== $side ) {
644 // Strip trailing whitespaces
645 $input = preg_replace(
646 sprintf( '/[%s]+$/u', $whitespaces ),
647 '',
648 $input
649 );
650 }
651
652 return $input;
653 }
654