1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
class FrmEntryValidate { |
4
|
|
|
public static function validate( $values, $exclude = false ) { |
5
|
|
|
global $wpdb; |
6
|
|
|
|
7
|
|
|
FrmEntry::sanitize_entry_post( $values ); |
8
|
|
|
$errors = array(); |
9
|
|
|
|
10
|
|
|
if ( ! isset($values['form_id']) || ! isset($values['item_meta']) ) { |
11
|
|
|
$errors['form'] = __( 'There was a problem with your submission. Please try again.', 'formidable' ); |
12
|
|
|
return $errors; |
13
|
|
|
} |
14
|
|
|
|
15
|
|
|
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' ) ) ) { |
16
|
|
|
$errors['form'] = __( 'You do not have permission to do that', 'formidable' ); |
17
|
|
|
} |
18
|
|
|
|
19
|
|
View Code Duplication |
if ( ! isset($values['item_key']) || $values['item_key'] == '' ) { |
|
|
|
|
20
|
|
|
$_POST['item_key'] = $values['item_key'] = FrmAppHelper::get_unique_key( '', $wpdb->prefix . 'frm_items', 'item_key' ); |
21
|
|
|
} |
22
|
|
|
|
23
|
|
|
$where = apply_filters('frm_posted_field_ids', array( 'fi.form_id' => $values['form_id'] ) ); |
24
|
|
|
// Don't get subfields |
25
|
|
|
$where['fr.parent_form_id'] = array( null, 0 ); |
26
|
|
|
// Don't get excluded fields (like file upload fields in the ajax validation) |
27
|
|
|
if ( ! empty( $exclude ) ) { |
28
|
|
|
$where['fi.type not'] = $exclude; |
29
|
|
|
} |
30
|
|
|
|
31
|
|
|
$posted_fields = FrmField::getAll($where, 'field_order'); |
32
|
|
|
|
33
|
|
|
// Pass exclude value to validate_field function so it can be used for repeating sections |
34
|
|
|
$args = array( 'exclude' => $exclude ); |
35
|
|
|
|
36
|
|
|
foreach ( $posted_fields as $posted_field ) { |
37
|
|
|
self::validate_field($posted_field, $errors, $values, $args); |
38
|
|
|
unset($posted_field); |
39
|
|
|
} |
40
|
|
|
|
41
|
|
|
// check for spam |
42
|
|
|
self::spam_check( $exclude, $values, $errors ); |
43
|
|
|
|
44
|
|
|
$errors = apply_filters( 'frm_validate_entry', $errors, $values, compact('exclude') ); |
45
|
|
|
|
46
|
|
|
return $errors; |
47
|
|
|
} |
48
|
|
|
|
49
|
|
|
public static function validate_field( $posted_field, &$errors, $values, $args = array() ) { |
50
|
|
|
$defaults = array( |
51
|
|
|
'id' => $posted_field->id, |
52
|
|
|
'parent_field_id' => '', // the id of the repeat or embed form |
53
|
|
|
'key_pointer' => '', // the pointer in the posted array |
54
|
|
|
'exclude' => array(), // exclude these field types from validation |
55
|
|
|
); |
56
|
|
|
$args = wp_parse_args( $args, $defaults ); |
57
|
|
|
|
58
|
|
|
if ( empty($args['parent_field_id']) ) { |
59
|
|
|
$value = isset( $values['item_meta'][ $args['id'] ] ) ? $values['item_meta'][ $args['id'] ] : ''; |
60
|
|
|
} else { |
61
|
|
|
// value is from a nested form |
62
|
|
|
$value = $values; |
63
|
|
|
} |
64
|
|
|
|
65
|
|
|
// Check for values in "Other" fields |
66
|
|
|
FrmEntriesHelper::maybe_set_other_validation( $posted_field, $value, $args ); |
67
|
|
|
|
68
|
|
|
self::maybe_clear_value_for_default_blank_setting( $posted_field, $value ); |
69
|
|
|
|
70
|
|
|
// Reset arrays with only one value if it's not a field where array keys need to be preserved |
71
|
|
View Code Duplication |
if ( is_array($value) && count( $value ) == 1 && isset( $value[0] ) ) { |
|
|
|
|
72
|
|
|
$value = reset($value); |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
if ( $posted_field->required == '1' && ! is_array( $value ) && trim( $value ) == '' ) { |
76
|
|
|
$errors[ 'field' . $args['id'] ] = FrmFieldsHelper::get_error_msg( $posted_field, 'blank' ); |
77
|
|
|
} else if ( $posted_field->type == 'text' && ! isset( $_POST['item_name'] ) ) { |
78
|
|
|
$_POST['item_name'] = $value; |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
if ( $value != '' ) { |
82
|
|
|
self::validate_url_field( $errors, $posted_field, $value, $args ); |
83
|
|
|
self::validate_email_field( $errors, $posted_field, $value, $args ); |
84
|
|
|
self::validate_number_field( $errors, $posted_field, $value, $args ); |
85
|
|
|
self::validate_phone_field( $errors, $posted_field, $value, $args ); |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
FrmEntriesHelper::set_posted_value($posted_field, $value, $args); |
89
|
|
|
|
90
|
|
|
self::validate_recaptcha($errors, $posted_field, $args); |
91
|
|
|
|
92
|
|
|
$errors = apply_filters( 'frm_validate_' . $posted_field->type . '_field_entry', $errors, $posted_field, $value, $args ); |
93
|
|
|
$errors = apply_filters( 'frm_validate_field_entry', $errors, $posted_field, $value, $args ); |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
private static function maybe_clear_value_for_default_blank_setting( $field, &$value ) { |
97
|
|
|
if ( FrmField::is_option_true_in_object( $field, 'default_blank' ) && $value == $field->default_value ) { |
98
|
|
|
$value = ''; |
99
|
|
|
} |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
public static function validate_url_field( &$errors, $field, &$value, $args ) { |
103
|
|
|
if ( $value == '' || ! in_array( $field->type, array( 'website', 'url', 'image' ) ) ) { |
104
|
|
|
return; |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
if ( trim($value) == 'http://' ) { |
108
|
|
|
$value = ''; |
109
|
|
|
} else { |
110
|
|
|
$value = esc_url_raw( $value ); |
111
|
|
|
$value = preg_match( '/^(https?|ftps?|mailto|news|feed|telnet):/is', $value ) ? $value : 'http://' . $value; |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
// validate the url format |
115
|
|
View Code Duplication |
if ( ! preg_match('/^http(s)?:\/\/(?:localhost|(?:[\da-z\.-]+\.[\da-z\.-]+))/i', $value) ) { |
|
|
|
|
116
|
|
|
$errors[ 'field' . $args['id'] ] = FrmFieldsHelper::get_error_msg( $field, 'invalid' ); |
117
|
|
|
} |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
public static function validate_email_field( &$errors, $field, $value, $args ) { |
121
|
|
|
if ( $value == '' || $field->type != 'email' ) { |
122
|
|
|
return; |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
//validate the email format |
126
|
|
View Code Duplication |
if ( ! is_email($value) ) { |
|
|
|
|
127
|
|
|
$errors[ 'field' . $args['id'] ] = FrmFieldsHelper::get_error_msg( $field, 'invalid' ); |
128
|
|
|
} |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
public static function validate_number_field( &$errors, $field, $value, $args ) { |
132
|
|
|
//validate the number format |
133
|
|
|
if ( $field->type != 'number' ) { |
134
|
|
|
return; |
135
|
|
|
} |
136
|
|
|
|
137
|
|
View Code Duplication |
if ( ! is_numeric( $value) ) { |
|
|
|
|
138
|
|
|
$errors[ 'field' . $args['id'] ] = FrmFieldsHelper::get_error_msg( $field, 'invalid' ); |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
// validate number settings |
142
|
|
|
if ( $value != '' ) { |
143
|
|
|
$frm_settings = FrmAppHelper::get_settings(); |
144
|
|
|
// only check if options are available in settings |
145
|
|
|
if ( $frm_settings->use_html && isset( $field->field_options['minnum'] ) && isset( $field->field_options['maxnum'] ) ) { |
146
|
|
|
//minnum maxnum |
147
|
|
|
if ( (float) $value < $field->field_options['minnum'] ) { |
148
|
|
|
$errors[ 'field' . $args['id'] ] = __( 'Please select a higher number', 'formidable' ); |
149
|
|
|
} else if ( (float) $value > $field->field_options['maxnum'] ) { |
150
|
|
|
$errors[ 'field' . $args['id'] ] = __( 'Please select a lower number', 'formidable' ); |
151
|
|
|
} |
152
|
|
|
} |
153
|
|
|
} |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
public static function validate_phone_field( &$errors, $field, $value, $args ) { |
157
|
|
|
if ( $field->type == 'phone' || ( $field->type == 'text' && FrmField::is_option_true_in_object( $field, 'format' ) ) ) { |
158
|
|
|
|
159
|
|
|
$pattern = self::phone_format( $field ); |
160
|
|
|
|
161
|
|
View Code Duplication |
if ( ! preg_match( $pattern, $value ) ) { |
|
|
|
|
162
|
|
|
$errors[ 'field' . $args['id'] ] = FrmFieldsHelper::get_error_msg( $field, 'invalid' ); |
163
|
|
|
} |
164
|
|
|
} |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
public static function phone_format( $field ) { |
168
|
|
|
$default_format = '^((\+\d{1,3}(-|.| )?\(?\d\)?(-| |.)?\d{1,5})|(\(?\d{2,6}\)?))(-|.| )?(\d{3,4})(-|.| )?(\d{4})(( x| ext)\d{1,5}){0,1}$'; |
169
|
|
|
if ( FrmField::is_option_empty( $field, 'format' ) ) { |
170
|
|
|
$pattern = $default_format; |
171
|
|
|
} else { |
172
|
|
|
$pattern = FrmField::get_option( $field, 'format' ); |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
$pattern = apply_filters( 'frm_phone_pattern', $pattern, $field ); |
176
|
|
|
|
177
|
|
|
// Create a regexp if format is not already a regexp |
178
|
|
|
if ( strpos( $pattern, '^' ) !== 0 ) { |
179
|
|
|
$pattern = self::create_regular_expression_from_format( $pattern ); |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
$pattern = '/' . $pattern . '/'; |
183
|
|
|
return $pattern; |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
/** |
187
|
|
|
* Create a regular expression from a phone number format |
188
|
|
|
* |
189
|
|
|
* @since 2.02.02 |
190
|
|
|
* @param string $pattern |
191
|
|
|
* @return string |
192
|
|
|
*/ |
193
|
|
|
private static function create_regular_expression_from_format( $pattern ) { |
194
|
|
|
$pattern = preg_quote( $pattern ); |
195
|
|
|
|
196
|
|
|
// Firefox doesn't like escaped dashes or colons |
197
|
|
|
$pattern = str_replace( array( '\-', '\:' ), array( '-', ':' ), $pattern ); |
198
|
|
|
|
199
|
|
|
// Switch generic values out for their regular expression |
200
|
|
|
$pattern = preg_replace( '/\d/', '\d', $pattern ); |
201
|
|
|
$pattern = str_replace( 'a', '[a-z]', $pattern ); |
202
|
|
|
$pattern = str_replace( 'A', '[A-Z]', $pattern ); |
203
|
|
|
$pattern = str_replace( '*', 'w', $pattern ); |
204
|
|
|
$pattern = str_replace( '/', '\/', $pattern ); |
205
|
|
|
|
206
|
|
|
if ( strpos( $pattern, '\?' ) !== false ) { |
207
|
|
|
$parts = explode( '\?', $pattern ); |
208
|
|
|
$pattern = ''; |
209
|
|
|
foreach ( $parts as $part ) { |
210
|
|
|
if ( empty( $pattern ) ) { |
211
|
|
|
$pattern .= $part; |
212
|
|
|
} else { |
213
|
|
|
$pattern .= '(' . $part . ')?'; |
214
|
|
|
} |
215
|
|
|
} |
216
|
|
|
} |
217
|
|
|
$pattern = '^' . $pattern . '$'; |
218
|
|
|
|
219
|
|
|
return $pattern; |
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
public static function validate_recaptcha( &$errors, $field, $args ) { |
223
|
|
|
if ( $field->type != 'captcha' || FrmAppHelper::is_admin() || apply_filters( 'frm_is_field_hidden', false, $field, stripslashes_deep( $_POST ) ) ) { |
|
|
|
|
224
|
|
|
return; |
225
|
|
|
} |
226
|
|
|
|
227
|
|
|
$frm_settings = FrmAppHelper::get_settings(); |
228
|
|
|
if ( empty( $frm_settings->pubkey ) ) { |
229
|
|
|
// don't require the captcha if it shouldn't be shown |
230
|
|
|
return; |
231
|
|
|
} |
232
|
|
|
|
233
|
|
|
if ( ! isset($_POST['g-recaptcha-response']) ) { |
234
|
|
|
// If captcha is missing, check if it was already verified |
235
|
|
|
if ( ! isset( $_POST['recaptcha_checked'] ) || ! wp_verify_nonce( $_POST['recaptcha_checked'], 'frm_ajax' ) ) { |
236
|
|
|
// There was no captcha submitted |
237
|
|
|
$errors[ 'field' . $args['id'] ] = __( 'The captcha is missing from this form', 'formidable' ); |
238
|
|
|
} |
239
|
|
|
return; |
240
|
|
|
} |
241
|
|
|
|
242
|
|
|
$arg_array = array( |
243
|
|
|
'body' => array( |
244
|
|
|
'secret' => $frm_settings->privkey, |
245
|
|
|
'response' => $_POST['g-recaptcha-response'], |
|
|
|
|
246
|
|
|
'remoteip' => FrmAppHelper::get_ip_address(), |
247
|
|
|
), |
248
|
|
|
); |
249
|
|
|
$resp = wp_remote_post( 'https://www.google.com/recaptcha/api/siteverify', $arg_array ); |
250
|
|
|
$response = json_decode(wp_remote_retrieve_body( $resp ), true); |
251
|
|
|
|
252
|
|
|
if ( isset( $response['success'] ) && ! $response['success'] ) { |
253
|
|
|
// What happens when the CAPTCHA was entered incorrectly |
254
|
|
|
$errors[ 'field' . $args['id'] ] = ( ! isset( $field->field_options['invalid'] ) || $field->field_options['invalid'] == '' ) ? $frm_settings->re_msg : $field->field_options['invalid']; |
255
|
|
|
} else if ( is_wp_error( $resp ) ) { |
256
|
|
|
$error_string = $resp->get_error_message(); |
257
|
|
|
$errors[ 'field' . $args['id'] ] = __( 'There was a problem verifying your recaptcha', 'formidable' ); |
258
|
|
|
$errors[ 'field' . $args['id'] ] .= ' ' . $error_string; |
259
|
|
|
} |
260
|
|
|
} |
261
|
|
|
|
262
|
|
|
/** |
263
|
|
|
* check for spam |
264
|
|
|
* @param boolean $exclude |
265
|
|
|
* @param array $values |
266
|
|
|
* @param array $errors by reference |
267
|
|
|
*/ |
268
|
|
|
public static function spam_check( $exclude, $values, &$errors ) { |
269
|
|
|
if ( ! empty( $exclude ) || ! isset( $values['item_meta'] ) || empty( $values['item_meta'] ) || ! empty( $errors ) ) { |
270
|
|
|
// only check spam if there are no other errors |
271
|
|
|
return; |
272
|
|
|
} |
273
|
|
|
|
274
|
|
|
if ( self::is_akismet_spam( $values ) ) { |
275
|
|
|
if ( self::is_akismet_enabled_for_user( $values['form_id'] ) ) { |
276
|
|
|
$errors['spam'] = __( 'Your entry appears to be spam!', 'formidable' ); |
277
|
|
|
} |
278
|
|
|
} |
279
|
|
|
|
280
|
|
|
if ( self::blacklist_check( $values ) ) { |
281
|
|
|
$errors['spam'] = __( 'Your entry appears to be spam!', 'formidable' ); |
282
|
|
|
} |
283
|
|
|
} |
284
|
|
|
|
285
|
|
|
private static function is_akismet_spam( $values ) { |
286
|
|
|
global $wpcom_api_key; |
287
|
|
|
return ( is_callable('Akismet::http_post') && ( get_option('wordpress_api_key') || $wpcom_api_key ) && self::akismet( $values ) ); |
288
|
|
|
} |
289
|
|
|
|
290
|
|
|
private static function is_akismet_enabled_for_user( $form_id ) { |
291
|
|
|
$form = FrmForm::getOne( $form_id ); |
292
|
|
|
return ( isset( $form->options['akismet'] ) && ! empty( $form->options['akismet'] ) && ( $form->options['akismet'] != 'logged' || ! is_user_logged_in() ) ); |
293
|
|
|
} |
294
|
|
|
|
295
|
|
|
public static function blacklist_check( $values ) { |
296
|
|
|
if ( ! apply_filters('frm_check_blacklist', true, $values) ) { |
297
|
|
|
return false; |
298
|
|
|
} |
299
|
|
|
|
300
|
|
|
$mod_keys = trim( get_option( 'blacklist_keys' ) ); |
301
|
|
|
|
302
|
|
|
if ( empty( $mod_keys ) ) { |
303
|
|
|
return false; |
304
|
|
|
} |
305
|
|
|
|
306
|
|
|
$content = FrmEntriesHelper::entry_array_to_string($values); |
307
|
|
|
|
308
|
|
|
if ( empty($content) ) { |
309
|
|
|
return false; |
310
|
|
|
} |
311
|
|
|
|
312
|
|
|
$content = strtolower( $content ); |
313
|
|
|
$content_without_html = wp_strip_all_tags( $content ); |
314
|
|
|
$ip = FrmAppHelper::get_ip_address(); |
315
|
|
|
$user_agent = FrmAppHelper::get_server_value( 'HTTP_USER_AGENT' ); |
316
|
|
|
|
317
|
|
|
$words = explode( "\n", $mod_keys ); |
318
|
|
|
|
319
|
|
|
foreach ( (array) $words as $word ) { |
320
|
|
|
$word = trim( $word ); |
321
|
|
|
|
322
|
|
|
if ( empty( $word ) ) { |
323
|
|
|
continue; |
324
|
|
|
} |
325
|
|
|
|
326
|
|
|
// Do some escaping magic so that '#' chars in the |
327
|
|
|
// spam words don't break things: |
328
|
|
|
$word = preg_quote( $word, '#' ); |
329
|
|
|
|
330
|
|
|
$pattern = "#$word#i"; |
331
|
|
|
|
332
|
|
|
$in_content = preg_match( $pattern, $content ) || preg_match( $pattern, $content_without_html ); |
333
|
|
|
$blacklist_user = preg_match( $pattern, $ip ) || preg_match( $pattern, $user_agent ); |
334
|
|
|
if ( $in_content || $blacklist_user ) { |
335
|
|
|
return true; |
336
|
|
|
} |
337
|
|
|
} |
338
|
|
|
|
339
|
|
|
return false; |
340
|
|
|
} |
341
|
|
|
|
342
|
|
|
/** |
343
|
|
|
* Check entries for spam |
344
|
|
|
* |
345
|
|
|
* @return boolean true if is spam |
346
|
|
|
*/ |
347
|
|
|
public static function akismet( $values ) { |
348
|
|
|
$content = FrmEntriesHelper::entry_array_to_string( $values ); |
349
|
|
|
|
350
|
|
|
if ( empty( $content ) ) { |
351
|
|
|
return false; |
352
|
|
|
} |
353
|
|
|
|
354
|
|
|
$datas = array(); |
355
|
|
|
self::parse_akismet_array( $datas, $content ); |
356
|
|
|
|
357
|
|
|
$query_string = ''; |
358
|
|
|
foreach ( $datas as $key => $data ) { |
359
|
|
|
$query_string .= $key . '=' . urlencode( stripslashes( $data ) ) . '&'; |
360
|
|
|
unset( $key, $data ); |
361
|
|
|
} |
362
|
|
|
|
363
|
|
|
$response = Akismet::http_post($query_string, 'comment-check'); |
364
|
|
|
|
365
|
|
|
return ( is_array( $response ) && $response[1] == 'true' ); |
366
|
|
|
} |
367
|
|
|
|
368
|
|
|
/** |
369
|
|
|
* @since 2.0 |
370
|
|
|
* @param string $content |
371
|
|
|
*/ |
372
|
|
|
private static function parse_akismet_array( &$datas, $content ) { |
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['comment_type'] = 'formidable'; |
378
|
|
|
$datas['comment_content'] = $content; |
379
|
|
|
|
380
|
|
|
if ( $permalink = get_permalink() ) { |
381
|
|
|
$datas['permalink'] = $permalink; |
382
|
|
|
} |
383
|
|
|
|
384
|
|
|
foreach ( $_SERVER as $key => $value ) { |
385
|
|
|
if ( ! in_array( $key, array( 'HTTP_COOKIE', 'HTTP_COOKIE2', 'PHP_AUTH_PW' ) ) && is_string( $value ) ) { |
386
|
|
|
$datas[ $key ] = wp_strip_all_tags( $value ); |
387
|
|
|
} else { |
388
|
|
|
$datas[ $key ] = ''; |
389
|
|
|
} |
390
|
|
|
|
391
|
|
|
unset($key, $value); |
392
|
|
|
} |
393
|
|
|
} |
394
|
|
|
} |
395
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.