Completed
Push — master ( 12fb23...61b4ab )
by Stephanie
02:50
created

FrmEntryValidate::validate_url_field()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 5
nc 2
nop 4
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
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 View Code Duplication
		if ( is_array($value) && count( $value ) == 1 && isset( $value[0] ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
83
			$value = reset($value);
84
		}
85
86
		if ( ! is_array( $value ) ) {
87
			$value = trim( $value );
88
		}
89
90
        if ( $posted_field->required == '1' && FrmAppHelper::is_empty_value( $value ) ) {
91
			$errors[ 'field' . $args['id'] ] = FrmFieldsHelper::get_error_msg( $posted_field, 'blank' );
92
        } else if ( $posted_field->type == 'text' && ! isset( $_POST['item_name'] ) ) {
93
            $_POST['item_name'] = $value;
94
        }
95
96
		FrmEntriesHelper::set_posted_value( $posted_field, $value, $args );
97
98
		self::validate_field_types( $errors, $posted_field, $value, $args );
99
100
		if ( $value != '' ) {
101
			self::validate_phone_field( $errors, $posted_field, $value, $args );
102
		}
103
104
		$errors = apply_filters( 'frm_validate_' . $posted_field->type . '_field_entry', $errors, $posted_field, $value, $args );
105
		$errors = apply_filters( 'frm_validate_field_entry', $errors, $posted_field, $value, $args );
106
    }
107
108
	private static function maybe_clear_value_for_default_blank_setting( $field, &$value ) {
109
		$is_default = ( FrmField::is_option_true_in_object( $field, 'default_blank' ) && $value == $field->default_value );
110
		$is_label = false;
111
112
		if ( ! $is_default ) {
113
			$position = FrmField::get_option( $field, 'label' );
114
			if ( empty( $position ) ) {
115
				$position = FrmStylesController::get_style_val( 'position', $field->form_id );
116
			}
117
118
			$is_label = ( $position == 'inside' && FrmFieldsHelper::is_placeholder_field_type( $field->type ) && $value == $field->name );
119
		}
120
121
		if ( $is_label || $is_default ) {
122
			$value = '';
123
		}
124
	}
125
126
	public static function validate_field_types( &$errors, $posted_field, $value, $args ) {
127
		$field_obj = FrmFieldFactory::get_field_object( $posted_field );
128
		$args['value'] = $value;
129
		$args['errors'] = $errors;
130
131
		$new_errors = $field_obj->validate( $args );
132
		if ( ! empty( $new_errors ) ) {
133
			$errors = array_merge( $errors, $new_errors );
134
		}
135
	}
136
137
	/**
138
	 * @deprecated 3.0
139
	 */
140
	public static function validate_url_field( &$errors, $field, $value, $args ) {
141
		_deprecated_function( __FUNCTION__, '3.0', 'FrmFieldType::validate' );
142
143
		if ( $value == '' || ! in_array( $field->type, array( 'website', 'url' ) ) ) {
144
			return;
145
		}
146
147
		self::validate_field_types( $errors, $field, $value, $args );
148
	}
149
150
	/**
151
	 * @deprecated 3.0
152
	 */
153 View Code Duplication
	public static function validate_email_field( &$errors, $field, $value, $args ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
154
		_deprecated_function( __FUNCTION__, '3.0', 'FrmFieldType::validate' );
155
156
		if ( $field->type != 'email' ) {
157
			return;
158
		}
159
160
		self::validate_field_types( $errors, $field, $value, $args );
161
	}
162
163
	/**
164
	 * @deprecated 3.0
165
	 */
166 View Code Duplication
	public static function validate_number_field( &$errors, $field, $value, $args ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
167
		_deprecated_function( __FUNCTION__, '3.0', 'FrmFieldType::validate' );
168
169
		//validate the number format
170
		if ( $field->type != 'number' ) {
171
			return;
172
		}
173
174
		self::validate_field_types( $errors, $field, $value, $args );
175
	}
176
177
	public static function validate_phone_field( &$errors, $field, $value, $args ) {
178
		if ( $field->type == 'phone' || ( $field->type == 'text' && FrmField::is_option_true_in_object( $field, 'format' ) ) ) {
179
180
			$pattern = self::phone_format( $field );
181
182
			if ( ! preg_match( $pattern, $value ) ) {
183
				$errors[ 'field' . $args['id'] ] = FrmFieldsHelper::get_error_msg( $field, 'invalid' );
184
			}
185
		}
186
	}
