PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 4.11.05
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v4.11.05
6.35 6.34 6.33.1 6.33 6.32.1 6.32 6.31 6.25 6.25.1 6.26 6.26.1 6.27 6.28 6.29 6.3 6.3.1 6.3.2 6.30 6.4 6.4.1 6.4.2 6.5 6.5.1 6.5.2 6.5.3 All 141 releases
formidable / classes / models / FrmEntryValidate.php

FrmEntryValidate.php in Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More 4.11.05, at classes/models/FrmEntryValidate.php

475 lines 14.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) {
3 die( 'You are not allowed to call this page directly.' );
4 }
5
6 class FrmEntryValidate {
7 public static function validate( $values, $exclude = false ) {
8 FrmEntry::sanitize_entry_post( $values );
9 $errors = array();
10
11 if ( ! isset( $values['form_id'] ) || ! isset( $values['item_meta'] ) ) {
12 $errors['form'] = __( 'There was a problem with your submission. Please try again.', 'formidable' );
13
14 return $errors;
15 }
16
17 if ( FrmAppHelper::is_admin() && is_user_logged_in() && ( ! isset( $values[ 'frm_submit_entry_' . $values['form_id'] ] ) || ! wp_verify_nonce( $values[ 'frm_submit_entry_' . $values['form_id'] ], 'frm_submit_entry_nonce' ) ) ) {
18 $errors['form'] = __( 'You do not have permission to do that', 'formidable' );
19 }
20
21 self::set_item_key( $values );
22
23 $posted_fields = self::get_fields_to_validate( $values, $exclude );
24
25 // Pass exclude value to validate_field function so it can be used for repeating sections
26 $args = array( 'exclude' => $exclude );
27
28 foreach ( $posted_fields as $posted_field ) {
29 self::validate_field( $posted_field, $errors, $values, $args );
30 unset( $posted_field );
31 }
32
33 if ( empty( $errors ) ) {
34 self::spam_check( $exclude, $values, $errors );
35 }
36
37 $errors = apply_filters( 'frm_validate_entry', $errors, $values, compact( 'exclude' ) );
38
39 return $errors;
40 }
41
42 private static function set_item_key( &$values ) {
43 if ( ! isset( $values['item_key'] ) || $values['item_key'] == '' ) {
44 global $wpdb;
45 $values['item_key'] = FrmAppHelper::get_unique_key( '', $wpdb->prefix . 'frm_items', 'item_key' );
46 $_POST['item_key'] = $values['item_key'];
47 }
48 }
49
50 private static function get_fields_to_validate( $values, $exclude ) {
51 $where = apply_filters( 'frm_posted_field_ids', array( 'fi.form_id' => $values['form_id'] ) );
52
53 // Don't get subfields
54 $where['fr.parent_form_id'] = array( null, 0 );
55
56 // Don't get excluded fields (like file upload fields in the ajax validation)
57 if ( ! empty( $exclude ) ) {
58 $where['fi.type not'] = $exclude;
59 }
60
61 return FrmField::getAll( $where, 'field_order' );
62 }
63
64 public static function validate_field( $posted_field, &$errors, $values, $args = array() ) {
65 $defaults = array(
66 'id' => $posted_field->id,
67 'parent_field_id' => '', // the id of the repeat or embed form
68 'key_pointer' => '', // the pointer in the posted array
69 'exclude' => array(), // exclude these field types from validation
70 );
71 $args = wp_parse_args( $args, $defaults );
72
73 if ( empty( $args['parent_field_id'] ) ) {
74 $value = isset( $values['item_meta'][ $args['id'] ] ) ? $values['item_meta'][ $args['id'] ] : '';
75 } else {
76 // value is from a nested form
77 $value = $values;
78 }
79
80 // Check for values in "Other" fields
81 FrmEntriesHelper::maybe_set_other_validation( $posted_field, $value, $args );
82
83 self::maybe_clear_value_for_default_blank_setting( $posted_field, $value );
84
85 $should_trim = is_array( $value ) && count( $value ) == 1 && isset( $value[0] ) && $posted_field->type !== 'checkbox';
86 if ( $should_trim ) {
87 $value = reset( $value );
88 }
89
90 if ( ! is_array( $value ) ) {
91 $value = trim( $value );
92 }
93
94 if ( $posted_field->required == '1' && FrmAppHelper::is_empty_value( $value ) ) {
95 $errors[ 'field' . $args['id'] ] = FrmFieldsHelper::get_error_msg( $posted_field, 'blank' );
96 } elseif ( $posted_field->type == 'text' && ! isset( $_POST['item_name'] ) ) { // WPCS: CSRF ok.
97 $_POST['item_name'] = $value;
98 }
99
100 FrmEntriesHelper::set_posted_value( $posted_field, $value, $args );
101
102 self::validate_field_types( $errors, $posted_field, $value, $args );
103
104 // Field might want to modify value before other parts of the system
105 // e.g. trim off excess values like in the case of fields with limit.
106 $value = apply_filters( 'frm_modify_posted_field_value', $value, $errors, $posted_field, $args );
107
108 if ( $value != '' ) {
109 self::validate_phone_field( $errors, $posted_field, $value, $args );
110 }
111
112 $errors = apply_filters( 'frm_validate_' . $posted_field->type . '_field_entry', $errors, $posted_field, $value, $args );
113 $errors = apply_filters( 'frm_validate_field_entry', $errors, $posted_field, $value, $args );
114 }
115
116 /**
117 * Set $value to an empty string if it matches its label
118 *
119 * @param object $field
120 * @param string $value
121 */
122 private static function maybe_clear_value_for_default_blank_setting( $field, &$value ) {
123 $position = FrmField::get_option( $field, 'label' );
124 if ( ! $position ) {
125 $position = FrmStylesController::get_style_val( 'position', $field->form_id );
126 }
127
128 if ( $position === 'inside' && FrmFieldsHelper::is_placeholder_field_type( $field->type ) && $value === $field->name ) {
129 $value = '';
130 }
131 }
132
133 public static function validate_field_types( &$errors, $posted_field, $value, $args ) {
134 $field_obj = FrmFieldFactory::get_field_object( $posted_field );
135 $args['value'] = $value;
136 $args['errors'] = $errors;
137
138 $new_errors = $field_obj->validate( $args );
139 if ( ! empty( $new_errors ) ) {
140 $errors = array_merge( $errors, $new_errors );
141 }
142 }
143
144 public static function validate_phone_field( &$errors, $field, $value, $args ) {
145 if ( $field->type == 'phone' || ( $field->type == 'text' && FrmField::is_option_true_in_object( $field, 'format' ) ) ) {
146
147 $pattern = self::phone_format( $field );
148
149 if ( ! preg_match( $pattern, $value ) ) {
150 $errors[ 'field' . $args['id'] ] = FrmFieldsHelper::get_error_msg( $field, 'invalid' );
151 }
152 }
153 }
154
155 public static function phone_format( $field ) {
156 if ( FrmField::is_option_empty( $field, 'format' ) ) {
157 $pattern = self::default_phone_format();
158 } else {
159 $pattern = FrmField::get_option( $field, 'format' );
160 }
161
162 $pattern = apply_filters( 'frm_phone_pattern', $pattern, $field );
163
164 // Create a regexp if format is not already a regexp
165 if ( strpos( $pattern, '^' ) !== 0 ) {
166 $pattern = self::create_regular_expression_from_format( $pattern );
167 }
168
169 $pattern = '/' . $pattern . '/';
170
171 return $pattern;
172 }
173
174 /**
175 * @since 3.01
176 */
177 private static function default_phone_format() {
178 return '^((\+\d{1,3}(-|.| )?\(?\d\)?(-| |.)?\d{1,5})|(\(?\d{2,6}\)?))(-|.| )?(\d{3,4})(-|.| )?(\d{4})(( x| ext)\d{1,5}){0,1}$';
179 }
180
181 /**
182 * Create a regular expression from a phone number format
183 *
184 * @since 2.02.02
185 *
186 * @param string $pattern
187 *
188 * @return string
189 */
190 private static function create_regular_expression_from_format( $pattern ) {
191 $pattern = preg_quote( $pattern );
192
193 // Firefox doesn't like escaped dashes or colons
194 $pattern = str_replace( array( '\-', '\:' ), array( '-', ':' ), $pattern );
195
196 // Switch generic values out for their regular expression
197 $pattern = preg_replace( '/\d/', '\d', $pattern );
198 $pattern = str_replace( 'A', '[A-Z]', $pattern );
199 $pattern = str_replace( 'a', '[a-zA-Z]', $pattern );
200 $pattern = str_replace( '*', 'w', $pattern );
201 $pattern = str_replace( '/', '\/', $pattern );
202
203 if ( strpos( $pattern, '\?' ) !== false ) {
204 $parts = explode( '\?', $pattern );
205 $pattern = '';
206 foreach ( $parts as $part ) {
207 if ( empty( $pattern ) ) {
208 $pattern .= $part;
209 } else {
210 $pattern .= '(' . $part . ')?';
211 }
212 }
213 }
214 $pattern = '^' . $pattern . '$';
215
216 return $pattern;
217 }
218
219 /**
220 * Check for spam
221 *
222 * @param boolean $exclude
223 * @param array $values
224 * @param array $errors by reference
225 */
226 public static function spam_check( $exclude, $values, &$errors ) {
227 if ( ! empty( $exclude ) || ! isset( $values['item_meta'] ) || empty( $values['item_meta'] ) || ! empty( $errors ) ) {
228 // only check spam if there are no other errors
229 return;
230 }
231
232 $antispam_check = self::is_antispam_check( $values['form_id'] );
233 if ( is_string( $antispam_check ) ) {
234 $errors['spam'] = $antispam_check;
235 } elseif ( self::is_honeypot_spam( $values ) || self::is_spam_bot() ) {
236 $errors['spam'] = __( 'Your entry appears to be spam!', 'formidable' );
237 } elseif ( self::blacklist_check( $values ) ) {
238 $errors['spam'] = __( 'Your entry appears to be blocked spam!', 'formidable' );
239 } elseif ( self::is_akismet_spam( $values ) && self::is_akismet_enabled_for_user( $values['form_id'] ) ) {
240 $errors['spam'] = __( 'Your entry appears to be spam!', 'formidable' );
241 }
242 }
243
244 /**
245 * @param int $form_id
246 * @return boolean
247 */
248 private static function is_antispam_check( $form_id ) {
249 $aspm = new FrmAntiSpam( $form_id );
250 return $aspm->validate();
251 }
252
253 /**
254 * @param array $values
255 * @return boolean
256 */
257 private static function is_honeypot_spam( $values ) {
258 $honeypot = new FrmHoneypot( $values['form_id'] );
259 return ! $honeypot->validate();
260 }
261
262 /**
263 * @return boolean
264 */
265 private static function is_spam_bot() {
266 $ip = FrmAppHelper::get_ip_address();
267
268 return empty( $ip );
269 }
270
271 /**
272 * @param array $values
273 * @return boolean
274 */
275 private static function is_akismet_spam( $values ) {
276 global $wpcom_api_key;
277
278 return ( is_callable( 'Akismet::http_post' ) && ( get_option( 'wordpress_api_key' ) || $wpcom_api_key ) && self::akismet( $values ) );
279 }
280
281 /**
282 * @param int $form_id
283 * @return bool
284 */
285 private static function is_akismet_enabled_for_user( $form_id ) {
286 $form = FrmForm::getOne( $form_id );
287
288 return ( isset( $form->options['akismet'] ) && ! empty( $form->options['akismet'] ) && ( $form->options['akismet'] !== 'logged' || ! is_user_logged_in() ) );
289 }
290
291 public static function blacklist_check( $values ) {
292 if ( ! apply_filters( 'frm_check_blacklist', true, $values ) ) {
293 return false;
294 }
295
296 $mod_keys = trim( self::get_disallowed_words() );
297 if ( empty( $mod_keys ) ) {
298 return false;
299 }
300
301 $content = FrmEntriesHelper::entry_array_to_string( $values );
302 if ( empty( $content ) ) {
303 return false;
304 }
305
306 $ip = FrmAppHelper::get_ip_address();
307 $user_agent = FrmAppHelper::get_server_value( 'HTTP_USER_AGENT' );
308 $user_info = self::get_spam_check_user_info( $values );
309
310 return self::check_disallowed_words( $user_info['comment_author'], $user_info['comment_author_email'], $user_info['comment_author_url'], $content, $ip, $user_agent );
311 }
312
313 /**
314 * For WP 5.5 compatibility.
315 *
316 * @since 4.06.02
317 */
318 private static function check_disallowed_words( $author, $email, $url, $content, $ip, $user_agent ) {
319 if ( function_exists( 'wp_check_comment_disallowed_list' ) ) {
320 return wp_check_comment_disallowed_list( $author, $email, $url, $content, $ip, $user_agent );
321 } else {
322 return wp_blacklist_check( $author, $email, $url, $content, $ip, $user_agent );
323 }
324 }
325
326 /**
327 * For WP 5.5 compatibility.
328 *
329 * @since 4.06.02
330 */
331 private static function get_disallowed_words() {
332 $keys = get_option( 'disallowed_keys' );
333 if ( false === $keys ) {
334 // Fallback for WP < 5.5.
335 $keys = get_option( 'blacklist_keys' );
336 }
337 return $keys;
338 }
339
340 /**
341 * Check entries for Akismet spam
342 *
343 * @return boolean true if is spam
344 */
345 public static function akismet( $values ) {
346 $content = FrmEntriesHelper::entry_array_to_string( $values );
347 if ( empty( $content ) ) {
348 return false;
349 }
350
351 $datas = array(
352 'comment_type' => 'formidable',
353 'comment_content' => $content,
354 );
355 self::parse_akismet_array( $datas, $values );
356
357 $query_string = _http_build_query( $datas, '', '&' );
358 $response = Akismet::http_post( $query_string, 'comment-check' );
359
360 return ( is_array( $response ) && $response[1] == 'true' );
361 }
362
363 /**
364 * @since 2.0
365 */
366 private static function parse_akismet_array( &$datas, $values ) {
367 self::add_site_info_to_akismet( $datas );
368 self::add_user_info_to_akismet( $datas, $values );
369 self::add_server_values_to_akismet( $datas );
370 }
371
372 private static function add_site_info_to_akismet( &$datas ) {
373 $datas['blog'] = FrmAppHelper::site_url();
374 $datas['user_ip'] = preg_replace( '/[^0-9., ]/', '', FrmAppHelper::get_ip_address() );
375 $datas['user_agent'] = FrmAppHelper::get_server_value( 'HTTP_USER_AGENT' );
376 $datas['referrer'] = isset( $_SERVER['HTTP_REFERER'] ) ? FrmAppHelper::get_server_value( 'HTTP_REFERER' ) : false;
377 $datas['blog_lang'] = get_locale();
378 $datas['blog_charset'] = get_option( 'blog_charset' );
379
380 if ( akismet_test_mode() ) {
381 $datas['is_test'] = 'true';
382 }
383 }
384
385 private static function add_user_info_to_akismet( &$datas, $values ) {
386 $user_info = self::get_spam_check_user_info( $values );
387 $datas = $datas + $user_info;
388
389 if ( isset( $user_info['user_ID'] ) ) {
390 $datas['user_role'] = Akismet::get_user_roles( $user_info['user_ID'] );
391 }
392 }
393
394 private static function get_spam_check_user_info( $values ) {
395 $datas = array();
396
397 if ( is_user_logged_in() ) {
398 $user = wp_get_current_user();
399
400 $datas['user_ID'] = $user->ID;
401 $datas['user_id'] = $user->ID;
402 $datas['comment_author'] = $user->display_name;
403 $datas['comment_author_email'] = $user->user_email;
404 $datas['comment_author_url'] = $user->user_url;
405 } else {
406 $datas['comment_author'] = '';
407 $datas['comment_author_email'] = '';
408 $datas['comment_author_url'] = '';
409
410 if ( isset( $values['item_meta'] ) ) {
411 $values = $values['item_meta'];
412 }
413
414 $values = array_filter( $values );
415 foreach ( $values as $value ) {
416 if ( ! is_array( $value ) ) {
417 if ( $datas['comment_author_email'] == '' && strpos( $value, '@' ) && is_email( $value ) ) {
418 $datas['comment_author_email'] = $value;
419 } elseif ( $datas['comment_author_url'] == '' && strpos( $value, 'http' ) === 0 ) {
420 $datas['comment_author_url'] = $value;
421 } elseif ( $datas['comment_author'] == '' && ! is_numeric( $value ) && strlen( $value ) < 200 ) {
422 $datas['comment_author'] = $value;
423 }
424 }
425 }
426 }
427
428 return $datas;
429 }
430
431 private static function add_server_values_to_akismet( &$datas ) {
432 foreach ( $_SERVER as $key => $value ) {
433 $include_value = is_string( $value ) && ! preg_match( '/^HTTP_COOKIE/', $key ) && preg_match( '/^(HTTP_|REMOTE_ADDR|REQUEST_URI|DOCUMENT_URI)/', $key );
434
435 // Send any potentially useful $_SERVER vars, but avoid sending junk we don't need.
436 if ( $include_value ) {
437 $datas[ $key ] = $value;
438 }
439 unset( $key, $value );
440 }
441 }
442
443 /**
444 * @deprecated 3.0
445 * @codeCoverageIgnore
446 */
447 public static function validate_url_field( &$errors, $field, $value, $args ) {
448 FrmDeprecated::validate_url_field( $errors, $field, $value, $args );
449 }
450
451 /**
452 * @deprecated 3.0
453 * @codeCoverageIgnore
454 */
455 public static function validate_email_field( &$errors, $field, $value, $args ) {
456 FrmDeprecated::validate_email_field( $errors, $field, $value, $args );
457 }
458
459 /**
460 * @deprecated 3.0
461 * @codeCoverageIgnore
462 */
463 public static function validate_number_field( &$errors, $field, $value, $args ) {
464 FrmDeprecated::validate_number_field( $errors, $field, $value, $args );
465 }
466
467 /**
468 * @deprecated 3.0
469 * @codeCoverageIgnore
470 */
471 public static function validate_recaptcha( &$errors, $field, $args ) {
472 FrmDeprecated::validate_recaptcha( $errors, $field, $args );
473 }
474 }
475