PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 4.07.01
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v4.07.01
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.07.01, at classes/models/FrmEntryValidate.php

453 lines 14.2 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 private static function maybe_clear_value_for_default_blank_setting( $field, &$value ) {
117 $placeholder = FrmField::get_option( $field, 'placeholder' );
118 $is_default = ( ! empty( $placeholder ) && $value == $placeholder );
119 $is_label = false;
120
121 if ( ! $is_default ) {
122 $position = FrmField::get_option( $field, 'label' );
123 if ( empty( $position ) ) {
124 $position = FrmStylesController::get_style_val( 'position', $field->form_id );
125 }
126
127 $is_label = ( $position == 'inside' && FrmFieldsHelper::is_placeholder_field_type( $field->type ) && $value == $field->name );
128 }
129
130 if ( $is_label || $is_default ) {
131 $value = '';
132 }
133 }
134
135 public static function validate_field_types( &$errors, $posted_field, $value, $args ) {
136 $field_obj = FrmFieldFactory::get_field_object( $posted_field );
137 $args['value'] = $value;
138 $args['errors'] = $errors;
139
140 $new_errors = $field_obj->validate( $args );
141 if ( ! empty( $new_errors ) ) {
142 $errors = array_merge( $errors, $new_errors );
143 }
144 }
145
146 public static function validate_phone_field( &$errors, $field, $value, $args ) {
147 if ( $field->type == 'phone' || ( $field->type == 'text' && FrmField::is_option_true_in_object( $field, 'format' ) ) ) {
148
149 $pattern = self::phone_format( $field );
150
151 if ( ! preg_match( $pattern, $value ) ) {
152 $errors[ 'field' . $args['id'] ] = FrmFieldsHelper::get_error_msg( $field, 'invalid' );
153 }
154 }
155 }
156
157 public static function phone_format( $field ) {
158 if ( FrmField::is_option_empty( $field, 'format' ) ) {
159 $pattern = self::default_phone_format();
160 } else {
161 $pattern = FrmField::get_option( $field, 'format' );
162 }
163
164 $pattern = apply_filters( 'frm_phone_pattern', $pattern, $field );
165
166 // Create a regexp if format is not already a regexp
167 if ( strpos( $pattern, '^' ) !== 0 ) {
168 $pattern = self::create_regular_expression_from_format( $pattern );
169 }
170
171 $pattern = '/' . $pattern . '/';
172
173 return $pattern;
174 }
175
176 /**
177 * @since 3.01
178 */
179 private static function default_phone_format() {
180 return '^((\+\d{1,3}(-|.| )?\(?\d\)?(-| |.)?\d{1,5})|(\(?\d{2,6}\)?))(-|.| )?(\d{3,4})(-|.| )?(\d{4})(( x| ext)\d{1,5}){0,1}$';
181 }
182
183 /**
184 * Create a regular expression from a phone number format
185 *
186 * @since 2.02.02
187 *
188 * @param string $pattern
189 *
190 * @return string
191 */
192 private static function create_regular_expression_from_format( $pattern ) {
193 $pattern = preg_quote( $pattern );
194
195 // Firefox doesn't like escaped dashes or colons
196 $pattern = str_replace( array( '\-', '\:' ), array( '-', ':' ), $pattern );
197
198 // Switch generic values out for their regular expression
199 $pattern = preg_replace( '/\d/', '\d', $pattern );
200 $pattern = str_replace( 'A', '[A-Z]', $pattern );
201 $pattern = str_replace( 'a', '[a-zA-Z]', $pattern );
202 $pattern = str_replace( '*', 'w', $pattern );
203 $pattern = str_replace( '/', '\/', $pattern );
204
205 if ( strpos( $pattern, '\?' ) !== false ) {
206 $parts = explode( '\?', $pattern );
207 $pattern = '';
208 foreach ( $parts as $part ) {
209 if ( empty( $pattern ) ) {
210 $pattern .= $part;
211 } else {
212 $pattern .= '(' . $part . ')?';
213 }
214 }
215 }
216 $pattern = '^' . $pattern . '$';
217
218 return $pattern;
219 }
220
221 /**
222 * Check for spam
223 *
224 * @param boolean $exclude
225 * @param array $values
226 * @param array $errors by reference
227 */
228 public static function spam_check( $exclude, $values, &$errors ) {
229 if ( ! empty( $exclude ) || ! isset( $values['item_meta'] ) || empty( $values['item_meta'] ) || ! empty( $errors ) ) {
230 // only check spam if there are no other errors
231 return;
232 }
233
234 if ( self::is_honeypot_spam() || self::is_spam_bot() ) {
235 $errors['spam'] = __( 'Your entry appears to be spam!', 'formidable' );
236 }
237
238 if ( self::blacklist_check( $values ) ) {
239 $errors['spam'] = __( 'Your entry appears to be blocked spam!', 'formidable' );
240 }
241
242 if ( self::is_akismet_spam( $values ) ) {
243 if ( self::is_akismet_enabled_for_user( $values['form_id'] ) ) {
244 $errors['spam'] = __( 'Your entry appears to be spam!', 'formidable' );
245 }
246 }
247 }
248
249 private static function is_honeypot_spam() {
250 $honeypot_value = FrmAppHelper::get_param( 'frm_verify', '', 'get', 'sanitize_text_field' );
251
252 return ( $honeypot_value !== '' );
253 }
254
255 private static function is_spam_bot() {
256 $ip = FrmAppHelper::get_ip_address();
257
258 return empty( $ip );
259 }
260
261 private static function is_akismet_spam( $values ) {
262 global $wpcom_api_key;
263
264 return ( is_callable( 'Akismet::http_post' ) && ( get_option( 'wordpress_api_key' ) || $wpcom_api_key ) && self::akismet( $values ) );
265 }
266
267 private static function is_akismet_enabled_for_user( $form_id ) {
268 $form = FrmForm::getOne( $form_id );
269
270 return ( isset( $form->options['akismet'] ) && ! empty( $form->options['akismet'] ) && ( $form->options['akismet'] != 'logged' || ! is_user_logged_in() ) );
271 }
272
273 public static function blacklist_check( $values ) {
274 if ( ! apply_filters( 'frm_check_blacklist', true, $values ) ) {
275 return false;
276 }
277
278 $mod_keys = trim( self::get_disallowed_words() );
279 if ( empty( $mod_keys ) ) {
280 return false;
281 }
282
283 $content = FrmEntriesHelper::entry_array_to_string( $values );
284 if ( empty( $content ) ) {
285 return false;
286 }
287
288 $ip = FrmAppHelper::get_ip_address();
289 $user_agent = FrmAppHelper::get_server_value( 'HTTP_USER_AGENT' );
290 $user_info = self::get_spam_check_user_info( $values );
291
292 return self::check_disallowed_words( $user_info['comment_author'], $user_info['comment_author_email'], $user_info['comment_author_url'], $content, $ip, $user_agent );
293 }
294
295 /**
296 * For WP 5.5 compatibility.
297 *
298 * @since 4.06.02
299 */
300 private static function check_disallowed_words( $author, $email, $url, $content, $ip, $user_agent ) {
301 if ( function_exists( 'wp_check_comment_disallowed_list' ) ) {
302 return wp_check_comment_disallowed_list( $author, $email, $url, $content, $ip, $user_agent );
303 } else {
304 return wp_blacklist_check( $author, $email, $url, $content, $ip, $user_agent );
305 }
306 }
307
308 /**
309 * For WP 5.5 compatibility.
310 *
311 * @since 4.06.02
312 */
313 private static function get_disallowed_words() {
314 $keys = get_option( 'disallowed_keys' );
315 if ( false === $keys ) {
316 // Fallback for WP < 5.5.
317 $keys = get_option( 'blacklist_keys' );
318 }
319 return $keys;
320 }
321
322 /**
323 * Check entries for Akismet spam
324 *
325 * @return boolean true if is spam
326 */
327 public static function akismet( $values ) {
328 $content = FrmEntriesHelper::entry_array_to_string( $values );
329 if ( empty( $content ) ) {
330 return false;
331 }
332
333 $datas = array(
334 'comment_type' => 'formidable',
335 'comment_content' => $content,
336 );
337 self::parse_akismet_array( $datas, $values );
338
339 $query_string = _http_build_query( $datas, '', '&' );
340 $response = Akismet::http_post( $query_string, 'comment-check' );
341
342 return ( is_array( $response ) && $response[1] == 'true' );
343 }
344
345 /**
346 * @since 2.0
347 */
348 private static function parse_akismet_array( &$datas, $values ) {
349 self::add_site_info_to_akismet( $datas );
350 self::add_user_info_to_akismet( $datas, $values );
351 self::add_server_values_to_akismet( $datas );
352 }
353
354 private static function add_site_info_to_akismet( &$datas ) {
355 $datas['blog'] = FrmAppHelper::site_url();
356 $datas['user_ip'] = preg_replace( '/[^0-9., ]/', '', FrmAppHelper::get_ip_address() );
357 $datas['user_agent'] = FrmAppHelper::get_server_value( 'HTTP_USER_AGENT' );
358 $datas['referrer'] = isset( $_SERVER['HTTP_REFERER'] ) ? FrmAppHelper::get_server_value( 'HTTP_REFERER' ) : false;
359 $datas['blog_lang'] = get_locale();
360 $datas['blog_charset'] = get_option( 'blog_charset' );
361
362 if ( akismet_test_mode() ) {
363 $datas['is_test'] = 'true';
364 }
365 }
366
367 private static function add_user_info_to_akismet( &$datas, $values ) {
368 $user_info = self::get_spam_check_user_info( $values );
369 $datas = $datas + $user_info;
370
371 if ( isset( $user_info['user_ID'] ) ) {
372 $datas['user_role'] = Akismet::get_user_roles( $user_info['user_ID'] );
373 }
374 }
375
376 private static function get_spam_check_user_info( $values ) {
377 $datas = array();
378
379 if ( is_user_logged_in() ) {
380 $user = wp_get_current_user();
381
382 $datas['user_ID'] = $user->ID;
383 $datas['user_id'] = $user->ID;
384 $datas['comment_author'] = $user->display_name;
385 $datas['comment_author_email'] = $user->user_email;
386 $datas['comment_author_url'] = $user->user_url;
387 } else {
388 $datas['comment_author'] = '';
389 $datas['comment_author_email'] = '';
390 $datas['comment_author_url'] = '';
391
392 $values = array_filter( $values );
393 foreach ( $values as $value ) {
394 if ( ! is_array( $value ) ) {
395 if ( $datas['comment_author_email'] == '' && strpos( $value, '@' ) && is_email( $value ) ) {
396 $datas['comment_author_email'] = $value;
397 } elseif ( $datas['comment_author_url'] == '' && strpos( $value, 'http' ) === 0 ) {
398 $datas['comment_author_url'] = $value;
399 } elseif ( $datas['comment_author'] == '' && ! is_numeric( $value ) && strlen( $value ) < 200 ) {
400 $datas['comment_author'] = $value;
401 }
402 }
403 }
404 }
405
406 return $datas;
407 }
408
409 private static function add_server_values_to_akismet( &$datas ) {
410 foreach ( $_SERVER as $key => $value ) {
411 $include_value = is_string( $value ) && ! preg_match( '/^HTTP_COOKIE/', $key ) && preg_match( '/^(HTTP_|REMOTE_ADDR|REQUEST_URI|DOCUMENT_URI)/', $key );
412
413 // Send any potentially useful $_SERVER vars, but avoid sending junk we don't need.
414 if ( $include_value ) {
415 $datas[ $key ] = $value;
416 }
417 unset( $key, $value );
418 }
419 }
420
421 /**
422 * @deprecated 3.0
423 * @codeCoverageIgnore
424 */
425 public static function validate_url_field( &$errors, $field, $value, $args ) {
426 FrmDeprecated::validate_url_field( $errors, $field, $value, $args );
427 }
428
429 /**
430 * @deprecated 3.0
431 * @codeCoverageIgnore
432 */
433 public static function validate_email_field( &$errors, $field, $value, $args ) {
434 FrmDeprecated::validate_email_field( $errors, $field, $value, $args );
435 }
436
437 /**
438 * @deprecated 3.0
439 * @codeCoverageIgnore
440 */
441 public static function validate_number_field( &$errors, $field, $value, $args ) {
442 FrmDeprecated::validate_number_field( $errors, $field, $value, $args );
443 }
444
445 /**
446 * @deprecated 3.0
447 * @codeCoverageIgnore
448 */
449 public static function validate_recaptcha( &$errors, $field, $args ) {
450 FrmDeprecated::validate_recaptcha( $errors, $field, $args );
451 }
452 }
453