PluginProbe ʕ •ᴥ•ʔ
Hustle – Email Marketing, Lead Generation, Optins, Popups / 7.8.9
Hustle – Email Marketing, Lead Generation, Optins, Popups v7.8.9
7.8.14 7.8.14.1 7.8.13 7.8.13.1 trunk 3.0 3.1 3.1.1 3.1.2 3.1.3 3.1.4 4.3.2 4.4.4 4.4.5 4.4.5.1 4.4.5.4 4.6 4.6.1.1 4.6.1.4 4.7.0.2 4.7.0.3 4.7.0.7 4.7.0.9 4.7.1.0 4.7.1.1 4.8.0.0 5.0.0 5.0.1 5.0.1.1 5.0.1.2 5.1 5.1.1 5.1.2 5.1.3 5.1.3.1 5.1.3.2 5.1.4 5.1.5 6.0 6.0.1 6.0.2 6.0.3 6.0.4.2 6.0.5 6.0.6.1 6.0.7 6.0.8.1 6.0.9 7.0.0.1 7.0.2 7.0.3 7.0.4 7.1.0 7.1.1 7.2.0 7.2.1 7.3.0 7.3.1 7.3.3 7.3.5 7.3.6 7.3.7 7.4.0 7.4.1 7.4.11 7.4.13 7.4.13.1 7.4.2 7.4.3 7.4.4 7.4.5 7.4.5.1 7.4.5.2 7.4.6 7.4.7 7.5.0 7.6.0 7.6.1 7.6.3 7.6.4 7.6.6 7.7.0 7.7.1 7.8.0 7.8.1 7.8.10 7.8.10.1 7.8.10.2 7.8.11 7.8.12 7.8.12.1 7.8.2 7.8.3 7.8.4 7.8.5 7.8.6 7.8.7 7.8.8 7.8.9 7.8.9.1 7.8.9.2 7.8.9.3
wordpress-popup / inc / opt-in-geo.php
wordpress-popup / inc Last commit date
display-conditions 3 years ago front 10 months ago helpers 10 months ago metas 3 years ago multisite 3 years ago palettes 3 years ago provider 3 years ago providers 10 months ago templates 3 years ago update 3 years ago class-hustle-admin-page-abstract.php 10 months ago class-hustle-black-friday-campaign.php 7 months ago class-hustle-condition-factory.php 6 years ago class-hustle-cross-sell.php 10 months ago class-hustle-dashboard-admin.php 3 years ago class-hustle-data.php 3 years ago class-hustle-db.php 2 years ago class-hustle-installer.php 4 years ago class-hustle-meta.php 3 years ago class-hustle-module-admin.php 10 months ago class-hustle-module-collection.php 3 years ago class-hustle-module-page-abstract.php 2 years ago class-hustle-notifications.php 3 years ago class-hustle-settings-admin.php 3 years ago class-hustle-tutorials-page.php 4 years ago class-hustle-wp-dashboard-page.php 3 years ago hustle-deletion.php 3 years ago hustle-embedded-admin.php 3 years ago hustle-entries-admin.php 3 years ago hustle-entry-model.php 3 years ago hustle-general-data-protection.php 3 years ago hustle-init.php 7 months ago hustle-mail.php 3 years ago hustle-migration.php 3 years ago hustle-model.php 3 years ago hustle-module-model.php 10 months ago hustle-module-widget-legacy.php 3 years ago hustle-module-widget.php 3 years ago hustle-modules-common-admin-ajax.php 10 months ago hustle-popup-admin.php 3 years ago hustle-providers-admin.php 3 years ago hustle-providers.php 3 years ago hustle-settings-admin-ajax.php 3 years ago hustle-settings-page.php 3 years ago hustle-slidein-admin.php 3 years ago hustle-sshare-admin.php 3 years ago hustle-sshare-model.php 3 years ago hustle-tracking-model.php 3 years ago opt-in-geo.php 10 months ago opt-in-utils.php 3 years ago opt-in-wpmudev-api.php 3 years ago
opt-in-geo.php
461 lines
1 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2 /**
3 * Geolocation utility functions
4 *
5 * @package Hustle
6 */
7
8 /**
9 * Class Opt_In_Geo
10 */
11 class Opt_In_Geo {
12 /**
13 * Site option key
14 *
15 * @var COUNTRY_IP_MAP
16 */
17 const COUNTRY_IP_MAP = 'wpoi-county-id-map';
18
19 /**
20 * Default GeoIP provider key
21 *
22 * @var DEFAULT_GEOIP_PROVIDER
23 */
24 const DEFAULT_GEOIP_PROVIDER = 'ipwhois';
25
26 /**
27 * Tries to get the public IP address of the current user.
28 *
29 * @return string The IP Address
30 */
31 public static function get_user_ip() {
32 // check for bot.
33 if ( self::is_crawler() ) {
34 return false;
35 }
36
37 // Check if request is from CloudFlare.
38 if ( self::is_cloudflare() ) {
39 $cf_ip = filter_input( INPUT_SERVER, 'HTTP_CF_CONNECTING_IP', FILTER_VALIDATE_IP );
40 if ( $cf_ip ) {
41 return apply_filters( 'hustle_user_ip', $cf_ip );
42 }
43 }
44
45 $result = (object) array(
46 'ip' => filter_input( INPUT_SERVER, 'REMOTE_ADDR', FILTER_SANITIZE_SPECIAL_CHARS ),
47 'proxy' => false,
48 'proxy_ip' => '',
49 );
50
51 /*
52 * This code tries to bypass a proxy and get the actual IP address of
53 * the visitor behind the proxy.
54 * Warning: These values might be spoofed!
55 */
56 $ip_fields = array(
57 'HTTP_CLIENT_IP',
58 'HTTP_X_FORWARDED_FOR',
59 'HTTP_X_FORWARDED',
60 'HTTP_X_CLUSTER_CLIENT_IP',
61 'HTTP_FORWARDED_FOR',
62 'HTTP_FORWARDED',
63 'REMOTE_ADDR',
64 );
65 $forwarded = false;
66 foreach ( $ip_fields as $key ) {
67 if ( true === array_key_exists( $key, $_SERVER ) ) {
68 $ips = filter_input( INPUT_SERVER, $key );
69 if ( ! $ips ) {
70 // Skip empty or invalid values.
71 continue;
72 }
73
74 foreach ( explode( ',', $ips ) as $ip ) {
75 $ip = trim( $ip );
76
77 if ( false !== filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) ) {
78 $forwarded = $ip;
79 break 2;
80 }
81 }
82 }
83 }
84
85 // If we found a different IP address than REMOTE_ADDR then it's a proxy!
86 if ( ! empty( $forwarded ) && $forwarded !== $result->ip ) {
87 $result->proxy = true;
88 $result->proxy_ip = $result->ip;
89 $result->ip = $forwarded;
90 }
91
92 if ( $result->ip ) {
93 $user_ip = $result->ip;
94 } else {
95 $user_ip = 'UNKNOWN';
96 }
97
98 return apply_filters( 'hustle_user_ip', esc_attr( $user_ip ) );
99 }
100
101 /**
102 * Validates that the IP that made the request is from cloudflare
103 *
104 * @param String $ip - the ip to check.
105 * @return bool
106 */
107 private static function validate_cloudflare_ip( $ip ) {
108 $cloudflare_ips = array(
109 '199.27.128.0/21',
110 '173.245.48.0/20',
111 '103.21.244.0/22',
112 '103.22.200.0/22',
113 '103.31.4.0/22',
114 '141.101.64.0/18',
115 '108.162.192.0/18',
116 '190.93.240.0/20',
117 '188.114.96.0/20',
118 '197.234.240.0/22',
119 '198.41.128.0/17',
120 '162.158.0.0/15',
121 '104.16.0.0/12',
122 );
123 $is_cf_ip = false;
124 foreach ( $cloudflare_ips as $cloudflare_ip ) {
125 if ( self::cloudflare_ip_in_range( $ip, $cloudflare_ip ) ) {
126 $is_cf_ip = true;
127 break;
128 }
129 }
130
131 return $is_cf_ip;
132 }
133
134 /**
135 * Check if the cloudflare IP is in range
136 *
137 * @param String $ip - the current IP.
138 * @param String $range - the allowed range of cloudflare ips.
139 * @return bool
140 */
141 private static function cloudflare_ip_in_range( $ip, $range ) {
142 if ( strpos( $range, '/' ) === false ) {
143 $range .= '/32';
144 }
145
146 // $range is in IP/CIDR format eg 127.0.0.1/24.
147 list( $range, $netmask ) = explode( '/', $range, 2 );
148 $range_decimal = ip2long( $range );
149 $ip_decimal = ip2long( $ip );
150 $wildcard_decimal = pow( 2, ( 32 - $netmask ) ) - 1;
151 $netmask_decimal = ~$wildcard_decimal;
152
153 return ( ( $ip_decimal & $netmask_decimal ) === ( $range_decimal & $netmask_decimal ) );
154 }
155
156 /**
157 * Check if there are any cloudflare headers in the request
158 *
159 * @return bool
160 */
161 private static function cloudflare_requests_check() {
162 $flag = true;
163
164 if ( ! isset( $_SERVER['HTTP_CF_CONNECTING_IP'] ) ) {
165 $flag = false;
166 }
167 if ( ! isset( $_SERVER['HTTP_CF_IPCOUNTRY'] ) ) {
168 $flag = false;
169 }
170 if ( ! isset( $_SERVER['HTTP_CF_RAY'] ) ) {
171 $flag = false;
172 }
173 if ( ! isset( $_SERVER['HTTP_CF_VISITOR'] ) ) {
174 $flag = false;
175 }
176
177 return $flag;
178 }
179
180 /**
181 * Check if the request is from cloudflare. If it is, we get the IP
182 *
183 * @return bool
184 */
185 private static function is_cloudflare() {
186 if ( isset( $_SERVER['HTTP_CLIENT_IP'] ) ) {
187 $ip = filter_input( INPUT_SERVER, 'HTTP_CLIENT_IP' );
188 } elseif ( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
189 $ip = filter_input( INPUT_SERVER, 'HTTP_X_FORWARDED_FOR' );
190 } else {
191 $ip = filter_input( INPUT_SERVER, 'REMOTE_ADDR' );
192 }
193 if ( isset( $ip ) ) {
194 $request_check = self::cloudflare_requests_check();
195 if ( ! $request_check ) {
196 return false;
197 }
198
199 $ip_check = self::validate_cloudflare_ip( $ip );
200
201 return $ip_check;
202 }
203
204 return false;
205 }
206
207 /**
208 * Checks if the users IP address belongs to a certain country.
209 *
210 * @return bool
211 */
212 public function get_user_country() {
213
214 // check for bot.
215 if ( self::is_crawler() ) {
216 return false;
217 }
218
219 // Grab the users IP address.
220 $ip = self::get_user_ip();
221
222 // Deprecated.
223 $country = apply_filters_deprecated( 'wpoi-get-user-country', array( '', $ip ), '4.6.0', 'hustle_get_user_country' );
224
225 // See if an add-on provides the country for us.
226 $country = apply_filters( 'hustle_get_user_country', $country, $ip );
227
228 if ( empty( $country ) ) {
229 $country = $this->get_country_from_ip( $ip );
230 }
231
232 if ( empty( $country ) ) {
233 $country = 'XX';
234 }
235
236 return $country;
237 }
238
239 /**
240 * Returns a list of available ip-resolution services.
241 *
242 * @return array List of available webservices.
243 */
244 private function get_geo_services() {
245 static $geo_service = null;
246 if ( null === $geo_service ) {
247 $geo_service = array();
248
249 $geo_service['freeip'] = (object) array(
250 'label' => 'Free IP API',
251 'url' => 'https://free.freeipapi.com/api/json/%ip%',
252 'type' => 'json',
253 'field' => array( 'country_code' ),
254 );
255
256 $geo_service['ipwhois'] = (object) array(
257 'label' => 'IPWhois',
258 'url' => 'https://ipwho.is/%ip%',
259 'type' => 'json',
260 'field' => array( 'country_code' ),
261 );
262
263 // Deprecated.
264 $geo_service = apply_filters_deprecated( 'wpoi-geo-services', array( $geo_service ), '4.6.0', 'hustle_geo_services' );
265
266 /**
267 * Allow other modules/plugins to register a geo service.
268 */
269 $geo_service = apply_filters( 'hustle_geo_services', $geo_service );
270 }
271
272 return $geo_service;
273 }
274
275 /**
276 * Returns the lookup-service details
277 *
278 * @param string $type Type.
279 * @return object Service object for geo lookup
280 */
281 private function get_service( $type = null ) {
282 $service = false;
283 if ( null === $type ) {
284 // Deprecated.
285 $remote_ip_url = apply_filters_deprecated( 'wpoi-remote-ip-url', array( '' ), '4.6.0', 'hustle_remote_ip_url' );
286 $remote_ip_url = apply_filters( 'hustle_remote_ip_url', $remote_ip_url );
287 if ( ! empty( $remote_ip_url ) ) {
288 $type = '';
289 } else {
290 $type = self::DEFAULT_GEOIP_PROVIDER;
291 }
292
293 // Deprecated.
294 $type = apply_filters_deprecated( 'wpoi-geo-type-service', array( $type ), '4.6.0', 'hustle_geo_type_service' );
295
296 /**
297 * Allow to choose a geo service.
298 */
299 $type = apply_filters( 'hustle_geo_type_service', $type );
300 }
301
302 if ( empty( $type ) ) {
303 $service = (object) array(
304 'url' => $remote_ip_url,
305 'label' => 'wp-config.php',
306 'type' => 'text',
307 );
308 } elseif ( 'geo_db' === $type ) {
309 $service = (object) array(
310 'url' => 'db',
311 'label' => __( 'Local IP Lookup Table', 'hustle' ),
312 'type' => 'text',
313 );
314 } else {
315 $geo_service = $this->get_geo_services();
316 if ( isset( $geo_service[ $type ] ) ) {
317 $service = $geo_service[ $type ];
318 } else {
319 if ( WP_DEBUG ) {
320 $message = sprintf(
321 /* translators: %s: geoip provider name. */
322 __( 'GeoIP provider %s does not exist. Switching to default.', 'hustle' ),
323 $type
324 );
325 trigger_error( esc_html( $message ), E_USER_NOTICE ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
326 }
327 $service = $geo_service[ self::DEFAULT_GEOIP_PROVIDER ];
328 }
329 }
330
331 return $service;
332 }
333
334 /**
335 * Queries an external geo-API to find the country of the specified IP.
336 *
337 * @param string $ip The IP Address.
338 * @param object $service Lookup-Service details.
339 * @return string The country code.
340 */
341 private function country_from_api( $ip, $service ) {
342 $country = false;
343
344 if ( is_object( $service ) && ! empty( $service->url ) ) {
345 $url = str_replace( '%ip%', $ip, $service->url );
346 $response = wp_remote_get( $url );
347
348 if ( ! is_wp_error( $response ) ) {
349
350 $body = isset( $response['body'] ) ? $response['body'] : '';
351
352 if ( ! is_wp_error( $response )
353 && 200 === (int) $response['response']['code']
354 && 'XX' !== $response['body']
355 || false !== ( $body = file_get_contents( $url ) ) // phpcs:ignore
356 ) {
357 if ( 'text' === $service->type ) {
358 $country = trim( $body );
359 } elseif ( 'json' === $service->type ) {
360 $data = (array) json_decode( $body );
361 if ( isset( $service->field ) ) {
362 if ( is_array( $service->field ) ) {
363 $keys = $service->field;
364 $value = $data;
365 while ( $a = array_shift( $keys ) ) { // phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition
366 if ( is_array( $value ) ) {
367 if ( isset( $value[ $a ] ) ) {
368 $value = $value[ $a ];
369 }
370 } elseif ( is_object( $value ) ) {
371 if ( isset( $value->$a ) ) {
372 $value = $value->$a;
373 }
374 }
375 if ( is_string( $value ) ) {
376 $country = $value;
377 }
378 }
379 } else {
380 $country = isset( $data[ $service->field ] ) ? $data[ $service->field ] : null;
381 }
382 }
383 }
384 }
385 }
386 }
387
388 return $country;
389 }
390
391 /**
392 * Updates ip-country map and stores in options ( sitemeta ) table
393 *
394 * @param string $ip IP.
395 * @param string $country Country.
396 * @return mixed
397 */
398 private function update_ip_county_map( $ip, $country ) {
399 $country_ip_map[ $ip ] = $country;
400 update_option( self::COUNTRY_IP_MAP, $country_ip_map );
401 return $country;
402 }
403
404 /**
405 * Retrieves ip-country map from options ( sitemeta ) table
406 *
407 * @return array
408 */
409 private function get_ip_county_map() {
410 return get_option( self::COUNTRY_IP_MAP, array() );
411 }
412
413 /**
414 * Checks if the user is a crawler/bot.
415 *
416 * @return bool
417 */
418 private static function is_crawler() {
419
420 $user_agent = filter_input( INPUT_SERVER, 'HTTP_USER_AGENT' );
421 if ( $user_agent && preg_match( '/bot|crawler|ia_archiver|mediapartners-google|80legs|wget|voyager|baiduspider|curl|yahoo!|slurp/i', $user_agent ) ) {
422 return true;
423 }
424
425 return false;
426 }
427
428 /**
429 * Returns country string using ip address
430 *
431 * @param string $ip IP.
432 * @return string
433 */
434 public function get_country_from_ip( $ip ) {
435 // check for bot.
436 if ( self::is_crawler() ) {
437 return false;
438 }
439
440 $ip = (string) $ip;
441
442 if ( '127.0.0.1' === $ip ) {
443 return $this->update_ip_county_map( $ip, 'localhost' ); }
444
445 $country_ip_map = $this->get_ip_county_map();
446 if ( isset( $country_ip_map[ $ip ] ) ) {
447 if ( ! empty( $country_ip_map[ $ip ] ) ) {
448 return $country_ip_map[ $ip ];
449 }
450 }
451 $service = $this->get_service();
452 $country = $this->country_from_api( $ip, $service );
453
454 if ( ! empty( $country ) ) {
455 return $this->update_ip_county_map( $ip, $country );
456 }
457
458 return 'XX';
459 }
460 }
461