Completed
Branch uploads (08d8c9)
by Stephanie
04:20
created

FrmAntiSpam   B

Complexity

Total Complexity 46

Size/Duplication

Total Lines 274
Duplicated Lines 5.11 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
dl 14
loc 274
rs 8.3999
c 0
b 0
f 0
wmc 46
lcom 1
cbo 1

9 Methods

Rating   Name   Duplication   Size   Complexity  
C is_spam() 0 65 14
A _is_shortest_time() 0 15 3
A _is_bbcode_spam() 0 3 1
B _is_fake_ip() 0 31 6
A _is_ipv6() 7 7 2
A _is_ipv4() 7 7 2
A _cut_ip() 0 6 3
C _is_regexp_spam() 0 85 12
B _is_dnsbl_spam() 0 25 3

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like FrmAntiSpam often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use FrmAntiSpam, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
if ( ! defined( 'ABSPATH' ) ) {
4
	die( 'You are not allowed to call this page directly.' );
5
}
6
7
class FrmAntiSpam {
8
9
	private static function is_spam( $comment ) {
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
10
		$url = $email = $author = $body = $comment; // TODO: get values from form
11
		$options = array(
12
			'time_check' => 1, 'bbcode_check' => 1,
13
			'advanced_check' => 1, 'regexp_check' => 1,
14
			'dnsbl_check' => 1,
15
		);
16
17
		$response = array( 'spam' => false );
18
19
		/* Check if logged in */
20
		if ( is_user_logged_in() ) {
21
		    return $response;
22
		}
23
24
		/* Honeypot */
25
		if ( ! empty( $_POST['ab_spam__hidden_field'] ) ) {
26
			$response['reason'] = 'css';
27
			return $response;
28
		}
29
30
		$ip = FrmAppHelper::get_ip_address();
31
		if ( empty( $ip ) ) {
32
			$response['reason'] = 'empty';
33
			return $response;
34
		}
35
36
		/* Action time */
37
		if ( $options['time_check'] && self::_is_shortest_time() ) {
38
			$response['reason'] = 'time';
39
			return $response;
40
		}
41
42
		/* BBCode Spam */
43
		if ( $options['bbcode_check'] && self::_is_bbcode_spam( $body ) ) {
44
			$response['reason'] = 'bbcode';
45
			return $response;
46
		}
47
48
		if ( $options['advanced_check'] && self::_is_fake_ip( $ip ) ) {
49
			$response['reason'] = 'server';
50
			return $response;
51
		}
52
53
		/* Regexp for Spam */
54
		if ( $options['regexp_check'] ) {
55
			$is_spam = self::_is_regexp_spam( array(
56
				'ip'	 => $ip,
57
				'host'	 => parse_url( $url, PHP_URL_HOST ),
58
				'body'	 => $body,
59
				'email'	 => $email,
60
				'author' => $author,
61
			) );
62
			if ( $is_spam ) {
63
				$response['reason'] = 'regexp';
64
				return $response;
65
			}
66
		}
67
68
		/* DNSBL Spam */
69
		if ( $options['dnsbl_check'] && self::_is_dnsbl_spam( $ip ) ) {
70
			$response['reason'] = 'dnsbl';
71
			return $response;
72
		}
73
	}
74
75
	/**
76
	* Check for form submission time
77
	*
78
	* @return  boolean    TRUE if the action time is less than 5 seconds
79
	*/
80
81
	private static function _is_shortest_time() {
82
		$too_short = false;
83
		$start_time = FrmAppHelper::get_post_param( 'ab_init_time', 0, 'absint' );
84
85
		if ( $start_time ) {
86
			// Compare time values
87
			$min_seconds = apply_filters( 'frm_spam_time_limit', 5 );
88
			$total_time = time() - $start_time;
89
			if ( $total_time < $min_seconds ) {
90
				$too_short = true;
91
			}
92
		}
93
94
		return $too_short;
95
	}
96
97
	private static function _is_bbcode_spam( $body ) {
98
		return (bool) preg_match( '/\[url[=\]].*\[\/url\]/is', $body );
99
	}
100
	
101
	private static function _is_fake_ip( $client_ip, $client_host = false ) {
102
		/* Remote Host */
103
		$host_by_ip = gethostbyaddr( $client_ip );
104
105
		/* IPv6 */
106
		if ( self::_is_ipv6( $client_ip ) ) {
107
			return $client_ip != $host_by_ip;
108
		}
109
110
		/* IPv4 */
111
		if ( empty( $client_host ) ) {
112
			$ip_by_host = gethostbyname( $host_by_ip );
113
114
			if ( $ip_by_host === $host_by_ip ) {
115
				return false;
116
			}
117
		} else {
118
			/* IPv4 / API */
119
			if ( $host_by_ip === $client_ip ) {
120
				return true;
121
			}
122
123
			$ip_by_host = gethostbyname( $client_host );
124
		}
125
126
		if ( strpos( $client_ip, self::_cut_ip( $ip_by_host ) ) === false ) {
127
			return true;
128
		}
129
130
		return false;
131
	}
132
133
	/**
134
	* Check for an IPv6 address
135
	*
136
	* @param   string   $ip  IP to validate
137
	* @return  boolean       TRUE if IPv6
138
	*/
139
140 View Code Duplication
	private static function _is_ipv6( $ip ) {
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...
141
		if ( function_exists('filter_var') ) {
142
			return filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ) !== false;
143
		} else {
144
			return ! self::_is_ipv4( $ip );
145
		}
146
	}