187
188
	public static function phone_format( $field ) {
189
		if ( FrmField::is_option_empty( $field, 'format' ) ) {
190
			$pattern = self::default_phone_format();
191
		} else {
192
			$pattern = FrmField::get_option( $field, 'format' );
193
		}
194
195
		$pattern = apply_filters( 'frm_phone_pattern', $pattern, $field );
196
197
		// Create a regexp if format is not already a regexp
198
		if ( strpos( $pattern, '^' ) !== 0 ) {
199
			$pattern = self::create_regular_expression_from_format( $pattern );
200
		}
201
202
		$pattern = '/' . $pattern . '/';
203
		return $pattern;
204
	}
205
206
	/**
207
	 * @since 3.0.07
208
	 */
209
	private static function default_phone_format() {
210
		return '^((\+\d{1,3}(-|.| )?\(?\d\)?(-| |.)?\d{1,5})|(\(?\d{2,6}\)?))(-|.| )?(\d{3,4})(-|.| )?(\d{4})(( x| ext)\d{1,5}){0,1}$';
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
	/**
250
	 * @deprecated 3.0
251
	 */
252
	public static function validate_recaptcha( &$errors, $field, $args ) {
253
		_deprecated_function( __FUNCTION__, '3.0', 'FrmFieldType::validate' );
254
255
		if ( $field->type != 'captcha' ) {
256
			return;
257
		}
258
259
		self::validate_field_types( $errors, $field, '', $args );
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_honeypot_spam() || self::is_spam_bot() ) {
275
			$errors['spam'] = __( 'Your entry appears to be spam!', 'formidable' );
276
		}
277
278
    	if ( self::blacklist_check( $values ) ) {
279
            $errors['spam'] = __( 'Your entry appears to be blacklist spam!', 'formidable' );
280
    	}
281
282
        if ( self::is_akismet_spam( $values ) ) {
283
			if ( self::is_akismet_enabled_for_user( $values['form_id'] ) ) {
284
				$errors['spam'] = __( 'Your entry appears to be spam!', 'formidable' );
285
			}
286
	    }
287
    }
288
289
	private static function is_honeypot_spam() {
290
		$honeypot_value = FrmAppHelper::get_param( 'frm_verify', '', 'get', 'sanitize_text_field' );
291
		return ( $honeypot_value !== '' );
292
	}
293
294
	private static function is_spam_bot() {
295
		$ip = FrmAppHelper::get_ip_address();
296
		return empty( $ip );
297
	}
298
299
	private static function is_akismet_spam( $values ) {
300
		global $wpcom_api_key;
301
		return ( is_callable('Akismet::http_post') && ( get_option('wordpress_api_key') || $wpcom_api_key ) && self::akismet( $values ) );
302
	}
303
304
	private static function is_akismet_enabled_for_user( $form_id ) {
305
		$form = FrmForm::getOne( $form_id );
306
		return ( isset( $form->options['akismet'] ) && ! empty( $form->options['akismet'] ) && ( $form->options['akismet'] != 'logged' || ! is_user_logged_in() ) );
307
	}
308
309
    public static function blacklist_check( $values ) {
310
        if ( ! apply_filters('frm_check_blacklist', true, $values) ) {
311
            return false;
312
        }
313
314
    	$mod_keys = trim( get_option( 'blacklist_keys' ) );
315
    	if ( empty( $mod_keys ) ) {
316
    		return false;
317
    	}
318
319
		$content = FrmEntriesHelper::entry_array_to_string( $values );
320
		if ( empty( $content ) ) {
321
			return false;
322
		}
323
324
		$ip = FrmAppHelper::get_ip_address();
325
		$user_agent = FrmAppHelper::get_server_value( 'HTTP_USER_AGENT' );
326
		$user_info = self::get_spam_check_user_info( $values );
327
328
		return wp_blacklist_check( $user_info['comment_author'], $user_info['comment_author_email'], $user_info['comment_author_url'], $content, $ip, $user_agent );
329
    }
