PluginProbe ʕ •ᴥ•ʔ
Contact Form 7 / 5.7.2
Contact Form 7 v5.7.2
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 / config-validator.php
contact-form-7 / includes Last commit date
block-editor 3 years ago css 3 years ago js 3 years ago swv 3 years ago capabilities.php 7 years ago config-validator.php 3 years ago contact-form-functions.php 3 years ago contact-form-template.php 3 years ago contact-form.php 3 years ago controller.php 3 years ago file.php 3 years ago form-tag.php 3 years ago form-tags-manager.php 3 years ago formatting.php 3 years ago functions.php 3 years ago html-formatter.php 3 years ago integration.php 3 years ago l10n.php 3 years ago mail.php 3 years ago pipe.php 4 years ago pocket-holder.php 3 years ago rest-api.php 4 years ago shortcodes.php 3 years ago special-mail-tags.php 3 years ago submission.php 3 years ago upgrade.php 7 years ago validation-functions.php 3 years ago validation.php 3 years ago
config-validator.php
983 lines
1 <?php
2
3 /**
4 * Configuration validator.
5 *
6 * @link https://contactform7.com/configuration-errors/
7 */
8 class WPCF7_ConfigValidator {
9
10 /**
11 * The plugin version in which important updates happened last time.
12 */
13 const last_important_update = '5.6.1';
14
15 const error = 100;
16 const error_maybe_empty = 101;
17 const error_invalid_mailbox_syntax = 102;
18 const error_email_not_in_site_domain = 103;
19 const error_html_in_message = 104;
20 const error_multiple_controls_in_label = 105;
21 const error_file_not_found = 106;
22 const error_unavailable_names = 107;
23 const error_invalid_mail_header = 108;
24 const error_deprecated_settings = 109;
25 const error_file_not_in_content_dir = 110;
26 const error_unavailable_html_elements = 111;
27 const error_attachments_overweight = 112;
28 const error_dots_in_names = 113;
29 const error_colons_in_names = 114;
30 const error_upload_filesize_overlimit = 115;
31
32
33 /**
34 * Returns a URL linking to the documentation page for the error type.
35 */
36 public static function get_doc_link( $error_code = '' ) {
37 $url = __( 'https://contactform7.com/configuration-errors/',
38 'contact-form-7'
39 );
40
41 if ( '' !== $error_code ) {
42 $error_code = strtr( $error_code, '_', '-' );
43
44 $url = sprintf( '%s/%s', untrailingslashit( $url ), $error_code );
45 }
46
47 return esc_url( $url );
48 }
49
50
51 private $contact_form;
52 private $errors = array();
53
54 public function __construct( WPCF7_ContactForm $contact_form ) {
55 $this->contact_form = $contact_form;
56 }
57
58
59 /**
60 * Returns the contact form object that is tied to this validator.
61 */
62 public function contact_form() {
63 return $this->contact_form;
64 }
65
66
67 /**
68 * Returns true if no error has been detected.
69 */
70 public function is_valid() {
71 return ! $this->count_errors();
72 }
73
74
75 /**
76 * Counts detected errors.
77 */
78 public function count_errors( $args = '' ) {
79 $args = wp_parse_args( $args, array(
80 'section' => '',
81 'code' => '',
82 ) );
83
84 $count = 0;
85
86 foreach ( $this->errors as $key => $errors ) {
87 if ( preg_match( '/^mail_[0-9]+\.(.*)$/', $key, $matches ) ) {
88 $key = sprintf( 'mail.%s', $matches[1] );
89 }
90
91 if ( $args['section']
92 and $key != $args['section']
93 and preg_replace( '/\..*$/', '', $key, 1 ) != $args['section'] ) {
94 continue;
95 }
96
97 foreach ( $errors as $error ) {
98 if ( empty( $error ) ) {
99 continue;
100 }
101
102 if ( $args['code'] and $error['code'] != $args['code'] ) {
103 continue;
104 }
105
106 $count += 1;
107 }
108 }
109
110 return $count;
111 }
112
113
114 /**
115 * Collects messages for detected errors.
116 */
117 public function collect_error_messages() {
118 $error_messages = array();
119
120 foreach ( $this->errors as $section => $errors ) {
121 $error_messages[$section] = array();
122
123 foreach ( $errors as $error ) {
124 if ( empty( $error['args']['message'] ) ) {
125 $message = $this->get_default_message( $error['code'] );
126 } elseif ( empty( $error['args']['params'] ) ) {
127 $message = $error['args']['message'];
128 } else {
129 $message = $this->build_message(
130 $error['args']['message'],
131 $error['args']['params'] );
132 }
133
134 $link = '';
135
136 if ( ! empty( $error['args']['link'] ) ) {
137 $link = $error['args']['link'];
138 }
139
140 $error_messages[$section][] = array(
141 'message' => $message,
142 'link' => esc_url( $link ),
143 );
144 }
145 }
146
147 return $error_messages;
148 }
149
150
151 /**
152 * Builds an error message by replacing placeholders.
153 */
154 public function build_message( $message, $params = '' ) {
155 $params = wp_parse_args( $params, array() );
156
157 foreach ( $params as $key => $val ) {
158 if ( ! preg_match( '/^[0-9A-Za-z_]+$/', $key ) ) { // invalid key
159 continue;
160 }
161
162 $placeholder = '%' . $key . '%';
163
164 if ( false !== stripos( $message, $placeholder ) ) {
165 $message = str_ireplace( $placeholder, $val, $message );
166 }
167 }
168
169 return $message;
170 }
171
172
173 /**
174 * Returns a default message that is used when the message for the error
175 * is not specified.
176 */
177 public function get_default_message( $code ) {
178 switch ( $code ) {
179 case self::error_maybe_empty:
180 return __( "There is a possible empty field.", 'contact-form-7' );
181 case self::error_invalid_mailbox_syntax:
182 return __( "Invalid mailbox syntax is used.", 'contact-form-7' );
183 case self::error_email_not_in_site_domain:
184 return __( "Sender email address does not belong to the site domain.", 'contact-form-7' );
185 case self::error_html_in_message:
186 return __( "HTML tags are used in a message.", 'contact-form-7' );
187 case self::error_multiple_controls_in_label:
188 return __( "Multiple form controls are in a single label element.", 'contact-form-7' );
189 case self::error_invalid_mail_header:
190 return __( "There are invalid mail header fields.", 'contact-form-7' );
191 case self::error_deprecated_settings:
192 return __( "Deprecated settings are used.", 'contact-form-7' );
193 default:
194 return '';
195 }
196 }
197
198
199 /**
200 * Adds a validation error.
201 *
202 * @param string $section The section where the error detected.
203 * @param int $code The unique code of the error.
204 * This must be one of the class constants.
205 * @param string|array $args Optional options for the error.
206 */
207 public function add_error( $section, $code, $args = '' ) {
208 $args = wp_parse_args( $args, array(
209 'message' => '',
210 'params' => array(),
211 ) );
212
213 if ( ! isset( $this->errors[$section] ) ) {
214 $this->errors[$section] = array();
215 }
216
217 $this->errors[$section][] = array( 'code' => $code, 'args' => $args );
218
219 return true;
220 }
221
222
223 /**
224 * Removes an error.
225 */
226 public function remove_error( $section, $code ) {
227 if ( empty( $this->errors[$section] ) ) {
228 return;
229 }
230
231 foreach ( (array) $this->errors[$section] as $key => $error ) {
232 if ( isset( $error['code'] )
233 and $error['code'] == $code ) {
234 unset( $this->errors[$section][$key] );
235 }
236 }
237
238 if ( empty( $this->errors[$section] ) ) {
239 unset( $this->errors[$section] );
240 }
241 }
242
243
244 /**
245 * The main validation runner.
246 *
247 * @return bool True if there is no error detected.
248 */
249 public function validate() {
250 $this->errors = array();
251
252 $this->validate_form();
253 $this->validate_mail( 'mail' );
254 $this->validate_mail( 'mail_2' );
255 $this->validate_messages();
256 $this->validate_additional_settings();
257
258 do_action( 'wpcf7_config_validator_validate', $this );
259
260 return $this->is_valid();
261 }
262
263
264 /**
265 * Saves detected errors as a post meta data.
266 */
267 public function save() {
268 if ( $this->contact_form->initial() ) {
269 return;
270 }
271
272 delete_post_meta( $this->contact_form->id(), '_config_errors' );
273
274 if ( $this->errors ) {
275 update_post_meta(
276 $this->contact_form->id(), '_config_errors', $this->errors
277 );
278 }
279 }
280
281
282 /**
283 * Restore errors from the database.
284 */
285 public function restore() {
286 $config_errors = get_post_meta(
287 $this->contact_form->id(), '_config_errors', true
288 );
289
290 foreach ( (array) $config_errors as $section => $errors ) {
291 if ( empty( $errors ) ) {
292 continue;
293 }
294
295 if ( ! is_array( $errors ) ) { // for back-compat
296 $code = $errors;
297 $this->add_error( $section, $code );
298 } else {
299 foreach ( (array) $errors as $error ) {
300 if ( ! empty( $error['code'] ) ) {
301 $code = $error['code'];
302 $args = isset( $error['args'] ) ? $error['args'] : '';
303 $this->add_error( $section, $code, $args );
304 }
305 }
306 }
307 }
308 }
309
310
311 /**
312 * Callback function for WPCF7_MailTaggedText. Replaces mail-tags with
313 * the most conservative inputs.
314 */
315 public function replace_mail_tags_with_minimum_input( $matches ) {
316 // allow [[foo]] syntax for escaping a tag
317 if ( $matches[1] == '[' && $matches[4] == ']' ) {
318 return substr( $matches[0], 1, -1 );
319 }
320
321 $tag = $matches[0];
322 $tagname = $matches[2];
323 $values = $matches[3];
324
325 $mail_tag = new WPCF7_MailTag( $tag, $tagname, $values );
326 $field_name = $mail_tag->field_name();
327
328 $example_email = 'example@example.com';
329 $example_text = 'example';
330 $example_blank = '';
331
332 $form_tags = $this->contact_form->scan_form_tags(
333 array( 'name' => $field_name )
334 );
335
336 if ( $form_tags ) {
337 $form_tag = new WPCF7_FormTag( $form_tags[0] );
338
339 $is_required = ( $form_tag->is_required() || 'radio' == $form_tag->type );
340
341 if ( ! $is_required ) {
342 return $example_blank;
343 }
344
345 if ( wpcf7_form_tag_supports( $form_tag->type, 'selectable-values' ) ) {
346 if ( $form_tag->pipes instanceof WPCF7_Pipes ) {
347 if ( $mail_tag->get_option( 'do_not_heat' ) ) {
348 $before_pipes = $form_tag->pipes->collect_befores();
349 $last_item = array_pop( $before_pipes );
350 } else {
351 $after_pipes = $form_tag->pipes->collect_afters();
352 $last_item = array_pop( $after_pipes );
353 }
354 } else {
355 $last_item = array_pop( $form_tag->values );
356 }
357
358 if ( $last_item and wpcf7_is_mailbox_list( $last_item ) ) {
359 return $example_email;
360 } else {
361 return $example_text;
362 }
363 }
364
365 if ( 'email' == $form_tag->basetype ) {
366 return $example_email;
367 } else {
368 return $example_text;
369 }
370
371 } else { // maybe special mail tag
372 // for back-compat
373 $field_name = preg_replace( '/^wpcf7\./', '_', $field_name );
374
375 if ( '_site_admin_email' == $field_name ) {
376 return get_bloginfo( 'admin_email', 'raw' );
377
378 } elseif ( '_user_agent' == $field_name ) {
379 return $example_text;
380
381 } elseif ( '_user_email' == $field_name ) {
382 return $this->contact_form->is_true( 'subscribers_only' )
383 ? $example_email
384 : $example_blank;
385
386 } elseif ( '_user_' == substr( $field_name, 0, 6 ) ) {
387 return $this->contact_form->is_true( 'subscribers_only' )
388 ? $example_text
389 : $example_blank;
390
391 } elseif ( '_' == substr( $field_name, 0, 1 ) ) {
392 return '_email' == substr( $field_name, -6 )
393 ? $example_email
394 : $example_text;
395
396 }
397 }
398
399 return $tag;
400 }
401
402
403 /**
404 * Runs error detection for the form section.
405 */
406 public function validate_form() {
407 $section = 'form.body';
408 $form = $this->contact_form->prop( 'form' );
409 $this->detect_multiple_controls_in_label( $section, $form );
410 $this->detect_unavailable_names( $section, $form );
411 $this->detect_unavailable_html_elements( $section, $form );
412 $this->detect_dots_in_names( $section, $form );
413 $this->detect_colons_in_names( $section, $form );
414 $this->detect_upload_filesize_overlimit( $section, $form );
415 }
416
417
418 /**
419 * Detects errors of multiple form controls in a single label.
420 *
421 * @link https://contactform7.com/configuration-errors/multiple-controls-in-label/
422 */
423 public function detect_multiple_controls_in_label( $section, $content ) {
424 $pattern = '%<label(?:[ \t\n]+.*?)?>(.+?)</label>%s';
425
426 if ( preg_match_all( $pattern, $content, $matches ) ) {
427 $form_tags_manager = WPCF7_FormTagsManager::get_instance();
428
429 foreach ( $matches[1] as $insidelabel ) {
430 $tags = $form_tags_manager->scan( $insidelabel );
431 $fields_count = 0;
432
433 foreach ( $tags as $tag ) {
434 $is_multiple_controls_container = wpcf7_form_tag_supports(
435 $tag->type, 'multiple-controls-container'
436 );
437
438 $is_zero_controls_container = wpcf7_form_tag_supports(
439 $tag->type, 'zero-controls-container'
440 );
441
442 if ( $is_multiple_controls_container ) {
443 $fields_count += count( $tag->values );
444
445 if ( $tag->has_option( 'free_text' ) ) {
446 $fields_count += 1;
447 }
448 } elseif ( $is_zero_controls_container ) {
449 $fields_count += 0;
450 } elseif ( ! empty( $tag->name ) ) {
451 $fields_count += 1;
452 }
453
454 if ( 1 < $fields_count ) {
455 return $this->add_error( $section,
456 self::error_multiple_controls_in_label, array(
457 'link' => self::get_doc_link( 'multiple_controls_in_label' ),
458 )
459 );
460 }
461 }
462 }
463 }
464
465 return false;
466 }
467
468
469 /**
470 * Detects errors of unavailable form-tag names.
471 *
472 * @link https://contactform7.com/configuration-errors/unavailable-names/
473 */
474 public function detect_unavailable_names( $section, $content ) {
475 $public_query_vars = array( 'm', 'p', 'posts', 'w', 'cat',
476 'withcomments', 'withoutcomments', 's', 'search', 'exact', 'sentence',
477 'calendar', 'page', 'paged', 'more', 'tb', 'pb', 'author', 'order',
478 'orderby', 'year', 'monthnum', 'day', 'hour', 'minute', 'second',
479 'name', 'category_name', 'tag', 'feed', 'author_name', 'static',
480 'pagename', 'page_id', 'error', 'attachment', 'attachment_id',
481 'subpost', 'subpost_id', 'preview', 'robots', 'taxonomy', 'term',
482 'cpage', 'post_type', 'embed',
483 );
484
485 $form_tags_manager = WPCF7_FormTagsManager::get_instance();
486
487 $ng_named_tags = $form_tags_manager->filter( $content, array(
488 'name' => $public_query_vars,
489 ) );
490
491 $ng_names = array();
492
493 foreach ( $ng_named_tags as $tag ) {
494 $ng_names[] = sprintf( '"%s"', $tag->name );
495 }
496
497 if ( $ng_names ) {
498 $ng_names = array_unique( $ng_names );
499
500 return $this->add_error( $section,
501 self::error_unavailable_names,
502 array(
503 'message' =>
504 /* translators: %names%: a list of form control names */
505 __( "Unavailable names (%names%) are used for form controls.", 'contact-form-7' ),
506 'params' => array( 'names' => implode( ', ', $ng_names ) ),
507 'link' => self::get_doc_link( 'unavailable_names' ),
508 )
509 );
510 }
511
512 return false;
513 }
514
515
516 /**
517 * Detects errors of unavailable HTML elements.
518 *
519 * @link https://contactform7.com/configuration-errors/unavailable-html-elements/
520 */
521 public function detect_unavailable_html_elements( $section, $content ) {
522 $pattern = '%(?:<form[\s\t>]|</form>)%i';
523
524 if ( preg_match( $pattern, $content ) ) {
525 return $this->add_error( $section,
526 self::error_unavailable_html_elements,
527 array(
528 'message' => __( "Unavailable HTML elements are used in the form template.", 'contact-form-7' ),
529 'link' => self::get_doc_link( 'unavailable_html_elements' ),
530 )
531 );
532 }
533
534 return false;
535 }
536
537
538 /**
539 * Detects errors of dots in form-tag names.
540 *
541 * @link https://contactform7.com/configuration-errors/dots-in-names/
542 */
543 public function detect_dots_in_names( $section, $content ) {
544 $form_tags_manager = WPCF7_FormTagsManager::get_instance();
545
546 $tags = $form_tags_manager->filter( $content, array(
547 'feature' => 'name-attr',
548 ) );
549
550 foreach ( $tags as $tag ) {
551 if ( false !== strpos( $tag->raw_name, '.' ) ) {
552 return $this->add_error( $section,
553 self::error_dots_in_names,
554 array(
555 'message' => __( "Dots are used in form-tag names.", 'contact-form-7' ),
556 'link' => self::get_doc_link( 'dots_in_names' ),
557 )
558 );
559 }
560 }
561
562 return false;
563 }
564
565
566 /**
567 * Detects errors of colons in form-tag names.
568 *
569 * @link https://contactform7.com/configuration-errors/colons-in-names/
570 */
571 public function detect_colons_in_names( $section, $content ) {
572 $form_tags_manager = WPCF7_FormTagsManager::get_instance();
573
574 $tags = $form_tags_manager->filter( $content, array(
575 'feature' => 'name-attr',
576 ) );
577
578 foreach ( $tags as $tag ) {
579 if ( false !== strpos( $tag->raw_name, ':' ) ) {
580 return $this->add_error( $section,
581 self::error_colons_in_names,
582 array(
583 'message' => __( "Colons are used in form-tag names.", 'contact-form-7' ),
584 'link' => self::get_doc_link( 'colons_in_names' ),
585 )
586 );
587 }
588 }
589
590 return false;
591 }
592
593
594 /**
595 * Detects errors of uploadable file size overlimit.
596 *
597 * @link https://contactform7.com/configuration-errors/upload-filesize-overlimit
598 */
599 public function detect_upload_filesize_overlimit( $section, $content ) {
600 $upload_max_filesize = ini_get( 'upload_max_filesize' );
601
602 if ( ! $upload_max_filesize ) {
603 return false;
604 }
605
606 $upload_max_filesize = strtolower( $upload_max_filesize );
607 $upload_max_filesize = trim( $upload_max_filesize );
608
609 if ( ! preg_match( '/^(\d+)([kmg]?)$/', $upload_max_filesize, $matches ) ) {
610 return false;
611 }
612
613 if ( 'k' === $matches[2] ) {
614 $upload_max_filesize = (int) $matches[1] * KB_IN_BYTES;
615 } elseif ( 'm' === $matches[2] ) {
616 $upload_max_filesize = (int) $matches[1] * MB_IN_BYTES;
617 } elseif ( 'g' === $matches[2] ) {
618 $upload_max_filesize = (int) $matches[1] * GB_IN_BYTES;
619 } else {
620 $upload_max_filesize = (int) $matches[1];
621 }
622
623 $form_tags_manager = WPCF7_FormTagsManager::get_instance();
624
625 $tags = $form_tags_manager->filter( $content, array(
626 'basetype' => 'file',
627 ) );
628
629 foreach ( $tags as $tag ) {
630 if ( $upload_max_filesize < $tag->get_limit_option() ) {
631 return $this->add_error( $section,
632 self::error_upload_filesize_overlimit,
633 array(
634 'message' => __( "Uploadable file size exceeds PHP’s maximum acceptable size.", 'contact-form-7' ),
635 'link' => self::get_doc_link( 'upload_filesize_overlimit' ),
636 )
637 );
638 }
639 }
640
641 return false;
642 }
643
644
645 /**
646 * Runs error detection for the mail sections.
647 */
648 public function validate_mail( $template = 'mail' ) {
649 $components = (array) $this->contact_form->prop( $template );
650
651 if ( ! $components ) {
652 return;
653 }
654
655 if ( 'mail' !== $template and empty( $components['active'] ) ) {
656 return;
657 }
658
659 $components = wp_parse_args( $components, array(
660 'subject' => '',
661 'sender' => '',
662 'recipient' => '',
663 'additional_headers' => '',
664 'body' => '',
665 'attachments' => '',
666 ) );
667
668 $callback = array( $this, 'replace_mail_tags_with_minimum_input' );
669
670 $subject = new WPCF7_MailTaggedText(
671 $components['subject'],
672 array( 'callback' => $callback )
673 );
674
675 $subject = $subject->replace_tags();
676 $subject = wpcf7_strip_newline( $subject );
677
678 $this->detect_maybe_empty( sprintf( '%s.subject', $template ), $subject );
679
680 $sender = new WPCF7_MailTaggedText(
681 $components['sender'],
682 array( 'callback' => $callback )
683 );
684
685 $sender = $sender->replace_tags();
686 $sender = wpcf7_strip_newline( $sender );
687
688 $invalid_mailbox = $this->detect_invalid_mailbox_syntax(
689 sprintf( '%s.sender', $template ),
690 $sender
691 );
692
693 if ( ! $invalid_mailbox and ! wpcf7_is_email_in_site_domain( $sender ) ) {
694 $this->add_error( sprintf( '%s.sender', $template ),
695 self::error_email_not_in_site_domain, array(
696 'link' => self::get_doc_link( 'email_not_in_site_domain' ),
697 )
698 );
699 }
700
701 $recipient = new WPCF7_MailTaggedText(
702 $components['recipient'],
703 array( 'callback' => $callback )
704 );
705
706 $recipient = $recipient->replace_tags();
707 $recipient = wpcf7_strip_newline( $recipient );
708
709 $this->detect_invalid_mailbox_syntax(
710 sprintf( '%s.recipient', $template ),
711 $recipient
712 );
713
714 $additional_headers = new WPCF7_MailTaggedText(
715 $components['additional_headers'],
716 array( 'callback' => $callback )
717 );
718
719 $additional_headers = $additional_headers->replace_tags();
720 $additional_headers = explode( "\n", $additional_headers );
721 $mailbox_header_types = array( 'reply-to', 'cc', 'bcc' );
722 $invalid_mail_header_exists = false;
723
724 foreach ( $additional_headers as $header ) {
725 $header = trim( $header );
726
727 if ( '' === $header ) {
728 continue;
729 }
730
731 if ( ! preg_match( '/^([0-9A-Za-z-]+):(.*)$/', $header, $matches ) ) {
732 $invalid_mail_header_exists = true;
733 } else {
734 $header_name = $matches[1];
735 $header_value = trim( $matches[2] );
736
737 if ( in_array( strtolower( $header_name ), $mailbox_header_types )
738 and '' !== $header_value ) {
739 $this->detect_invalid_mailbox_syntax(
740 sprintf( '%s.additional_headers', $template ),
741 $header_value,
742 array(
743 'message' =>
744 __( "Invalid mailbox syntax is used in the %name% field.", 'contact-form-7' ),
745 'params' => array( 'name' => $header_name )
746 )
747 );
748 }
749 }
750 }
751
752 if ( $invalid_mail_header_exists ) {
753 $this->add_error( sprintf( '%s.additional_headers', $template ),
754 self::error_invalid_mail_header, array(
755 'link' => self::get_doc_link( 'invalid_mail_header' ),
756 )
757 );
758 }
759
760 $body = new WPCF7_MailTaggedText(
761 $components['body'],
762 array( 'callback' => $callback )
763 );
764
765 $body = $body->replace_tags();
766
767 $this->detect_maybe_empty( sprintf( '%s.body', $template ), $body );
768
769 if ( '' !== $components['attachments'] ) {
770 $attachables = array();
771
772 $tags = $this->contact_form->scan_form_tags(
773 array( 'type' => array( 'file', 'file*' ) )
774 );
775
776 foreach ( $tags as $tag ) {
777 $name = $tag->name;
778
779 if ( false === strpos( $components['attachments'], "[{$name}]" ) ) {
780 continue;
781 }
782
783 $limit = (int) $tag->get_limit_option();
784
785 if ( empty( $attachables[$name] )
786 or $attachables[$name] < $limit ) {
787 $attachables[$name] = $limit;
788 }
789 }
790
791 $total_size = array_sum( $attachables );
792
793 $has_file_not_found = false;
794 $has_file_not_in_content_dir = false;
795
796 foreach ( explode( "\n", $components['attachments'] ) as $line ) {
797 $line = trim( $line );
798
799 if ( '' === $line or '[' == substr( $line, 0, 1 ) ) {
800 continue;
801 }
802
803 $has_file_not_found = $this->detect_file_not_found(
804 sprintf( '%s.attachments', $template ), $line
805 );
806
807 if ( ! $has_file_not_found and ! $has_file_not_in_content_dir ) {
808 $has_file_not_in_content_dir = $this->detect_file_not_in_content_dir(
809 sprintf( '%s.attachments', $template ), $line
810 );
811 }
812
813 if ( ! $has_file_not_found ) {
814 $path = path_join( WP_CONTENT_DIR, $line );
815 $total_size += (int) @filesize( $path );
816 }
817 }
818
819 $max = 25 * MB_IN_BYTES; // 25 MB
820
821 if ( $max < $total_size ) {
822 $this->add_error( sprintf( '%s.attachments', $template ),
823 self::error_attachments_overweight,
824 array(
825 'message' => __( "The total size of attachment files is too large.", 'contact-form-7' ),
826 'link' => self::get_doc_link( 'attachments_overweight' ),
827 )
828 );
829 }
830 }
831 }
832
833
834 /**
835 * Detects errors of invalid mailbox syntax.
836 *
837 * @link https://contactform7.com/configuration-errors/invalid-mailbox-syntax/
838 */
839 public function detect_invalid_mailbox_syntax( $section, $content, $args = '' ) {
840 $args = wp_parse_args( $args, array(
841 'link' => self::get_doc_link( 'invalid_mailbox_syntax' ),
842 'message' => '',
843 'params' => array(),
844 ) );
845
846 if ( ! wpcf7_is_mailbox_list( $content ) ) {
847 return $this->add_error( $section,
848 self::error_invalid_mailbox_syntax, $args
849 );
850 }
851
852 return false;
853 }
854
855
856 /**
857 * Detects errors of empty message fields.
858 *
859 * @link https://contactform7.com/configuration-errors/maybe-empty/
860 */
861 public function detect_maybe_empty( $section, $content ) {
862 if ( '' === $content ) {
863 return $this->add_error( $section,
864 self::error_maybe_empty, array(
865 'link' => self::get_doc_link( 'maybe_empty' ),
866 )
867 );
868 }
869
870 return false;
871 }
872
873
874 /**
875 * Detects errors of nonexistent attachment files.
876 *
877 * @link https://contactform7.com/configuration-errors/file-not-found/
878 */
879 public function detect_file_not_found( $section, $content ) {
880 $path = path_join( WP_CONTENT_DIR, $content );
881
882 if ( ! is_readable( $path ) or ! is_file( $path ) ) {
883 return $this->add_error( $section,
884 self::error_file_not_found,
885 array(
886 'message' =>
887 __( "Attachment file does not exist at %path%.", 'contact-form-7' ),
888 'params' => array( 'path' => $content ),
889 'link' => self::get_doc_link( 'file_not_found' ),
890 )
891 );
892 }
893
894 return false;
895 }
896
897
898 /**
899 * Detects errors of attachment files out of the content directory.
900 *
901 * @link https://contactform7.com/configuration-errors/file-not-in-content-dir/
902 */
903 public function detect_file_not_in_content_dir( $section, $content ) {
904 $path = path_join( WP_CONTENT_DIR, $content );
905
906 if ( ! wpcf7_is_file_path_in_content_dir( $path ) ) {
907 return $this->add_error( $section,
908 self::error_file_not_in_content_dir,
909 array(
910 'message' =>
911 __( "It is not allowed to use files outside the wp-content directory.", 'contact-form-7' ),
912 'link' => self::get_doc_link( 'file_not_in_content_dir' ),
913 )
914 );
915 }
916
917 return false;
918 }
919
920
921 /**
922 * Runs error detection for the messages section.
923 */
924 public function validate_messages() {
925 $messages = (array) $this->contact_form->prop( 'messages' );
926
927 if ( ! $messages ) {
928 return;
929 }
930
931 if ( isset( $messages['captcha_not_match'] )
932 and ! wpcf7_use_really_simple_captcha() ) {
933 unset( $messages['captcha_not_match'] );
934 }
935
936 foreach ( $messages as $key => $message ) {
937 $section = sprintf( 'messages.%s', $key );
938 $this->detect_html_in_message( $section, $message );
939 }
940 }
941
942
943 /**
944 * Detects errors of HTML uses in a message.
945 *
946 * @link https://contactform7.com/configuration-errors/html-in-message/
947 */
948 public function detect_html_in_message( $section, $content ) {
949 $stripped = wp_strip_all_tags( $content );
950
951 if ( $stripped != $content ) {
952 return $this->add_error( $section,
953 self::error_html_in_message,
954 array(
955 'link' => self::get_doc_link( 'html_in_message' ),
956 )
957 );
958 }
959
960 return false;
961 }
962
963
964 /**
965 * Runs error detection for the additional settings section.
966 */
967 public function validate_additional_settings() {
968 $deprecated_settings_used =
969 $this->contact_form->additional_setting( 'on_sent_ok' ) ||
970 $this->contact_form->additional_setting( 'on_submit' );
971
972 if ( $deprecated_settings_used ) {
973 return $this->add_error( 'additional_settings.body',
974 self::error_deprecated_settings,
975 array(
976 'link' => self::get_doc_link( 'deprecated_settings' ),
977 )
978 );
979 }
980 }
981
982 }
983