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

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