330
331
	/**
332
	 * Check entries for Akismet spam
333
	 *
334
	 * @return boolean true if is spam
335
	 */
336
	public static function akismet( $values ) {
337
		$content = FrmEntriesHelper::entry_array_to_string( $values );
338
		if ( empty( $content ) ) {
339
			return false;
340
		}
341
342
		$datas = array(
343
			'comment_type'    => 'formidable',
344
			'comment_content' => $content,
345
		);
346
		self::parse_akismet_array( $datas, $values );
347
348
		$query_string = _http_build_query( $datas, '', '&' );
349
		$response = Akismet::http_post( $query_string, 'comment-check' );
350
351
		return ( is_array( $response ) && $response[1] == 'true' );
352
	}
353
354
	/**
355
	 * @since 2.0
356
	 */
357
	private  static function parse_akismet_array( &$datas, $values ) {
358
		self::add_site_info_to_akismet( $datas );
359
		self::add_user_info_to_akismet( $datas, $values );
360
		self::add_server_values_to_akismet( $datas );
361
	}
362
363
	private static function add_site_info_to_akismet( &$datas ) {
364
		$datas['blog'] = FrmAppHelper::site_url();
365
		$datas['user_ip'] = preg_replace( '/[^0-9., ]/', '', FrmAppHelper::get_ip_address() );
366
		$datas['user_agent'] = FrmAppHelper::get_server_value( 'HTTP_USER_AGENT' );
367
		$datas['referrer'] = isset( $_SERVER['HTTP_REFERER'] ) ? FrmAppHelper::get_server_value( 'HTTP_REFERER' ) : false;
368
		$datas['blog_lang'] = get_locale();
369
		$datas['blog_charset'] = get_option('blog_charset');
370
371
		if ( akismet_test_mode() ) {
372
			$datas['is_test'] = 'true';
373
		}
374
	}
375
376
	private static function add_user_info_to_akismet( &$datas, $values ) {
377
		$user_info = self::get_spam_check_user_info( $values );
378
		$datas = $datas + $user_info;
379
380
		if ( isset( $user_info['user_ID'] ) ) {
381
			$datas['user_role'] = Akismet::get_user_roles( $user_info['user_ID'] );
382
		}
383
	}
384
385
	private static function get_spam_check_user_info( $values ) {
386
		$datas = array();
387
388
		if ( is_user_logged_in() ) {
389
			$user = wp_get_current_user();
390
			$datas['user_ID'] = $user->ID;
391
			$datas['user_id'] = $user->ID;
392
			$datas['comment_author'] = $user->display_name;
393
			$datas['comment_author_email'] = $user->user_email;
394
			$datas['comment_author_url'] = $user->user_url;
395
		} else {
396
			$datas['comment_author'] = '';
397
			$datas['comment_author_email'] = '';
398
			$datas['comment_author_url'] = '';
399
400
			$values = array_filter( $values );
401
			foreach ( $values as $value ) {
402
				if ( ! is_array( $value ) ) {
403
					if ( $datas['comment_author_email'] == '' && strpos( $value, '@' ) && is_email( $value ) ) {
404
						$datas['comment_author_email'] = $value;
405
					} elseif ( $datas['comment_author_url'] == '' && strpos( $value, 'http' ) === 0 ) {
406
						$datas['comment_author_url'] = $value;
407
					} elseif ( $datas['comment_author'] == '' && ! is_numeric( $value ) && strlen( $value ) < 200 ) {
408
						$datas['comment_author'] = $value;
409
					}
410
				}
411
			}
412
		}
413
414
		return $datas;
415
	}
416
417
	private static function add_server_values_to_akismet( &$datas ) {
418
		foreach ( $_SERVER as $key => $value ) {
419
			$include_value = is_string( $value ) && ! preg_match( '/^HTTP_COOKIE/', $key ) && preg_match( '/^(HTTP_|REMOTE_ADDR|REQUEST_URI|DOCUMENT_URI)/', $key );
420
421
			// Send any potentially useful $_SERVER vars, but avoid sending junk we don't need.
422
			if ( $include_value ) {
423
				$datas[ $key ] = $value;
424
			}
425
			unset( $key, $value );
426
		}
427
	}
428
}
429