PluginProbe
StoreEngine — Complete eCommerce Solution with Memberships, Licensing, Affiliates & More / 2.2.0
StoreEngine — Complete eCommerce Solution with Memberships, Licensing, Affiliates & More v2.2.0
2.3.0 2.2.0 2.1.1 2.1.0 2.0.0 1.10.0 1.9.1 1.9.0 1.2.1 1.2.2 1.3.0 1.3.1 1.3.2 1.3.3 1.4.0 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6.0 All 59 releases
storeengine / includes / utils / traits / maxmind.php

maxmind.php in StoreEngine — Complete eCommerce Solution with Memberships, Licensing, Affiliates & More 2.2.0, at includes/utils/traits/maxmind.php

217 lines 5.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * GeoLocation MaxMind integration Helper.
4 *
5 * @version 1.0.0
6 * @since 1.5.6
7 *
8 * @package StoreEngine/Utils/traits
9 */
10
11 namespace StoreEngine\Utils\traits;
12
13 use Exception;
14 use PharData;
15 use StoreEngine\MaxMind\Db\Reader;
16 use StoreEngine\Utils\Formatting;
17 use StoreEngine\Utils\Fs;
18 use StoreEngine\Utils\Helper;
19 use WP_Error;
20
21 trait Maxmind {
22 /**
23 * The name of the MaxMind database to utilize.
24 */
25 public static string $maxmind_db = 'GeoLite2-Country';
26
27 /**
28 * The extension for the MaxMind database.
29 */
30 public static string $maxmind_db_ext = '.mmdb';
31
32 public static function maxmind_locate_ip( array $data, string $ip_address = null ): array {
33 // Check if country already found from header.
34 if ( ! empty( $data['country'] ) ) {
35 return $data;
36 }
37
38 if ( empty( $ip_address ) ) {
39 return $data;
40 }
41
42 $country_code = self::country_code_for_ip( $ip_address );
43
44 return [
45 'country' => $country_code,
46 'state' => '',
47 'city' => '',
48 'postcode' => '',
49 ];
50 }
51
52 /**
53 * Fetches the ISO country code associated with an IP address.
54 *
55 * @param string $ip_address The IP address to find the country code for.
56 * @return string The country code for the IP address, or empty if not found.
57 */
58 public static function country_code_for_ip( string $ip_address ): string {
59 $country_code = '';
60
61
62 $database_path = self::get_maxmind_db_path();
63
64 if ( ! file_exists( $database_path ) ) {
65 return $country_code;
66 }
67
68 try {
69 $reader = new Reader( $database_path );
70 $data = $reader->get( $ip_address );
71 if ( isset( $data['country']['iso_code'] ) ) {
72 $country_code = $data['country']['iso_code'];
73 }
74
75 $reader->close();
76 } catch ( Exception $e ) {
77 Helper::log_error( $e );
78 }
79
80 return $country_code;
81 }
82
83 public static function validate_maxmind_license_key( string $license_key ) {
84 $cache_key = 'maxmind_license_key_' . md5( $license_key ) . 'validation_status';
85 $cached = get_transient( $cache_key );
86
87 if ( false !== $cached ) {
88 return $cached;
89 }
90
91 $response = wp_remote_post(
92 'https://secret-scanning.maxmind.com/secrets/validate-license-key',
93 [
94 'body' => [ 'license_key' => $license_key ],
95 'timeout' => 15,
96 ]
97 );
98
99 if ( ! is_wp_error( $response ) ) {
100 $code = wp_remote_retrieve_response_code( $response );
101
102 $response = 204 === $code ? true : new WP_Error( 'invalid_maxmind_license_key', __( 'The MaxMind license key is invalid. Newly created keys may take a few minutes to activate.', 'storeengine' ) );
103 }
104
105 set_transient( $cache_key, $response, 5 * MINUTE_IN_SECONDS );
106
107 return $response;
108 }
109
110 /**
111 * Fetches the database from the MaxMind service.
112 *
113 * @return string|WP_Error The path to the database file or an error if invalid.
114 */
115 public static function download_maxmind_db( string $license_key ) {
116 $download_uri = add_query_arg(
117 [
118 'edition_id' => self::$maxmind_db,
119 'license_key' => urlencode( Formatting::clean( $license_key ) ),
120 'suffix' => 'tar.gz',
121 ],
122 'https://download.maxmind.com/app/geoip_download'
123 );
124
125 $tmp_file = Fs::download_url( $download_uri );
126
127 if ( is_wp_error( $tmp_file ) ) {
128 $error_data = [
129 'data' => $tmp_file->get_error_data(),
130 'error' => $tmp_file->get_error_message(),
131 ];
132
133 if ( isset( $error_data['code'] ) && 401 === (int) $error_data['code'] ) {
134 return new WP_Error(
135 'invalid_maxmind_license_key',
136 __( 'The MaxMind license key is invalid. Newly created keys may take a few minutes to activate.', 'storeengine' ),
137 $error_data
138 );
139 }
140
141 return new WP_Error( 'maxmind_db_download_failed', __( 'Failed to download the MaxMind GeoLite2-Country database.', 'storeengine' ), $error_data );
142 }
143
144 // Extract the database from the archive.
145 try {
146 $file = new PharData( $tmp_file );
147
148 $tmp_database_path = trailingslashit( dirname( $tmp_file ) ) . trailingslashit( $file->current()->getFilename() ) . self::$maxmind_db . self::$maxmind_db_ext;
149
150 $file->extractTo( dirname( $tmp_file ), trailingslashit( $file->current()->getFilename() ) . self::$maxmind_db . self::$maxmind_db_ext, true );
151 } catch ( Exception $exception ) {
152 return new WP_Error( 'maxmind_db_archive_extract_failed', $exception->getMessage() );
153 } finally {
154 // Remove the archive since we only care about a single file in it.
155 wp_delete_file( $tmp_file );
156 }
157
158 return $tmp_database_path;
159 }
160
161 /**
162 * Updates the database used for geolocation queries.
163 *
164 * @param ?string $new_db The path to the new database file. Null will fetch a new archive.
165 */
166 public static function update_maxmind_db( string $new_db = null ) {
167 // Allow us to easily interact with the filesystem.
168 $fs = Fs::load();
169
170 if ( is_wp_error( $fs ) ) {
171 return;
172 }
173
174 // Remove any existing archives to comply with the MaxMind TOS.
175 $target_database_path = self::get_maxmind_db_path();
176
177 // If there's no database path, we can't store the database.
178 if ( empty( $target_database_path ) ) {
179 return;
180 }
181
182 if ( $fs->exists( $target_database_path ) ) {
183 $fs->delete( $target_database_path );
184 }
185
186 if ( isset( $new_db ) ) {
187 $tmp_file = $new_db;
188 } else {
189 // We can't download a database if there's no license key configured.
190 $license_key = Helper::get_settings( 'maxmind_license' );
191
192 if ( empty( $license_key ) ) {
193 return;
194 }
195
196 $tmp_file = self::download_maxmind_db( $license_key );
197
198 if ( is_wp_error( $tmp_file ) ) {
199 return;
200 }
201 }
202
203 // Move the new database into position.
204 $fs->move( $tmp_file, $target_database_path, true );
205 $fs->delete( dirname( $tmp_file ) );
206 }
207
208 public static function get_maxmind_db_path(): string {
209 return apply_filters(
210 'storeengine/geolocation/maxmind/db-path',
211 STOREENGINE_SECURE_UPLOADS_DIR . '/maxmind/' . self::$maxmind_db . self::$maxmind_db_ext
212 );
213 }
214 }
215
216 // End of file maxmind.php.
217