PluginProbe ʕ •ᴥ•ʔ
Contact Form 7 / 5.1.1
Contact Form 7 v5.1.1
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
css 7 years ago js 7 years ago capabilities.php 7 years ago config-validator.php 7 years ago contact-form-functions.php 7 years ago contact-form-template.php 7 years ago contact-form.php 7 years ago controller.php 7 years ago form-tag.php 7 years ago form-tags-manager.php 7 years ago formatting.php 7 years ago functions.php 7 years ago integration.php 7 years ago l10n.php 7 years ago mail.php 7 years ago pipe.php 9 years ago rest-api.php 7 years ago shortcodes.php 9 years ago special-mail-tags.php 7 years ago submission.php 7 years ago upgrade.php 7 years ago validation.php 7 years ago
config-validator.php
644 lines
1 <?php
2
3 class WPCF7_ConfigValidator {
4
5 const error = 100;
6 const error_maybe_empty = 101;
7 const error_invalid_mailbox_syntax = 102;
8 const error_email_not_in_site_domain = 103;
9 const error_html_in_message = 104;
10 const error_multiple_controls_in_label = 105;
11 const error_file_not_found = 106;
12 const error_unavailable_names = 107;
13 const error_invalid_mail_header = 108;
14 const error_deprecated_settings = 109;
15 const error_file_not_in_content_dir = 110;
16
17 public static function get_doc_link( $error_code = '' ) {
18 $url = __( 'https://contactform7.com/configuration-errors/',
19 'contact-form-7' );
20
21 if ( '' !== $error_code ) {
22 $error_code = strtr( $error_code, '_', '-' );
23
24 $url = sprintf( '%s/%s', untrailingslashit( $url ), $error_code );
25 }
26
27 return esc_url( $url );
28 }
29
30 private $contact_form;
31 private $errors = array();
32
33 public function __construct( WPCF7_ContactForm $contact_form ) {
34 $this->contact_form = $contact_form;
35 }
36
37 public function contact_form() {
38 return $this->contact_form;
39 }
40
41 public function is_valid() {
42 return ! $this->count_errors();
43 }
44
45 public function count_errors( $args = '' ) {
46 $args = wp_parse_args( $args, array(
47 'section' => '',
48 'code' => '',
49 ) );
50
51 $count = 0;
52
53 foreach ( $this->errors as $key => $errors ) {
54 if ( preg_match( '/^mail_[0-9]+\.(.*)$/', $key, $matches ) ) {
55 $key = sprintf( 'mail.%s', $matches[1] );
56 }
57
58 if ( $args['section']
59 and $key != $args['section']
60 and preg_replace( '/\..*$/', '', $key, 1 ) != $args['section'] ) {
61 continue;
62 }
63
64 foreach ( $errors as $error ) {
65 if ( empty( $error ) ) {
66 continue;
67 }
68
69 if ( $args['code'] and $error['code'] != $args['code'] ) {
70 continue;
71 }
72
73 $count += 1;
74 }
75 }
76
77 return $count;
78 }
79
80 public function collect_error_messages() {
81 $error_messages = array();
82
83 foreach ( $this->errors as $section => $errors ) {
84 $error_messages[$section] = array();
85
86 foreach ( $errors as $error ) {
87 if ( empty( $error['args']['message'] ) ) {
88 $message = $this->get_default_message( $error['code'] );
89 } elseif ( empty( $error['args']['params'] ) ) {
90 $message = $error['args']['message'];
91 } else {
92 $message = $this->build_message(
93 $error['args']['message'],
94 $error['args']['params'] );
95 }
96
97 $link = '';
98
99 if ( ! empty( $error['args']['link'] ) ) {
100 $link = $error['args']['link'];
101 }
102
103 $error_messages[$section][] = array(
104 'message' => $message,
105 'link' => esc_url( $link ),
106 );
107 }
108 }
109
110 return $error_messages;
111 }
112
113 public function build_message( $message, $params = '' ) {
114 $params = wp_parse_args( $params, array() );
115
116 foreach ( $params as $key => $val ) {
117 if ( ! preg_match( '/^[0-9A-Za-z_]+$/', $key ) ) { // invalid key
118 continue;
119 }
120
121 $placeholder = '%' . $key . '%';
122
123 if ( false !== stripos( $message, $placeholder ) ) {
124 $message = str_ireplace( $placeholder, $val, $message );
125 }
126 }
127
128 return $message;
129 }
130
131 public function get_default_message( $code ) {
132 switch ( $code ) {
133 case self::error_maybe_empty:
134 return __( "There is a possible empty field.", 'contact-form-7' );
135 case self::error_invalid_mailbox_syntax:
136 return __( "Invalid mailbox syntax is used.", 'contact-form-7' );
137 case self::error_email_not_in_site_domain:
138 return __( "Sender email address does not belong to the site domain.", 'contact-form-7' );
139 case self::error_html_in_message:
140 return __( "HTML tags are used in a message.", 'contact-form-7' );
141 case self::error_multiple_controls_in_label:
142 return __( "Multiple form controls are in a single label element.", 'contact-form-7' );
143 case self::error_invalid_mail_header:
144 return __( "There are invalid mail header fields.", 'contact-form-7' );
145 case self::error_deprecated_settings:
146 return __( "Deprecated settings are used.", 'contact-form-7' );
147 default:
148 return '';
149 }
150 }
151
152 public function add_error( $section, $code, $args = '' ) {
153 $args = wp_parse_args( $args, array(
154 'message' => '',
155 'params' => array(),
156 ) );
157
158 if ( ! isset( $this->errors[$section] ) ) {
159 $this->errors[$section] = array();
160 }
161
162 $this->errors[$section][] = array( 'code' => $code, 'args' => $args );
163
164 return true;
165 }
166
167 public function remove_error( $section, $code ) {
168 if ( empty( $this->errors[$section] ) ) {
169 return;
170 }
171
172 foreach ( (array) $this->errors[$section] as $key => $error ) {
173 if ( isset( $error['code'] )
174 and $error['code'] == $code ) {
175 unset( $this->errors[$section][$key] );
176 }
177 }
178 }
179
180 public function validate() {
181 $this->errors = array();
182
183 $this->validate_form();
184 $this->validate_mail( 'mail' );
185 $this->validate_mail( 'mail_2' );
186 $this->validate_messages();
187 $this->validate_additional_settings();
188
189 do_action( 'wpcf7_config_validator_validate', $this );
190
191 return $this->is_valid();
192 }
193
194 public function save() {
195 if ( $this->contact_form->initial() ) {
196 return;
197 }
198
199 delete_post_meta( $this->contact_form->id(), '_config_errors' );
200
201 if ( $this->errors ) {
202 update_post_meta( $this->contact_form->id(), '_config_errors',
203 $this->errors );
204 }
205 }
206
207 public function restore() {
208 $config_errors = get_post_meta(
209 $this->contact_form->id(), '_config_errors', true );
210
211 foreach ( (array) $config_errors as $section => $errors ) {
212 if ( empty( $errors ) ) {
213 continue;
214 }
215
216 if ( ! is_array( $errors ) ) { // for back-compat
217 $code = $errors;
218 $this->add_error( $section, $code );
219 } else {
220 foreach ( (array) $errors as $error ) {
221 if ( ! empty( $error['code'] ) ) {
222 $code = $error['code'];
223 $args = isset( $error['args'] ) ? $error['args'] : '';
224 $this->add_error( $section, $code, $args );
225 }
226 }
227 }
228 }
229 }
230
231 public function replace_mail_tags_with_minimum_input( $matches ) {
232 // allow [[foo]] syntax for escaping a tag
233 if ( $matches[1] == '[' && $matches[4] == ']' ) {
234 return substr( $matches[0], 1, -1 );
235 }
236
237 $tag = $matches[0];
238 $tagname = $matches[2];
239 $values = $matches[3];
240
241 $mail_tag = new WPCF7_MailTag( $tag, $tagname, $values );
242 $field_name = $mail_tag->field_name();
243
244 $example_email = 'example@example.com';
245 $example_text = 'example';
246 $example_blank = '';
247
248 $form_tags = $this->contact_form->scan_form_tags(
249 array( 'name' => $field_name ) );
250
251 if ( $form_tags ) {
252 $form_tag = new WPCF7_FormTag( $form_tags[0] );
253
254 $is_required = ( $form_tag->is_required() || 'radio' == $form_tag->type );
255
256 if ( ! $is_required ) {
257 return $example_blank;
258 }
259
260 if ( wpcf7_form_tag_supports( $form_tag->type, 'selectable-values' ) ) {
261 if ( $form_tag->pipes instanceof WPCF7_Pipes ) {
262 if ( $mail_tag->get_option( 'do_not_heat' ) ) {
263 $before_pipes = $form_tag->pipes->collect_befores();
264 $last_item = array_pop( $before_pipes );
265 } else {
266 $after_pipes = $form_tag->pipes->collect_afters();
267 $last_item = array_pop( $after_pipes );
268 }
269 } else {
270 $last_item = array_pop( $form_tag->values );
271 }
272
273 if ( $last_item and wpcf7_is_mailbox_list( $last_item ) ) {
274 return $example_email;
275 } else {
276 return $example_text;
277 }
278 }
279
280 if ( 'email' == $form_tag->basetype ) {
281 return $example_email;
282 } else {
283 return $example_text;
284 }
285
286 } else { // maybe special mail tag
287 // for back-compat
288 $field_name = preg_replace( '/^wpcf7\./', '_', $field_name );
289
290 if ( '_site_admin_email' == $field_name ) {
291 return get_bloginfo( 'admin_email', 'raw' );
292
293 } elseif ( '_user_agent' == $field_name ) {
294 return $example_text;
295
296 } elseif ( '_user_email' == $field_name ) {
297 return $this->contact_form->is_true( 'subscribers_only' )
298 ? $example_email
299 : $example_blank;
300
301 } elseif ( '_user_' == substr( $field_name, 0, 6 ) ) {
302 return $this->contact_form->is_true( 'subscribers_only' )
303 ? $example_text
304 : $example_blank;
305
306 } elseif ( '_' == substr( $field_name, 0, 1 ) ) {
307 return '_email' == substr( $field_name, -6 )
308 ? $example_email
309 : $example_text;
310
311 }
312 }
313
314 return $tag;
315 }
316
317 public function validate_form() {
318 $section = 'form.body';
319 $form = $this->contact_form->prop( 'form' );
320 $this->detect_multiple_controls_in_label( $section, $form );
321 $this->detect_unavailable_names( $section, $form );
322 }
323
324 public function detect_multiple_controls_in_label( $section, $content ) {
325 $pattern = '%<label(?:[ \t\n]+.*?)?>(.+?)</label>%s';
326
327 if ( preg_match_all( $pattern, $content, $matches ) ) {
328 $form_tags_manager = WPCF7_FormTagsManager::get_instance();
329
330 foreach ( $matches[1] as $insidelabel ) {
331 $tags = $form_tags_manager->scan( $insidelabel );
332 $fields_count = 0;
333
334 foreach ( $tags as $tag ) {
335 $is_multiple_controls_container = wpcf7_form_tag_supports(
336 $tag->type, 'multiple-controls-container' );
337 $is_zero_controls_container = wpcf7_form_tag_supports(
338 $tag->type, 'zero-controls-container' );
339
340 if ( $is_multiple_controls_container ) {
341 $fields_count += count( $tag->values );
342
343 if ( $tag->has_option( 'free_text' ) ) {
344 $fields_count += 1;
345 }
346 } elseif ( $is_zero_controls_container ) {
347 $fields_count += 0;
348 } elseif ( ! empty( $tag->name ) ) {
349 $fields_count += 1;
350 }
351
352 if ( 1 < $fields_count ) {
353 return $this->add_error( $section,
354 self::error_multiple_controls_in_label, array(
355 'link' => self::get_doc_link( 'multiple_controls_in_label' ),
356 )
357 );
358 }
359 }
360 }
361 }
362
363 return false;
364 }
365
366 public function detect_unavailable_names( $section, $content ) {
367 $public_query_vars = array( 'm', 'p', 'posts', 'w', 'cat',
368 'withcomments', 'withoutcomments', 's', 'search', 'exact', 'sentence',
369 'calendar', 'page', 'paged', 'more', 'tb', 'pb', 'author', 'order',
370 'orderby', 'year', 'monthnum', 'day', 'hour', 'minute', 'second',
371 'name', 'category_name', 'tag', 'feed', 'author_name', 'static',
372 'pagename', 'page_id', 'error', 'attachment', 'attachment_id',
373 'subpost', 'subpost_id', 'preview', 'robots', 'taxonomy', 'term',
374 'cpage', 'post_type', 'embed' );
375
376 $form_tags_manager = WPCF7_FormTagsManager::get_instance();
377 $ng_named_tags = $form_tags_manager->filter( $content,
378 array( 'name' => $public_query_vars ) );
379
380 $ng_names = array();
381
382 foreach ( $ng_named_tags as $tag ) {
383 $ng_names[] = sprintf( '"%s"', $tag->name );
384 }
385
386 if ( $ng_names ) {
387 $ng_names = array_unique( $ng_names );
388
389 return $this->add_error( $section,
390 self::error_unavailable_names,
391 array(
392 'message' =>
393 /* translators: %names%: a list of form control names */
394 __( "Unavailable names (%names%) are used for form controls.", 'contact-form-7' ),
395 'params' => array( 'names' => implode( ', ', $ng_names ) ),
396 'link' => self::get_doc_link( 'unavailable_names' ),
397 )
398 );
399 }
400
401 return false;
402 }
403
404 public function validate_mail( $template = 'mail' ) {
405 $components = (array) $this->contact_form->prop( $template );
406
407 if ( ! $components ) {
408 return;
409 }
410
411 if ( 'mail' != $template
412 and empty( $components['active'] ) ) {
413 return;
414 }
415
416 $components = wp_parse_args( $components, array(
417 'subject' => '',
418 'sender' => '',
419 'recipient' => '',
420 'additional_headers' => '',
421 'body' => '',
422 'attachments' => '',
423 ) );
424
425 $callback = array( $this, 'replace_mail_tags_with_minimum_input' );
426
427 $subject = $components['subject'];
428 $subject = new WPCF7_MailTaggedText( $subject,
429 array( 'callback' => $callback ) );
430 $subject = $subject->replace_tags();
431 $subject = wpcf7_strip_newline( $subject );
432 $this->detect_maybe_empty( sprintf( '%s.subject', $template ), $subject );
433
434 $sender = $components['sender'];
435 $sender = new WPCF7_MailTaggedText( $sender,
436 array( 'callback' => $callback ) );
437 $sender = $sender->replace_tags();
438 $sender = wpcf7_strip_newline( $sender );
439
440 if ( ! $this->detect_invalid_mailbox_syntax( sprintf( '%s.sender', $template ), $sender )
441 and ! wpcf7_is_email_in_site_domain( $sender ) ) {
442 $this->add_error( sprintf( '%s.sender', $template ),
443 self::error_email_not_in_site_domain, array(
444 'link' => self::get_doc_link( 'email_not_in_site_domain' ),
445 )
446 );
447 }
448
449 $recipient = $components['recipient'];
450 $recipient = new WPCF7_MailTaggedText( $recipient,
451 array( 'callback' => $callback ) );
452 $recipient = $recipient->replace_tags();
453 $recipient = wpcf7_strip_newline( $recipient );
454
455 $this->detect_invalid_mailbox_syntax(
456 sprintf( '%s.recipient', $template ), $recipient );
457
458 $additional_headers = $components['additional_headers'];
459 $additional_headers = new WPCF7_MailTaggedText( $additional_headers,
460 array( 'callback' => $callback ) );
461 $additional_headers = $additional_headers->replace_tags();
462 $additional_headers = explode( "\n", $additional_headers );
463 $mailbox_header_types = array( 'reply-to', 'cc', 'bcc' );
464 $invalid_mail_header_exists = false;
465
466 foreach ( $additional_headers as $header ) {
467 $header = trim( $header );
468
469 if ( '' === $header ) {
470 continue;
471 }
472
473 if ( ! preg_match( '/^([0-9A-Za-z-]+):(.*)$/', $header, $matches ) ) {
474 $invalid_mail_header_exists = true;
475 } else {
476 $header_name = $matches[1];
477 $header_value = trim( $matches[2] );
478
479 if ( in_array( strtolower( $header_name ), $mailbox_header_types ) ) {
480 $this->detect_invalid_mailbox_syntax(
481 sprintf( '%s.additional_headers', $template ),
482 $header_value, array(
483 'message' =>
484 __( "Invalid mailbox syntax is used in the %name% field.", 'contact-form-7' ),
485 'params' => array( 'name' => $header_name ) ) );
486 } elseif ( empty( $header_value ) ) {
487 $invalid_mail_header_exists = true;
488 }
489 }
490 }
491
492 if ( $invalid_mail_header_exists ) {
493 $this->add_error( sprintf( '%s.additional_headers', $template ),
494 self::error_invalid_mail_header, array(
495 'link' => self::get_doc_link( 'invalid_mail_header' ),
496 )
497 );
498 }
499
500 $body = $components['body'];
501 $body = new WPCF7_MailTaggedText( $body,
502 array( 'callback' => $callback ) );
503 $body = $body->replace_tags();
504 $this->detect_maybe_empty( sprintf( '%s.body', $template ), $body );
505
506 if ( '' !== $components['attachments'] ) {
507 $has_file_not_found = false;
508 $has_file_not_in_content_dir = false;
509
510 foreach ( explode( "\n", $components['attachments'] ) as $line ) {
511 $line = trim( $line );
512
513 if ( '' === $line
514 or '[' == substr( $line, 0, 1 ) ) {
515 continue;
516 }
517
518 $has_file_not_found = $this->detect_file_not_found(
519 sprintf( '%s.attachments', $template ), $line
520 );
521
522 if ( ! $has_file_not_found
523 and ! $has_file_not_in_content_dir ) {
524 $has_file_not_in_content_dir = $this->detect_file_not_in_content_dir(
525 sprintf( '%s.attachments', $template ), $line
526 );
527 }
528 }
529 }
530 }
531
532 public function detect_invalid_mailbox_syntax( $section, $content, $args = '' ) {
533 $args = wp_parse_args( $args, array(
534 'link' => self::get_doc_link( 'invalid_mailbox_syntax' ),
535 'message' => '',
536 'params' => array(),
537 ) );
538
539 if ( ! wpcf7_is_mailbox_list( $content ) ) {
540 return $this->add_error( $section,
541 self::error_invalid_mailbox_syntax, $args );
542 }
543
544 return false;
545 }
546
547 public function detect_maybe_empty( $section, $content ) {
548 if ( '' === $content ) {
549 return $this->add_error( $section,
550 self::error_maybe_empty, array(
551 'link' => self::get_doc_link( 'maybe_empty' ),
552 )
553 );
554 }
555
556 return false;
557 }
558
559 public function detect_file_not_found( $section, $content ) {
560 $path = path_join( WP_CONTENT_DIR, $content );
561
562 if ( ! is_readable( $path )
563 or ! is_file( $path ) ) {
564 return $this->add_error( $section,
565 self::error_file_not_found,
566 array(
567 'message' =>
568 __( "Attachment file does not exist at %path%.", 'contact-form-7' ),
569 'params' => array( 'path' => $content ),
570 'link' => self::get_doc_link( 'file_not_found' ),
571 )
572 );
573 }
574
575 return false;
576 }
577
578 public function detect_file_not_in_content_dir( $section, $content ) {
579 $path = path_join( WP_CONTENT_DIR, $content );
580
581 if ( ! wpcf7_is_file_path_in_content_dir( $path ) ) {
582 return $this->add_error( $section,
583 self::error_file_not_in_content_dir,
584 array(
585 'message' =>
586 __( "It is not allowed to use files outside the wp-content directory.", 'contact-form-7' ),
587 'link' => self::get_doc_link( 'file_not_in_content_dir' ),
588 )
589 );
590 }
591
592 return false;
593 }
594
595 public function validate_messages() {
596 $messages = (array) $this->contact_form->prop( 'messages' );
597
598 if ( ! $messages ) {
599 return;
600 }
601
602 if ( isset( $messages['captcha_not_match'] )
603 and ! wpcf7_use_really_simple_captcha() ) {
604 unset( $messages['captcha_not_match'] );
605 }
606
607 foreach ( $messages as $key => $message ) {
608 $section = sprintf( 'messages.%s', $key );
609 $this->detect_html_in_message( $section, $message );
610 }
611 }
612
613 public function detect_html_in_message( $section, $content ) {
614 $stripped = wp_strip_all_tags( $content );
615
616 if ( $stripped != $content ) {
617 return $this->add_error( $section,
618 self::error_html_in_message,
619 array(
620 'link' => self::get_doc_link( 'html_in_message' ),
621 )
622 );
623 }
624
625 return false;
626 }
627
628 public function validate_additional_settings() {
629 $deprecated_settings_used =
630 $this->contact_form->additional_setting( 'on_sent_ok' ) ||
631 $this->contact_form->additional_setting( 'on_submit' );
632
633 if ( $deprecated_settings_used ) {
634 return $this->add_error( 'additional_settings.body',
635 self::error_deprecated_settings,
636 array(
637 'link' => self::get_doc_link( 'deprecated_settings' ),
638 )
639 );
640 }
641 }
642
643 }
644