From b3d23e6c3ae23d4da82a2b350ced12b9b49c3dad Mon Sep 17 00:00:00 2001 From: "Daniel S. Reichenbach" Date: Thu, 2 May 2024 14:50:20 +0200 Subject: [PATCH] feat: replaced unknown API calls with official MaxMind endpoints --- .github/ISSUE_TEMPLATE/bug_report.md | 5 +- README.md | 53 +++++++------ composer.json | 17 ++++- src/Client.php | 110 ++++++++++++--------------- src/ComposerConsole.php | 28 +++---- 5 files changed, 103 insertions(+), 110 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index bd32ade..bba99d0 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -12,6 +12,7 @@ A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: + 1. Updating via Composer or from a php application? 2. Which editions of the databases you are trying to update? 3. What configuration settings are you using? (here you can replace the license key "maxmind" with "..."). @@ -23,9 +24,11 @@ Steps to reproduce the behavior: If applicable, add screenshots to help explain your problem. **Environment (please complete the following information):** + - OS: [e.g. Windows or Linux or other] - PHP version: [e.g. 5.3.0, 7.4.0] -- `tronovav/geoip2-update` version (to check the version run the command `composer show tronovav/geoip2-update`): [e.g. v2.1.11] +- `danielsreichenbach/geoip2-update` version (to check the version run the + command `composer show danielsreichenbach/geoip2-update`): [e.g. v2.1.11] - Composer version: [e.g. 2.0.13] **Additional context** diff --git a/README.md b/README.md index 424ceeb..fa1a708 100644 --- a/README.md +++ b/README.md @@ -1,46 +1,51 @@ -[![Geoip2 Update](https://user-images.githubusercontent.com/25905384/111375423-4631ce00-86af-11eb-81a9-2bc4dab89068.png)](https://www.geodbase-update.com/?utm_source=github&utm_medium=organic&utm_campaign=github_project_page&utm_content=main_banner) +# geoip2-update -Geoip2 Update is a PHP tool for updating Maxmind GeoLite2 and GeoIP2 databases from your script, application or via Composer. +[![Geoip2 Update](https://user-images.githubusercontent.com/25905384/111375423-4631ce00-86af-11eb-81a9-2bc4dab89068.png)](https://www.geodbase-update.com/?utm_source=github&utm_medium=organic&utm_campaign=github_project_page&utm_content=main_banner) -[![Latest Stable Version](https://img.shields.io/packagist/v/tronovav/geoip2-update)](https://packagist.org/packages/tronovav/geoip2-update) -[![GitHub downloads](https://img.shields.io/packagist/dt/tronovav/geoip2-update)](https://packagist.org/packages/tronovav/geoip2-update) +Geoip2 Update is a PHP tool for updating Maxmind GeoLite2 and GeoIP2 databases +from your script, application or via Composer. -[![Payeer](https://payeer.com/style/images/banner/970x90-1.jpg)](https://payeer.com/031702636) +[![Latest Stable Version](https://img.shields.io/packagist/v/danielsreichenbach/geoip2-update)](https://packagist.org/packages/danielsreichenbach/geoip2-update) +[![GitHub downloads](https://img.shields.io/packagist/dt/danielsreichenbach/geoip2-update)](https://packagist.org/packages/danielsreichenbach/geoip2-update) -INSTALLATION ------------- +## INSTALLATION ```bash - composer require tronovav/geoip2-update + composer require danielsreichenbach/geoip2-update ``` -DOCUMENTATION -------------- +## DOCUMENTATION -You can read the documentation on setting up and using the library, as well as learn about new features on the official website. +You can read the documentation on setting up and using the library, as well as +learn about new features on the official website. **Go to documentation -> [GeoIP2 Update Documentation](https://www.geodbase-update.com/?utm_source=github&utm_medium=organic&utm_campaign=github_project_page&utm_content=documentation_link)** -FEATURES --------- +## FEATURES + +### 1. Updating GeoIP2 databases via Composer + +To update Geoip2 databases via Composer, you can set up an update call in your +`composer.json`. -### 1. Updating GeoIP2 databases via Composer. +Each time the `composer update` command is invoked, the library will check for +updates on the "maxmind.com" server and update the Geoip2 databases if +necessary. -To update Geoip2 databases via Composer, you can set up an update call in your `composer.json`. -Each time the `composer update` command is invoked, the library will check for updates on the "maxmind.com" server and update the Geoip2 databases if necessary. -You can also update only `GeoIP2` databases without updating all project dependencies: -`composer update tronovav/geoip2-update`. +You can also update only `GeoIP2` databases without updating all project +dependencies: `composer update danielsreichenbach/geoip2-update`. -### 2. Updating GeoIP2 databases from your PHP application. +### 2. Updating GeoIP2 databases from your PHP application -You can use this option to update `GeoIP2` databases from your PHP project, or use `Cron` on Linux, or use `Task Scheduler` on Windows. +You can use this option to update `GeoIP2` databases from your PHP project, or +use `cron` on Linux, or use `Task Scheduler` on Windows. -### 3. Simple, cross-platform and reliable. +### 3. Simple, cross-platform and reliable -Does not depend on the operating system and can be used on hosting services and production servers. +Does not depend on the operating system and can be used on hosting services and +production servers. -COPYRIGHT AND LICENSE ---------------------- +## COPYRIGHT AND LICENSE This software is Copyright (c) 2021 by Andrey Tronov. diff --git a/composer.json b/composer.json index ecea98b..38133dc 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,5 @@ { - "name": "tronovav/geoip2-update", + "name": "danielsreichenbach/geoip2-update", "description": "Update GeoIP2/GeoLite2 databases from your script, program or via composer.", "keywords": [ "geoip", @@ -14,22 +14,31 @@ { "name": "Andrey Tronov", "email": "newtronov@gmail.com" + }, + { + "name": "Daniel S. Reichenbach", + "email": "daniel@kogito.network" } ], "support": { "email": "newtronov@gmail.com" }, "require": { - "php": ">=5.3", + "php": ">=8.0", "ext-curl": "*", - "ext-json": "*" + "ext-json": "*", + "composer-plugin-api": "^2.1" }, "suggest": { "ext-zip": "Required for updating the CSV databases." }, "autoload": { "psr-4": { - "tronovav\\GeoIP2Update\\": "src/" + "danielsreichenbach\\GeoIP2Update\\": "src/" } + }, + "require-dev": { + "composer/composer": "^2.1", + "symfony/console": "^7.0" } } diff --git a/src/Client.php b/src/Client.php index 60c1536..a88d657 100644 --- a/src/Client.php +++ b/src/Client.php @@ -20,16 +20,16 @@ class Client const ARCHIVE_ZIP = 'zip'; /** - * @var string Your account’s actual license key on www.maxmind.com + * @var string Your account’s unique id on www.maxmind.com * @link https://support.maxmind.com/account-faq/license-keys/where-do-i-find-my-license-key/ */ - public $license_key; + public $account_id; /** - * @var string Your account’s actual "geodbase_update_key" on www.geodbase-update.com - * @link https://www.geodbase-update.com + * @var string Your account’s actual license key on www.maxmind.com + * @link https://support.maxmind.com/account-faq/license-keys/where-do-i-find-my-license-key/ */ - public $geodbase_update_key; + public $license_key; /** * @var string[] Database editions list to update. @@ -47,7 +47,7 @@ class Client public $dir; protected $_editionVersions = array(); - protected $_baseUrlApi = 'https://www.geodbase-update.com/api/v1/edition'; + protected $_baseUrlApi = 'https://updates.maxmind.com'; protected $_updated = array(); protected $_errors = array(); protected $_errorUpdateEditions = array(); @@ -102,7 +102,7 @@ public function run() protected function setConfParams(&$params) { if (array_key_exists('geoipConfFile', $params)) { - if(is_file($params['geoipConfFile']) && is_readable($params['geoipConfFile'])) { + if (is_file($params['geoipConfFile']) && is_readable($params['geoipConfFile'])) { $confParams = array(); foreach (file($params['geoipConfFile']) as $line) { $confString = trim($line); @@ -114,11 +114,11 @@ protected function setConfParams(&$params) : trim($matches['value']); } } + $this->account_id = !empty($confParams['AccountId']) ? $confParams['AccountId'] : $this->account_id; $this->license_key = !empty($confParams['LicenseKey']) ? $confParams['LicenseKey'] : $this->license_key; $this->editions = !empty($confParams['EditionIDs']) ? $confParams['EditionIDs'] : $this->editions; - } - else{ - $this->_errors[] = 'The geoipConfFile parameter was specified, but the file itself is missing or unreadable. See https://www.geodbase-update.com'; + } else { + $this->_errors[] = 'The geoipConfFile parameter was specified, but the file itself is missing or unreadable.'; } unset($params['geoipConfFile']); } @@ -134,20 +134,23 @@ protected function validate() switch (true) { case empty($this->dir): - $this->_errors[] = 'Destination directory not specified. See documentation at https://www.geodbase-update.com'; + $this->_errors[] = 'Destination directory not specified.'; break; case !is_dir($this->dir): - $this->_errors[] = "The destination directory \"{$this->dir}\" does not exist. See documentation at https://www.geodbase-update.com"; + $this->_errors[] = "The destination directory \"{$this->dir}\" does not exist."; break; case !is_writable($this->dir): - $this->_errors[] = "The destination directory \"{$this->dir}\" is not writable. See documentation at https://www.geodbase-update.com"; + $this->_errors[] = "The destination directory \"{$this->dir}\" is not writable."; } + if (empty($this->account_id)) + $this->_errors[] = 'You must specify your Maxmind "account_id".'; + if (empty($this->license_key)) - $this->_errors[] = 'You must specify your Maxmind "license_key". See documentation at https://www.geodbase-update.com'; + $this->_errors[] = 'You must specify your Maxmind "license_key".'; if (empty($this->editions)) - $this->_errors[] = "No GeoIP revision names are specified for the update. See documentation at https://www.geodbase-update.com"; + $this->_errors[] = "No GeoIP revision names are specified for the update."; if (!empty($this->_errors)) return false; @@ -166,21 +169,16 @@ protected function updateEdition($editionId) if (!empty($this->_errorUpdateEditions[$editionId])) return; - if ($remoteEditionData['ext'] === self::ARCHIVE_ZIP && !class_exists('\ZipArchive')) { - $this->_errorUpdateEditions[$editionId] = "PHP zip extension is required to update csv databases. See https://www.php.net/manual/en/zip.installation.php to install zip php extension."; - return; - } - - $remoteActualVersion = date_create($remoteEditionData['version']); + $remoteActualVersion = date_create($remoteEditionData['date']); $localEditionData = is_file($this->getEditionDirectory($editionId) . DIRECTORY_SEPARATOR . $this->_lastModifiedStorageFileName) ? file_get_contents($this->getEditionDirectory($editionId) . DIRECTORY_SEPARATOR . $this->_lastModifiedStorageFileName) : ''; - $currentVersion = date_create_from_format('Y-m-d\TH:i:sP',$localEditionData) ?: 0; + $currentVersion = date_create_from_format('Y-m-d', $localEditionData) ?: 0; - $this->_editionVersions[$editionId] = array(!empty($currentVersion) ? $currentVersion->format('c') : 0,$remoteActualVersion->format('c')); + $this->_editionVersions[$editionId] = array(!empty($currentVersion) ? $currentVersion->format('Ymd') : 0, $remoteActualVersion->format('Ymd')); - if (empty($currentVersion) || $currentVersion != $remoteActualVersion) { + if (empty($currentVersion) || $currentVersion < $remoteActualVersion) { $this->download($remoteEditionData); if (!empty($this->_errorUpdateEditions[$editionId])) @@ -210,41 +208,38 @@ protected function getEditionDirectory($editionId) */ protected function getRemoteEditionData($editionId) { - $ch = curl_init(trim($this->_baseUrlApi,'/').'/'.'data'.'?'. http_build_query(array( - 'id' => $editionId, - ))); + $ch = curl_init(trim($this->_baseUrlApi, '/') . '/' . 'geoip/updates/metadata' . '?' . http_build_query([ + 'edition_id' => $editionId, + ])); curl_setopt_array($ch, array( CURLOPT_HEADER => false, CURLOPT_RETURNTRANSFER => true, - CURLOPT_CUSTOMREQUEST => 'POST', + CURLOPT_CUSTOMREQUEST => 'GET', CURLOPT_HTTPHEADER => array( 'Content-Type: application/json', 'Accept: application/json', - 'X-Api-Key: '.$this->geodbase_update_key, ), - CURLOPT_POSTFIELDS => json_encode(array( - 'maxmind_key' =>$this->license_key, - 'client' => $this->_client, - 'client_version' => $this->_client_version, - )), + CURLOPT_HTTPAUTH => CURLAUTH_BASIC, + CURLOPT_USERPWD => sprintf("%s:%s", $this->account_id, $this->license_key), )); $result = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); - if(empty($httpCode)){ + if (empty($httpCode)) { $this->_errorUpdateEditions[$editionId] = "The remote server is not available."; return array(); } - $resultArray = json_decode($result,true); + $resultArray = json_decode($result, true); - if($httpCode !== 200){ - $this->_errorUpdateEditions[$editionId] = $resultArray['data']['message'] ?: $resultArray['data']['name']; + if ($httpCode !== 200) { + $this->_errorUpdateEditions[$editionId] = $resultArray['error'] ?: $resultArray['error']; return array(); } - return $resultArray['data']; + + return $resultArray['databases'][0]; } /** @@ -252,7 +247,7 @@ protected function getRemoteEditionData($editionId) */ protected function download($remoteEditionData) { - $ch = curl_init(trim($this->_baseUrlApi,'/').'/'.'download'.'?'. http_build_query(array( + $ch = curl_init(trim($this->_baseUrlApi, '/') . '/' . 'download' . '?' . http_build_query(array( 'request_id' => $remoteEditionData['request_id'], ))); $fh = fopen($this->getArchiveFile($remoteEditionData), 'wb'); @@ -261,16 +256,17 @@ protected function download($remoteEditionData) CURLOPT_FOLLOWLOCATION => true, CURLOPT_HTTPHEADER => array( 'Content-Type: application/json', - 'X-Api-Key: '.$this->geodbase_update_key, ), CURLOPT_FILE => $fh, + CURLOPT_HTTPAUTH => CURLAUTH_BASIC, + CURLOPT_USERPWD => sprintf("%s:%s", $this->account_id, $this->license_key), )); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); fclose($fh); - if ($response === false || $httpCode !== 200){ - if(is_file($this->getArchiveFile($remoteEditionData))) + if ($response === false || $httpCode !== 200) { + if (is_file($this->getArchiveFile($remoteEditionData))) unlink($this->getArchiveFile($remoteEditionData)); $this->_errorUpdateEditions[$remoteEditionData['id']] = "Download error: ($httpCode)" . curl_error($ch); } @@ -278,7 +274,7 @@ protected function download($remoteEditionData) protected function getArchiveFile($remoteEditionData) { - return $this->dir . DIRECTORY_SEPARATOR . $remoteEditionData['id'] . '.' . $remoteEditionData['ext']; + return $this->dir . DIRECTORY_SEPARATOR . $remoteEditionData['edition_id'] . '.' . 'tar.gz'; } /** @@ -286,35 +282,23 @@ protected function getArchiveFile($remoteEditionData) */ protected function extract($remoteEditionData) { - switch ($remoteEditionData['ext']) { - case self::ARCHIVE_GZ: - - $phar = new \PharData($this->getArchiveFile($remoteEditionData)); - $phar->extractTo($this->dir, null, true); - break; - case self::ARCHIVE_ZIP: - - $zip = new \ZipArchive; - $zip->open($this->getArchiveFile($remoteEditionData)); - $zip->extractTo($this->dir); - $zip->close(); - break; - } + $phar = new \PharData($this->getArchiveFile($remoteEditionData)); + $phar->extractTo($this->dir, null, true); unlink($this->getArchiveFile($remoteEditionData)); - if (!is_dir($this->getEditionDirectory($remoteEditionData['id']))) - mkdir($this->getEditionDirectory($remoteEditionData['id'])); + if (!is_dir($this->getEditionDirectory($remoteEditionData['edition_id']))) + mkdir($this->getEditionDirectory($remoteEditionData['edition_id'])); $directories = new \DirectoryIterator($this->dir); foreach ($directories as $directory) /* @var \DirectoryIterator $directory */ - if ($directory->isDir() && preg_match('/^' . $remoteEditionData['id'] . '[_\d]+$/i', $directory->getBasename())) { + if ($directory->isDir() && preg_match('/^' . $remoteEditionData['edition_id'] . '[_\d]+$/i', $directory->getBasename())) { $newEditionDirectory = new \DirectoryIterator($directory->getPathname()); foreach ($newEditionDirectory as $item) if ($item->isFile()) - rename($item->getPathname(), $this->getEditionDirectory($remoteEditionData['id']) . DIRECTORY_SEPARATOR . $item->getBasename()); - file_put_contents($this->getEditionDirectory($remoteEditionData['id']) . DIRECTORY_SEPARATOR . $this->_lastModifiedStorageFileName, $remoteEditionData['version']); + rename($item->getPathname(), $this->getEditionDirectory($remoteEditionData['edition_id']) . DIRECTORY_SEPARATOR . $item->getBasename()); + file_put_contents($this->getEditionDirectory($remoteEditionData['edition_id']) . DIRECTORY_SEPARATOR . $this->_lastModifiedStorageFileName, $remoteEditionData['date']); $this->deleteDirectory($directory->getPathname()); break; } diff --git a/src/ComposerConsole.php b/src/ComposerConsole.php index faf38b0..07152a9 100644 --- a/src/ComposerConsole.php +++ b/src/ComposerConsole.php @@ -30,15 +30,16 @@ class ComposerConsole extends Client */ protected function download($remoteEditionData) { - $editionId = $remoteEditionData['id']; + $editionId = $remoteEditionData['edition_id']; $progressBar = new ProgressBar((new ConsoleOutput()), 100); $progressBar->setFormat(" - Downloading $editionId: [%bar%] %percent:3s%%"); $progressBar->setRedrawFrequency(1); $progressBar->start(); $progressBarFinish = false; - $ch = curl_init(trim($this->_baseUrlApi,'/').'/'.'download'.'?'. http_build_query(array( - 'request_id' => $remoteEditionData['request_id'], + $ch = curl_init(trim($this->_baseUrlApi, '/') . '/' . 'geoip/databases/' . $editionId . '/download' . '?' . http_build_query(array( + 'date' => date_create($remoteEditionData['date'])->format('Ymd'), + 'suffix' => 'tar.gz' ))); $fh = fopen($this->getArchiveFile($remoteEditionData), 'wb'); curl_setopt_array($ch, array( @@ -47,23 +48,14 @@ protected function download($remoteEditionData) CURLOPT_FILE => $fh, CURLOPT_HTTPHEADER => array( 'Content-Type: application/json', - 'X-Api-Key: '.$this->geodbase_update_key, ), + CURLOPT_HTTPAUTH => CURLAUTH_BASIC, + CURLOPT_USERPWD => sprintf("%s:%s", $this->account_id, $this->license_key), CURLOPT_NOPROGRESS => false, CURLOPT_PROGRESSFUNCTION => function ($resource, $download_size = 0, $downloaded = 0, $upload_size = 0, $uploaded = 0, $uploaded2 = 0) use ($progressBar, &$progressBarFinish, $remoteEditionData) { - /** - * $resource parameter was added in version 5.5.0 breaking backwards compatibility; - * if we are using PHP version lower than 5.5.0, we need to shift the arguments - * @see http://php.net/manual/en/function.curl-setopt.php#refsect1-function.curl-setopt-changelog - */ - if (version_compare(PHP_VERSION, '5.5.0') < 0) - $downloaded = $download_size; - - $download_size = $remoteEditionData['length']; - if ($download_size && !$progressBarFinish) if ($downloaded < $download_size) - $progressBar->setProgress(round(($downloaded / $download_size) * 100,0,PHP_ROUND_HALF_DOWN)); + $progressBar->setProgress(round(($downloaded / $download_size) * 100, 0, PHP_ROUND_HALF_DOWN)); else { $progressBar->finish(); $progressBarFinish = true; @@ -75,10 +67,10 @@ protected function download($remoteEditionData) $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); fclose($fh); - if ($response === false || $httpCode !== 200){ - if(is_file($this->getArchiveFile($remoteEditionData))) + if ($response === false || $httpCode !== 200) { + if (is_file($this->getArchiveFile($remoteEditionData))) unlink($this->getArchiveFile($remoteEditionData)); - $this->_errorUpdateEditions[$remoteEditionData['id']] = "$editionId: download error. Remote server response code \"$httpCode\"."; + $this->_errorUpdateEditions[$remoteEditionData['edition_id']] = "$editionId: download error. Remote server response code \"$httpCode\"."; echo PHP_EOL; } }