147
148
	/**
149
	 * Check for an IPv4 address
150
	 *
151
	 * @param   string   $ip  IP to validate
152
	 * @return  integer       TRUE if IPv4
153
	 */
154 View Code Duplication
	private static function _is_ipv4( $ip ) {
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...
155
		if ( function_exists('filter_var') ) {
156
			return filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) !== false;
157
		} else {
158
			return preg_match( '/^\d{1,3}(\.\d{1,3}){3,3}$/', $ip );
159
		}
160
	}
161
162
	private static function _cut_ip( $ip, $cut_end = true ) {
163
		$separator = ( self::_is_ipv4( $ip ) ? '.' : ':' );
164
		$part = ( $cut_end ? strrchr( $ip, $separator ) : strstr( $ip, $separator ) );
165
166
		return str_replace( $part, '', $ip );
167
	}
168
169
	private static function _is_regexp_spam( $comment ) {
170
		/* Felder */
171
		$fields = array(
172
			'ip',
173
			'host',
174
			'body',
175
			'email',
176
			'author',
177
		);
178
179
		/* Regexp */
180
		$patterns = array(
181
			0 => array(
182
				'host'	=> '^(www\.)?\d+\w+\.com$',
183
				'body'	=> '^\w+\s\d+$',
184
				'email'	=> '@gmail.com$',
185
			),
186
			1 => array(
187
				'body'	=> '\<\!.+?mfunc.+?\>',
188
			)
189
		);
190
191
		/* Spammy author */
192
		if ( $quoted_author = preg_quote( $comment['author'], '/' ) ) {
193
			$patterns[] = array(
194
				'body' => sprintf(
195
					'<a.+?>%s<\/a>$',
196
					$quoted_author
197
				)
198
			);
199
			$patterns[] = array(
200
				'body' => sprintf(
201
					'%s https?:.+?$',
202
					$quoted_author
203
				)
204
			);
205
			$patterns[] = array(
206
				'email'	 => '@gmail.com$',
207
				'author' => '^[a-z0-9-\.]+\.[a-z]{2,6}$',
208
				'host'	 => sprintf(
209
					'^%s$',
210
					$quoted_author
211
				)
212
			);
213
		}
214
215
		/* Hook */
216
		$patterns = apply_filters(
217
			'antispam_bee_patterns',
218
			$patterns
219
		);
220
221
		if ( ! $patterns ) {
222
			return false;
223
		}
224
225
		foreach ( $patterns as $pattern ) {
226
			$hits = array();
227
228
			foreach ( $pattern as $field => $regexp ) {
229
				$is_empty = ( empty( $field ) || ! in_array( $field, $fields ) || empty( $regexp ) );
230
				if ( $is_empty ) {
231
					continue;
232
				}
233
234
				/* Ignore non utf-8 chars */
235
				$comment[ $field ] = ( function_exists('iconv') ? iconv( 'utf-8', 'utf-8//TRANSLIT', $comment[ $field ] ) : $comment[ $field ] );
236
237
				if ( empty( $comment[ $field ] ) ) {
238
					continue;
239
				}
240
241
				/* Perform regex */
242
				if ( preg_match( '/' .$regexp. '/isu', $comment[ $field ] ) ) {
243
					$hits[ $field ] = true;
244
				}
245
			}
246
247
			if ( count( $hits ) === count( $pattern ) ) {
248
				return true;
249
			}
250
		}
251
252
		return false;
253
	}
254
255
	private static function _is_dnsbl_spam( $ip ) {
256
257
		$response = wp_safe_remote_request(
258
			esc_url_raw(
259
				sprintf( 'http://www.stopforumspam.com/api?ip=%s&f=json', $ip ),
260
				'http'
261
			)
262
		);
263
264
		if ( is_wp_error( $response ) ) {
265
			return false;
266
		}
267
268
		/* Get JSON */
269
		$json = wp_remote_retrieve_body( $response );
270
271
		$result = json_decode( $json );
272
273
		if ( empty( $result->success ) ) {
274
			return false;
275
		}
276
277
		$status = (bool) $result->ip->appears;
278
		return $status;
279
	}	
280
}
281