PluginProbe ʕ •ᴥ•ʔ
Hustle – Email Marketing, Lead Generation, Optins, Popups / 7.8.14.1
Hustle – Email Marketing, Lead Generation, Optins, Popups v7.8.14.1
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 5 months ago front 2 days ago helpers 1 month ago metas 3 months ago multisite 5 months ago palettes 3 years ago provider 1 month ago providers 2 days ago templates 3 years ago update 5 months ago class-hustle-admin-page-abstract.php 1 month ago class-hustle-auth-token.php 5 months ago class-hustle-condition-factory.php 6 years ago class-hustle-cross-sell.php 10 months ago class-hustle-dashboard-admin.php 1 month ago class-hustle-data.php 1 month 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 1 month ago class-hustle-module-collection.php 5 months ago class-hustle-module-fields.php 3 months ago class-hustle-module-page-abstract.php 1 month ago class-hustle-module-validator.php 3 months ago class-hustle-notifications.php 2 days ago class-hustle-settings-admin.php 1 month ago class-hustle-wp-dashboard-page.php 5 months ago class-opt-in.php 2 days ago hustle-background-conversion-log.php 3 months ago hustle-deletion.php 3 months ago hustle-embedded-admin.php 3 years ago hustle-entries-admin.php 5 months ago hustle-entry-model.php 1 month ago hustle-general-data-protection.php 5 months ago hustle-init.php 1 month ago hustle-mail.php 5 months ago hustle-migration.php 5 months ago hustle-model.php 2 days ago hustle-module-model.php 1 month ago hustle-module-widget-legacy.php 5 months ago hustle-module-widget.php 5 months ago hustle-modules-common-admin-ajax.php 1 month ago hustle-popup-admin.php 3 years ago hustle-providers-admin.php 1 month ago hustle-providers.php 5 months ago hustle-settings-admin-ajax.php 1 month ago hustle-settings-page.php 3 years ago hustle-slidein-admin.php 3 years ago hustle-sshare-admin.php 2 days ago hustle-sshare-model.php 2 days ago hustle-tracking-model.php 3 months ago interface-hustle-auth-provider.php 5 months ago opt-in-geo.php 5 months ago opt-in-utils.php 2 days ago opt-in-wpmudev-api.php 5 months ago
opt-in-geo.php
542 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 * Group name of ip list cache.
28 */
29 private const IP_CACHE_GROUP = 'hustle_ip_list';
30
31 /**
32 * Tries to get the public IP address of the current user.
33 *
34 * @return string The IP Address
35 */
36 public static function get_user_ip() {
37 // check for bot.
38 if ( self::is_crawler() ) {
39 return false;
40 }
41
42 // Check if request is from CloudFlare.
43 if ( self::is_cloudflare() ) {
44 $cf_ip = filter_input( INPUT_SERVER, 'HTTP_CF_CONNECTING_IP', FILTER_VALIDATE_IP );
45 if ( $cf_ip ) {
46 return apply_filters( 'hustle_user_ip', $cf_ip );
47 }
48 }
49
50 $result = (object) array(
51 'ip' => filter_input( INPUT_SERVER, 'REMOTE_ADDR', FILTER_SANITIZE_SPECIAL_CHARS ),
52 'proxy' => false,
53 'proxy_ip' => '',
54 );
55
56 /*
57 * This code tries to bypass a proxy and get the actual IP address of
58 * the visitor behind the proxy.
59 * Warning: These values might be spoofed!
60 */
61 $ip_fields = array(
62 'HTTP_CLIENT_IP',
63 'HTTP_X_FORWARDED_FOR',
64 'HTTP_X_FORWARDED',
65 'HTTP_X_CLUSTER_CLIENT_IP',
66 'HTTP_FORWARDED_FOR',
67 'HTTP_FORWARDED',
68 'REMOTE_ADDR',
69 );
70 $forwarded = false;
71 foreach ( $ip_fields as $key ) {
72 if ( true === array_key_exists( $key, $_SERVER ) ) {
73 $ips = filter_input( INPUT_SERVER, $key );
74 if ( ! $ips ) {
75 // Skip empty or invalid values.
76 continue;
77 }
78
79 foreach ( explode( ',', $ips ) as $ip ) {
80 $ip = trim( $ip );
81
82 if ( false !== filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) ) {
83 $forwarded = $ip;
84 break 2;
85 }
86 }
87 }
88 }
89
90 // If we found a different IP address than REMOTE_ADDR then it's a proxy!
91 if ( ! empty( $forwarded ) && $forwarded !== $result->ip ) {
92 $result->proxy = true;
93 $result->proxy_ip = $result->ip;
94 $result->ip = $forwarded;
95 }
96
97 if ( $result->ip ) {
98 $user_ip = $result->ip;
99 } else {
100 $user_ip = 'UNKNOWN';
101 }
102
103 return apply_filters( 'hustle_user_ip', esc_attr( $user_ip ) );
104 }
105
106 /**
107 * Validates that the IP that made the request is from cloudflare
108 *
109 * @param String $ip - the ip to check.
110 * @return bool
111 */
112 private static function validate_cloudflare_ip( $ip ) {
113 $cloudflare_ips = array(
114 '199.27.128.0/21',
115 '173.245.48.0/20',
116 '103.21.244.0/22',
117 '103.22.200.0/22',
118 '103.31.4.0/22',
119 '141.101.64.0/18',
120 '108.162.192.0/18',
121 '190.93.240.0/20',
122 '188.114.96.0/20',
123 '197.234.240.0/22',
124 '198.41.128.0/17',
125 '162.158.0.0/15',
126 '104.16.0.0/12',
127 );
128 $is_cf_ip = false;
129 foreach ( $cloudflare_ips as $cloudflare_ip ) {
130 if ( self::cloudflare_ip_in_range( $ip, $cloudflare_ip ) ) {
131 $is_cf_ip = true;
132 break;
133 }
134 }
135
136 return $is_cf_ip;
137 }
138
139 /**
140 * Check if the cloudflare IP is in range
141 *
142 * @param String $ip - the current IP.
143 * @param String $range - the allowed range of cloudflare ips.
144 * @return bool
145 */
146 private static function cloudflare_ip_in_range( $ip, $range ) {
147 if ( strpos( $range, '/' ) === false ) {
148 $range .= '/32';
149 }
150
151 // $range is in IP/CIDR format eg 127.0.0.1/24.
152 list( $range, $netmask ) = explode( '/', $range, 2 );
153 $range_decimal = ip2long( $range );
154 $ip_decimal = ip2long( $ip );
155 $wildcard_decimal = pow( 2, ( 32 - $netmask ) ) - 1;
156 $netmask_decimal = ~$wildcard_decimal;
157
158 return ( ( $ip_decimal & $netmask_decimal ) === ( $range_decimal & $netmask_decimal ) );
159 }
160
161 /**
162 * Check if there are any cloudflare headers in the request
163 *
164 * @return bool
165 */
166 private static function cloudflare_requests_check() {
167 $flag = true;
168
169 if ( ! isset( $_SERVER['HTTP_CF_CONNECTING_IP'] ) ) {
170 $flag = false;
171 }
172 if ( ! isset( $_SERVER['HTTP_CF_IPCOUNTRY'] ) ) {
173 $flag = false;
174 }
175 if ( ! isset( $_SERVER['HTTP_CF_RAY'] ) ) {
176 $flag = false;
177 }
178 if ( ! isset( $_SERVER['HTTP_CF_VISITOR'] ) ) {
179 $flag = false;
180 }
181
182 return $flag;
183 }
184
185 /**
186 * Check if the request is from cloudflare. If it is, we get the IP
187 *
188 * @return bool
189 */
190 private static function is_cloudflare() {
191 if ( isset( $_SERVER['HTTP_CLIENT_IP'] ) ) {
192 $ip = filter_input( INPUT_SERVER, 'HTTP_CLIENT_IP' );
193 } elseif ( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
194 $ip = filter_input( INPUT_SERVER, 'HTTP_X_FORWARDED_FOR' );
195 } else {
196 $ip = filter_input( INPUT_SERVER, 'REMOTE_ADDR' );
197 }
198 if ( isset( $ip ) ) {
199 $request_check = self::cloudflare_requests_check();
200 if ( ! $request_check ) {
201 return false;
202 }
203
204 $ip_check = self::validate_cloudflare_ip( $ip );
205
206 return $ip_check;
207 }
208
209 return false;
210 }
211
212 /**
213 * Checks if the users IP address belongs to a certain country.
214 *
215 * @return bool
216 */
217 public function get_user_country() {
218
219 // check for bot.
220 if ( self::is_crawler() ) {
221 return false;
222 }
223
224 // Grab the users IP address.
225 $ip = self::get_user_ip();
226
227 // Deprecated.
228 $country = apply_filters_deprecated( 'wpoi-get-user-country', array( '', $ip ), '4.6.0', 'hustle_get_user_country' );
229
230 // See if an add-on provides the country for us.
231 $country = apply_filters( 'hustle_get_user_country', $country, $ip );
232
233 if ( empty( $country ) ) {
234 $country = $this->get_country_from_ip( $ip );
235 }
236
237 if ( empty( $country ) ) {
238 $country = 'XX';
239 }
240
241 return $country;
242 }
243
244 /**
245 * Returns a list of available ip-resolution services.
246 *
247 * @return array List of available webservices.
248 */
249 private function get_geo_services() {
250 static $geo_service = null;
251 if ( null === $geo_service ) {
252 $geo_service = array();
253
254 $geo_service['freeip'] = (object) array(
255 'label' => 'Free IP API',
256 'url' => 'https://free.freeipapi.com/api/json/%ip%',
257 'type' => 'json',
258 'field' => array( 'country_code' ),
259 );
260
261 $geo_service['ipwhois'] = (object) array(
262 'label' => 'IPWhois',
263 'url' => 'https://ipwho.is/%ip%',
264 'type' => 'json',
265 'field' => array( 'country_code' ),
266 );
267
268 // Deprecated.
269 $geo_service = apply_filters_deprecated( 'wpoi-geo-services', array( $geo_service ), '4.6.0', 'hustle_geo_services' );
270
271 /**
272 * Allow other modules/plugins to register a geo service.
273 */
274 $geo_service = apply_filters( 'hustle_geo_services', $geo_service );
275 }
276
277 return $geo_service;
278 }
279
280 /**
281 * Returns the lookup-service details
282 *
283 * @param string $type Type.
284 * @return object Service object for geo lookup
285 */
286 private function get_service( $type = null ) {
287 $service = false;
288 if ( null === $type ) {
289 // Deprecated.
290 $remote_ip_url = apply_filters_deprecated( 'wpoi-remote-ip-url', array( '' ), '4.6.0', 'hustle_remote_ip_url' );
291 $remote_ip_url = apply_filters( 'hustle_remote_ip_url', $remote_ip_url );
292 if ( ! empty( $remote_ip_url ) ) {
293 $type = '';
294 } else {
295 $type = self::DEFAULT_GEOIP_PROVIDER;
296 }
297
298 // Deprecated.
299 $type = apply_filters_deprecated( 'wpoi-geo-type-service', array( $type ), '4.6.0', 'hustle_geo_type_service' );
300
301 /**
302 * Allow to choose a geo service.
303 */
304 $type = apply_filters( 'hustle_geo_type_service', $type );
305 }
306
307 if ( empty( $type ) ) {
308 $service = (object) array(
309 'url' => $remote_ip_url,
310 'label' => 'wp-config.php',
311 'type' => 'text',
312 );
313 } elseif ( 'geo_db' === $type ) {
314 $service = (object) array(
315 'url' => 'db',
316 'label' => __( 'Local IP Lookup Table', 'hustle' ),
317 'type' => 'text',
318 );
319 } else {
320 $geo_service = $this->get_geo_services();
321 if ( isset( $geo_service[ $type ] ) ) {
322 $service = $geo_service[ $type ];
323 } else {
324 if ( WP_DEBUG ) {
325 $message = sprintf(
326 /* translators: %s: geoip provider name. */
327 __( 'GeoIP provider %s does not exist. Switching to default.', 'hustle' ),
328 $type
329 );
330 trigger_error( esc_html( $message ), E_USER_NOTICE ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
331 }
332 $service = $geo_service[ self::DEFAULT_GEOIP_PROVIDER ];
333 }
334 }
335
336 return $service;
337 }
338
339 /**
340 * Queries an external geo-API to find the country of the specified IP.
341 *
342 * @param string $ip The IP Address.
343 * @param object $service Lookup-Service details.
344 * @return string The country code.
345 */
346 private function country_from_api( $ip, $service ) {
347 $country = false;
348
349 if ( is_object( $service ) && ! empty( $service->url ) ) {
350 $url = str_replace( '%ip%', $ip, $service->url );
351 $response = wp_remote_get( $url );
352
353 if ( ! is_wp_error( $response ) ) {
354
355 $body = isset( $response['body'] ) ? $response['body'] : '';
356
357 if ( ! is_wp_error( $response )
358 && 200 === (int) $response['response']['code']
359 && 'XX' !== $response['body']
360 || false !== ( $body = file_get_contents( $url ) ) // phpcs:ignore
361 ) {
362 if ( 'text' === $service->type ) {
363 $country = trim( $body );
364 } elseif ( 'json' === $service->type ) {
365 $data = (array) json_decode( $body );
366 if ( isset( $service->field ) ) {
367 if ( is_array( $service->field ) ) {
368 $keys = $service->field;
369 $value = $data;
370
371 $element = array_shift( $keys );
372 while ( $element ) {
373 if ( is_array( $value ) ) {
374 if ( isset( $value[ $element ] ) ) {
375 $value = $value[ $element ];
376 }
377 } elseif ( is_object( $value ) ) {
378 if ( isset( $value->$element ) ) {
379 $value = $value->$element;
380 }
381 }
382 if ( is_string( $value ) ) {
383 $country = $value;
384 }
385
386 $element = array_shift( $keys );
387 }
388 } else {
389 $country = isset( $data[ $service->field ] ) ? $data[ $service->field ] : null;
390 }
391 }
392 }
393 }
394 }
395 }
396
397 return $country;
398 }
399
400 /**
401 * Updates ip-country map and stores in options ( sitemeta ) table
402 *
403 * @param string $ip IP.
404 * @param string $country Country.
405 * @return mixed
406 */
407 private function update_ip_county_map( $ip, $country ) {
408 $country_ip_map = $this->get_ip_county_map();
409 $country_ip_map[ $ip ] = $country;
410
411 update_option( self::COUNTRY_IP_MAP, $country_ip_map );
412 return $country;
413 }
414
415 /**
416 * Retrieves ip-country map from options ( sitemeta ) table
417 *
418 * @return array
419 */
420 private function get_ip_county_map() {
421 return get_option( self::COUNTRY_IP_MAP, array() );
422 }
423
424 /**
425 * Checks if the user is a crawler/bot.
426 *
427 * @return bool
428 */
429 private static function is_crawler() {
430
431 $user_agent = filter_input( INPUT_SERVER, 'HTTP_USER_AGENT' );
432 if ( $user_agent && preg_match( '/bot|crawler|ia_archiver|mediapartners-google|80legs|wget|voyager|baiduspider|curl|yahoo!|slurp/i', $user_agent ) ) {
433 return true;
434 }
435
436 return false;
437 }
438
439 /**
440 * Returns country string using ip address
441 *
442 * @param string $ip IP.
443 * @return string
444 */
445 public function get_country_from_ip( $ip ) {
446 // check for bot.
447 if ( self::is_crawler() ) {
448 return false;
449 }
450
451 $ip = (string) $ip;
452
453 if ( '127.0.0.1' === $ip ) {
454 return $this->update_ip_county_map( $ip, 'localhost' );
455 }
456
457 // See if we have it cached/saved already.
458 $country = $this->find_saved_ip_country( $ip );
459 if ( ! empty( $country ) ) {
460 return $country;
461 }
462
463 // Query external geo-API.
464 $country = $this->api_find_country( $ip );
465 if ( ! empty( $country ) ) {
466 // Save it for next time.
467 $this->update_ip_country( $ip, $country );
468 return $country;
469 }
470
471 return 'XX';
472 }
473
474 /**
475 * Returns country string using ip address
476 *
477 * @param string $ip_address IP address.
478 * @return string|false
479 */
480 public function find_saved_ip_country( $ip_address ) {
481 $ip_country = self::get_cached_ip_country( $ip_address );
482 if ( $ip_country ) {
483 return $ip_country;
484 }
485
486 $country_ip_map = $this->get_ip_county_map();
487 if ( isset( $country_ip_map[ $ip_address ] ) ) {
488 if ( ! empty( $country_ip_map[ $ip_address ] ) ) {
489 return $country_ip_map[ $ip_address ];
490 }
491 }
492
493 return false;
494 }
495
496 /**
497 * Get cached ip country.
498 *
499 * @param string $ip_address IP address.
500 * @return string|false
501 */
502 private static function get_cached_ip_country( $ip_address ) {
503 $hash = md5( $ip_address );
504 return wp_cache_get( $hash, self::IP_CACHE_GROUP );
505 }
506
507 /**
508 * Queries an external geo-API to find the country of the specified IP.
509 *
510 * @param string $ip_address IP address.
511 * @return string
512 */
513 private function api_find_country( $ip_address ) {
514 $service = $this->get_service();
515 return $this->country_from_api( $ip_address, $service );
516 }
517
518 /**
519 * Update ip country.
520 *
521 * @param string $ip_address IP address.
522 * @param string $country Country code.
523 * @return string
524 */
525 public function update_ip_country( $ip_address, $country ) {
526 self::cache_ip_country( $ip_address, $country );
527 return $this->update_ip_county_map( $ip_address, $country );
528 }
529
530 /**
531 * Cache ip country.
532 *
533 * @param string $ip_address IP address.
534 * @param string $country Country code.
535 * @return void
536 */
537 private static function cache_ip_country( $ip_address, $country ) {
538 $hash = md5( $ip_address );
539 wp_cache_set( $hash, $country, self::IP_CACHE_GROUP, DAY_IN_SECONDS );
540 }
541 }
542