From e1f103aafc108afeabb132935f93504ebe96c1c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Br=C3=A4ndle?= Date: Thu, 2 May 2024 15:52:48 +0200 Subject: [PATCH] Usage statistics with GeoIP2 --- GeoLocationTool.inc.php | 113 ++++++----------- README | 46 ++++--- composer.json | 5 + composer.lock | 269 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 347 insertions(+), 86 deletions(-) create mode 100644 composer.json create mode 100644 composer.lock diff --git a/GeoLocationTool.inc.php b/GeoLocationTool.inc.php index 076a46e..94e8ac3 100644 --- a/GeoLocationTool.inc.php +++ b/GeoLocationTool.inc.php @@ -13,8 +13,8 @@ * @brief Geo location by ip wrapper class. */ -/** GeoIp tool for geo location based on ip */ -include('lib' . DIRECTORY_SEPARATOR . 'geoIp' . DIRECTORY_SEPARATOR . 'geoipcity.inc'); +require_once 'vendor/autoload.php'; +use GeoIp2\Database\Reader; class GeoLocationTool { @@ -30,10 +30,10 @@ class GeoLocationTool { * Use the method isPresent() to check if the database file is present before use. */ function __construct() { - $geoLocationDbFile = dirname(__FILE__) . DIRECTORY_SEPARATOR . "GeoLiteCity.dat"; + $geoLocationDbFile = dirname(__FILE__) . DIRECTORY_SEPARATOR . "GeoLite2-City.mmdb"; if (file_exists($geoLocationDbFile)) { $isDbFilePresent = true; - $this->_geoLocationTool = geoip_open($geoLocationDbFile, GEOIP_STANDARD); + $this->_geoLocationTool = new Reader($geoLocationDbFile); include('lib' . DIRECTORY_SEPARATOR . 'geoIp' . DIRECTORY_SEPARATOR . 'geoipregionvars.php'); $this->_regionName = $GEOIP_REGION_NAME; } else { @@ -64,21 +64,19 @@ function getGeoLocation($ip) { // If no geolocation tool, the geo database file is missing. if (!$this->_geoLocationTool) return array(null, null, null); - $record = geoip_record_by_addr($this->_geoLocationTool, $ip); - - if (!$record) { + try { + $record = $this->_geoLocationTool->city($ip); + } catch (GeoIp2\Exception\AddressNotFoundException $e) { return array(null, null, null); } - - $regionName = null; - if(isset($this->_regionName[$record->country_code][$record->region])) { - $regionName = $this->_regionName[$record->country_code][$record->region]; - } + + $regionIsoCode = $record->mostSpecificSubdivision->isoCode; + if (strlen($regionIsoCode) > 2) $regionIsoCode = ''; return array( - $record->country_code, - utf8_encode($record->city), - $record->region + $record->country->isoCode, + utf8_encode($record->city->name), + $regionIsoCode ); } @@ -89,8 +87,34 @@ function getGeoLocation($ip) { function getAllCountryCodes() { if (!$this->_geoLocationTool) return null; - $tool = $this->_geoLocationTool; - $countryCodes = $tool->GEOIP_COUNTRY_CODES; + $countryCodes = array( + "","AP","EU","AD","AE","AF","AG","AI","AL","AM","CW", + "AO","AQ","AR","AS","AT","AU","AW","AZ","BA","BB", + "BD","BE","BF","BG","BH","BI","BJ","BM","BN","BO", + "BR","BS","BT","BV","BW","BY","BZ","CA","CC","CD", + "CF","CG","CH","CI","CK","CL","CM","CN","CO","CR", + "CU","CV","CX","CY","CZ","DE","DJ","DK","DM","DO", + "DZ","EC","EE","EG","EH","ER","ES","ET","FI","FJ", + "FK","FM","FO","FR","SX","GA","GB","GD","GE","GF", + "GH","GI","GL","GM","GN","GP","GQ","GR","GS","GT", + "GU","GW","GY","HK","HM","HN","HR","HT","HU","ID", + "IE","IL","IN","IO","IQ","IR","IS","IT","JM","JO", + "JP","KE","KG","KH","KI","KM","KN","KP","KR","KW", + "KY","KZ","LA","LB","LC","LI","LK","LR","LS","LT", + "LU","LV","LY","MA","MC","MD","MG","MH","MK","ML", + "MM","MN","MO","MP","MQ","MR","MS","MT","MU","MV", + "MW","MX","MY","MZ","NA","NC","NE","NF","NG","NI", + "NL","NO","NP","NR","NU","NZ","OM","PA","PE","PF", + "PG","PH","PK","PL","PM","PN","PR","PS","PT","PW", + "PY","QA","RE","RO","RU","RW","SA","SB","SC","SD", + "SE","SG","SH","SI","SJ","SK","SL","SM","SN","SO", + "SR","ST","SV","SY","SZ","TC","TD","TF","TG","TH", + "TJ","TK","TM","TN","TO","TL","TR","TT","TV","TW", + "TZ","UA","UG","UM","US","UY","UZ","VA","VC","VE", + "VG","VI","VN","VU","WF","WS","YE","YT","RS","ZA", + "ZM","ME","ZW","A1","A2","O1","AX","GG","IM","JE", + "BL","MF", "BQ" + ); // Overwrite the first empty record with the code to // unknow country. @@ -98,25 +122,6 @@ function getAllCountryCodes() { return $countryCodes; } - /** - * Return the 3 letters version of country codes - * based on the passed 2 letters version. - * @param $countryCode string - * @return mixed string or null - */ - function get3LettersCountryCode($countryCode) { - return $this->_getCountryCodeOnList($countryCode, 'GEOIP_COUNTRY_CODES3'); - } - - /** - * Return the 2 letter version of country codes - * based on the passed 3 letters version. - * @param $countryCode3 string - * @return mixed string or null - */ - function get2LettersCountryCode($countryCode3) { - return $this->_getCountryCodeOnList($countryCode3, 'GEOIP_COUNTRY_CODES'); - } /** * Get regions by country. @@ -132,41 +137,5 @@ function getRegions($countryId) { return $regions; } - - /** - * Get the passed country code inside the passed - * list. - * @param $countryCode The 2 letters country code. - * @param $countryCodeList array Any geoip country - * code list. - * @return mixed String or null. - */ - function _getCountryCodeOnList($countryCode, $countryCodeListName) { - $returner = null; - - if (!$this->_geoLocationTool) return $returner; - $tool = $this->_geoLocationTool; - - if (isset($tool->$countryCodeListName)) { - $countryCodeList = $tool->$countryCodeListName; - } else { - return $returner; - } - - $countryCodesIndex = $tool->GEOIP_COUNTRY_CODE_TO_NUMBER; - $countryCodeIndex = null; - - if (isset($countryCodesIndex[$countryCode])) { - $countryCodeIndex = $countryCodesIndex[$countryCode]; - } - - if ($countryCodeIndex) { - if (isset($countryCodeList[$countryCodeIndex])) { - $returner = $countryCodeList[$countryCodeIndex]; - } - } - - return $returner; - } } diff --git a/README b/README index fbc9b11..07e0b94 100644 --- a/README +++ b/README @@ -45,23 +45,41 @@ The only needed additional step depends on whether you want to have geo location data in your statistics or not. If that's the case, follow these steps: -Linux: -1 - open a shell prompt -2 - go into OJS i.e. OMP installation’s base directory -2 - wget http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz -3 - gunzip GeoLiteCity.dat.gz -4 - mv GeoLiteCity.dat plugins/generic/usageStats -Windows -1 - download the file http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz -2 - decompress it using any decompression tool into plugins/generic/usageStats directory +Signup for Geolite2 Free Geolocation Data + +1 - You will need a free GeoLite2 account to download the GeoLite2 databases. + For signing up, follow the instructions at + https://dev.maxmind.com/geoip/geolite2-free-geolocation-data#accessing-geolite2-free-geolocation-data +2 - For Linux, it is recommended to generate a license key, which can be used to automate + Geolite2 binary database downloads with the geoipupdate program + +Linux +1 - Let your Linux administrator install the geoipupdate program using package manager of + your Linux distribution. The geoipupdate program is usually stored in /usr/bin/geoipupdate +2 - open a shell prompt +3 - create a directory geoip outside of your OJS i.e. OMP installation’s base directory +4 - obtain the GeoIP.conf file with your license key according to + https://dev.maxmind.com/geoip/updating-databases/#2-obtain-geoipconf-with-account-information + and store it in your geoip directory created in step 4 +5 - give {ojs_root}/plugins/generic/usageStats write access for the apache user, e.g. + chmod g+w {ojs_root}/plugins/generic/usageStats +6 - /usr/bin/geoipupdate -v -f {geoip_directory}/GeoIP.conf -d {ojs_root}/plugins/generic/usageStats + This downloads the GeoLite2-ASN.mmdb, GeoLite2-City.mmdb and GeoLite2-Country.mmdb databases + to your plugins/generic/usageStats directory +7 - You may automate monthly updates of your Geolite2 databases using a cronjob. Add an entry to the + cron table like this: + # Download GeoIP2 databases every month + 30 7 1 * * /usr/bin/geoipupdate -v -f {geoip_directory}/GeoIP.conf -d {ojs_root}/plugins/generic/usageStats + In order that this works, give the apache user write permissions for the following files: + {ojs_root}/plugins/generic/usageStats/.geoipupdate.lock + {ojs_root}/plugins/generic/usageStats/GeoLite2-ASN.mmdb + {ojs_root}/plugins/generic/usageStats/GeoLite2-City.mmdb + {ojs_root}/plugins/generic/usageStats/GeoLite2-Country.mmdb -In both cases the complete path to the installed database file should be -plugins/generic/usageStats/GeoLiteCity.dat -Note, that a separate license agreement is required for this use of this database. -For details, see: - https://dev.maxmind.com/geoip/legacy/geolite/ +Windows +(add steps here) Management diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..bdcb887 --- /dev/null +++ b/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "geoip2/geoip2": "~2.0" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..66c8948 --- /dev/null +++ b/composer.lock @@ -0,0 +1,269 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "25737d43de99fb5d718029bd29d5374e", + "packages": [ + { + "name": "composer/ca-bundle", + "version": "1.5.0", + "source": { + "type": "git", + "url": "https://github.com/composer/ca-bundle.git", + "reference": "0c5ccfcfea312b5c5a190a21ac5cef93f74baf99" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/0c5ccfcfea312b5c5a190a21ac5cef93f74baf99", + "reference": "0c5ccfcfea312b5c5a190a21ac5cef93f74baf99", + "shasum": "" + }, + "require": { + "ext-openssl": "*", + "ext-pcre": "*", + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.10", + "psr/log": "^1.0", + "symfony/phpunit-bridge": "^4.2 || ^5", + "symfony/process": "^4.0 || ^5.0 || ^6.0 || ^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\CaBundle\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.", + "keywords": [ + "cabundle", + "cacert", + "certificate", + "ssl", + "tls" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/ca-bundle/issues", + "source": "https://github.com/composer/ca-bundle/tree/1.5.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-03-15T14:00:32+00:00" + }, + { + "name": "geoip2/geoip2", + "version": "v2.13.0", + "source": { + "type": "git", + "url": "https://github.com/maxmind/GeoIP2-php.git", + "reference": "6a41d8fbd6b90052bc34dff3b4252d0f88067b23" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/maxmind/GeoIP2-php/zipball/6a41d8fbd6b90052bc34dff3b4252d0f88067b23", + "reference": "6a41d8fbd6b90052bc34dff3b4252d0f88067b23", + "shasum": "" + }, + "require": { + "ext-json": "*", + "maxmind-db/reader": "~1.8", + "maxmind/web-service-common": "~0.8", + "php": ">=7.2" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "3.*", + "phpstan/phpstan": "*", + "phpunit/phpunit": "^8.0 || ^9.0", + "squizlabs/php_codesniffer": "3.*" + }, + "type": "library", + "autoload": { + "psr-4": { + "GeoIp2\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Gregory J. Oschwald", + "email": "goschwald@maxmind.com", + "homepage": "https://www.maxmind.com/" + } + ], + "description": "MaxMind GeoIP2 PHP API", + "homepage": "https://github.com/maxmind/GeoIP2-php", + "keywords": [ + "IP", + "geoip", + "geoip2", + "geolocation", + "maxmind" + ], + "support": { + "issues": "https://github.com/maxmind/GeoIP2-php/issues", + "source": "https://github.com/maxmind/GeoIP2-php/tree/v2.13.0" + }, + "time": "2022-08-05T20:32:58+00:00" + }, + { + "name": "maxmind-db/reader", + "version": "v1.11.1", + "source": { + "type": "git", + "url": "https://github.com/maxmind/MaxMind-DB-Reader-php.git", + "reference": "1e66f73ffcf25e17c7a910a1317e9720a95497c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/maxmind/MaxMind-DB-Reader-php/zipball/1e66f73ffcf25e17c7a910a1317e9720a95497c7", + "reference": "1e66f73ffcf25e17c7a910a1317e9720a95497c7", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "conflict": { + "ext-maxminddb": "<1.11.1,>=2.0.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "3.*", + "php-coveralls/php-coveralls": "^2.1", + "phpstan/phpstan": "*", + "phpunit/phpcov": ">=6.0.0", + "phpunit/phpunit": ">=8.0.0,<10.0.0", + "squizlabs/php_codesniffer": "3.*" + }, + "suggest": { + "ext-bcmath": "bcmath or gmp is required for decoding larger integers with the pure PHP decoder", + "ext-gmp": "bcmath or gmp is required for decoding larger integers with the pure PHP decoder", + "ext-maxminddb": "A C-based database decoder that provides significantly faster lookups" + }, + "type": "library", + "autoload": { + "psr-4": { + "MaxMind\\Db\\": "src/MaxMind/Db" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Gregory J. Oschwald", + "email": "goschwald@maxmind.com", + "homepage": "https://www.maxmind.com/" + } + ], + "description": "MaxMind DB Reader API", + "homepage": "https://github.com/maxmind/MaxMind-DB-Reader-php", + "keywords": [ + "database", + "geoip", + "geoip2", + "geolocation", + "maxmind" + ], + "support": { + "issues": "https://github.com/maxmind/MaxMind-DB-Reader-php/issues", + "source": "https://github.com/maxmind/MaxMind-DB-Reader-php/tree/v1.11.1" + }, + "time": "2023-12-02T00:09:23+00:00" + }, + { + "name": "maxmind/web-service-common", + "version": "v0.9.0", + "source": { + "type": "git", + "url": "https://github.com/maxmind/web-service-common-php.git", + "reference": "4dc5a3e8df38aea4ca3b1096cee3a038094e9b53" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/maxmind/web-service-common-php/zipball/4dc5a3e8df38aea4ca3b1096cee3a038094e9b53", + "reference": "4dc5a3e8df38aea4ca3b1096cee3a038094e9b53", + "shasum": "" + }, + "require": { + "composer/ca-bundle": "^1.0.3", + "ext-curl": "*", + "ext-json": "*", + "php": ">=7.2" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "3.*", + "phpstan/phpstan": "*", + "phpunit/phpunit": "^8.0 || ^9.0", + "squizlabs/php_codesniffer": "3.*" + }, + "type": "library", + "autoload": { + "psr-4": { + "MaxMind\\Exception\\": "src/Exception", + "MaxMind\\WebService\\": "src/WebService" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Gregory Oschwald", + "email": "goschwald@maxmind.com" + } + ], + "description": "Internal MaxMind Web Service API", + "homepage": "https://github.com/maxmind/web-service-common-php", + "support": { + "issues": "https://github.com/maxmind/web-service-common-php/issues", + "source": "https://github.com/maxmind/web-service-common-php/tree/v0.9.0" + }, + "time": "2022-03-28T17:43:20+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "plugin-api-version": "2.0.0" +}