Completed
Push — master ( ce427e...4339a7 )
by Jamie
06:16
created

FrmEntryValidate::validate_phone_field()   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 10
Code Lines 5

Duplication

Lines 3
Ratio 30 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 5
nc 3
nop 4
dl 3
loc 10
rs 8.8571
c 1
b 0
f 0
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'] == '' ) {
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...
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] ) ) {
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...
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)?:\/\/([\da-z\.-]+)\.([\da-z\.-]+)/i', $value) ) {
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...
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) ) {
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...
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
		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 ) ) {
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...
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 ) ) ) {
0 ignored issues
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
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'],
0 ignored issues
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_POST
Loading history...
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
    	$words = explode( "\n", $mod_keys );
313
314
    	foreach ( (array) $words as $word ) {
315
    		$word = trim( $word );
316
317
    		if ( empty($word) ) {
318
    			continue;
319
    		}
320
321
    		if ( preg_match('#' . preg_quote( $word, '#' ) . '#', $content) ) {
322
    			return true;
323
    		}
324
    	}
325
326
    	return false;
327
    }
328
329
    /**
330
     * Check entries for spam
331
     *
332
     * @return boolean true if is spam
333
     */
334
    public static function akismet( $values ) {
335
	    $content = FrmEntriesHelper::entry_array_to_string( $values );
336
337
		if ( empty( $content ) ) {
338
		    return false;
339
		}
340
341
        $datas = array();
342
        self::parse_akismet_array( $datas, $content );
343
344
		$query_string = '';
345
		foreach ( $datas as $key => $data ) {
346
			$query_string .= $key . '=' . urlencode( stripslashes( $data ) ) . '&';
347
			unset( $key, $data );
348
		}
349
350
        $response = Akismet::http_post($query_string, 'comment-check');
351
352
		return ( is_array( $response ) && $response[1] == 'true' );
353
    }
354
355
    /**
356
     * @since 2.0
357
     * @param string $content
358
     */
359
    private  static function parse_akismet_array( &$datas, $content ) {
360
        $datas['blog'] = FrmAppHelper::site_url();
361
        $datas['user_ip'] = preg_replace( '/[^0-9., ]/', '', FrmAppHelper::get_ip_address() );
362
		$datas['user_agent'] = FrmAppHelper::get_server_value( 'HTTP_USER_AGENT' );
363
		$datas['referrer'] = isset( $_SERVER['HTTP_REFERER'] ) ? FrmAppHelper::get_server_value( 'HTTP_REFERER' ) : false;
364
        $datas['comment_type'] = 'formidable';
365
        $datas['comment_content'] = $content;
366
367
        if ( $permalink = get_permalink() ) {
368
            $datas['permalink'] = $permalink;
369
        }
370
371
        foreach ( $_SERVER as $key => $value ) {
372
			if ( ! in_array( $key, array( 'HTTP_COOKIE', 'HTTP_COOKIE2', 'PHP_AUTH_PW' ) ) && is_string( $value ) ) {
373
				$datas[ $key ] = wp_strip_all_tags( $value );
374
            } else {
375
				$datas[ $key ] = '';
376
            }
377
378
            unset($key, $value);
379
        }
380
    }
381
}
382