PageRenderTime 49ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/plugins/UserCountry/LocationProvider/GeoIp.php

https://github.com/CodeYellowBV/piwik
PHP | 274 lines | 143 code | 32 blank | 99 comment | 22 complexity | feedad1b40a69be69a3d50e7da4e1a7a MD5 | raw file
Possible License(s): LGPL-3.0, JSON, MIT, GPL-3.0, LGPL-2.1, GPL-2.0, AGPL-1.0, BSD-2-Clause, BSD-3-Clause
  1. <?php
  2. /**
  3. * Piwik - free/libre analytics platform
  4. *
  5. * @link http://piwik.org
  6. * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
  7. *
  8. */
  9. namespace Piwik\Plugins\UserCountry\LocationProvider;
  10. use Exception;
  11. use Piwik\Piwik;
  12. use Piwik\Plugins\UserCountry\LocationProvider;
  13. /**
  14. * Base type for all GeoIP LocationProviders.
  15. *
  16. */
  17. abstract class GeoIp extends LocationProvider
  18. {
  19. /* For testing, use: 'http://piwik-team.s3.amazonaws.com/GeoLiteCity.dat.gz' */
  20. const GEO_LITE_URL = 'http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz';
  21. const TEST_IP = '194.57.91.215';
  22. public static $geoIPDatabaseDir = 'misc';
  23. /**
  24. * Stores possible database file names categorized by the type of information
  25. * GeoIP databases hold.
  26. *
  27. * @var array
  28. */
  29. public static $dbNames = array(
  30. 'loc' => array('GeoIPCity.dat', 'GeoLiteCity.dat', 'GeoIP.dat'),
  31. 'isp' => array('GeoIPISP.dat'),
  32. 'org' => array('GeoIPOrg.dat'),
  33. );
  34. /**
  35. * Cached region name array. Data is from geoipregionvars.php.
  36. *
  37. * @var array
  38. */
  39. private static $regionNames = null;
  40. /**
  41. * Attempts to fill in some missing information in a GeoIP location.
  42. *
  43. * This method will call LocationProvider::completeLocationResult and then
  44. * try to set the region name of the location if the country code & region
  45. * code are set.
  46. *
  47. * @param array $location The location information to modify.
  48. */
  49. public function completeLocationResult(&$location)
  50. {
  51. $this->fixupLocation($location);
  52. parent::completeLocationResult($location);
  53. // set region name if region code is set
  54. if (empty($location[self::REGION_NAME_KEY])
  55. && !empty($location[self::REGION_CODE_KEY])
  56. && !empty($location[self::COUNTRY_CODE_KEY])
  57. ) {
  58. $countryCode = $location[self::COUNTRY_CODE_KEY];
  59. $regionCode = (string)$location[self::REGION_CODE_KEY];
  60. $location[self::REGION_NAME_KEY] = self::getRegionNameFromCodes($countryCode, $regionCode);
  61. }
  62. }
  63. /**
  64. * Fix up data to work with our SVG maps which include 'Tib' boundaries
  65. */
  66. protected function fixupLocation(&$location)
  67. {
  68. if (!empty($location[self::REGION_CODE_KEY])
  69. && $location[self::REGION_CODE_KEY] == '14'
  70. && !empty($location[self::COUNTRY_CODE_KEY])
  71. && strtoupper($location[self::COUNTRY_CODE_KEY]) == 'CN'
  72. ) {
  73. $location[self::COUNTRY_CODE_KEY] = 'ti';
  74. $location[self::REGION_CODE_KEY] = '1';
  75. }
  76. }
  77. /**
  78. * Returns true if this provider has been setup correctly, the error message if
  79. * otherwise.
  80. *
  81. * @return bool|string
  82. */
  83. public function isWorking()
  84. {
  85. // test with an example IP to make sure the provider is working
  86. // NOTE: At the moment only country, region & city info is tested.
  87. try {
  88. $supportedInfo = $this->getSupportedLocationInfo();
  89. list($testIp, $expectedResult) = self::getTestIpAndResult();
  90. // get location using test IP
  91. $location = $this->getLocation(array('ip' => $testIp));
  92. // check that result is the same as expected
  93. $isResultCorrect = true;
  94. foreach ($expectedResult as $key => $value) {
  95. // if this provider is not configured to support this information type, skip it
  96. if (empty($supportedInfo[$key])) {
  97. continue;
  98. }
  99. if (empty($location[$key])
  100. || $location[$key] != $value
  101. ) {
  102. $isResultCorrect = false;
  103. }
  104. }
  105. if (!$isResultCorrect) {
  106. $unknown = Piwik::translate('General_Unknown');
  107. $location = "'"
  108. . (empty($location[self::CITY_NAME_KEY]) ? $unknown : $location[self::CITY_NAME_KEY])
  109. . ", "
  110. . (empty($location[self::REGION_CODE_KEY]) ? $unknown : $location[self::REGION_CODE_KEY])
  111. . ", "
  112. . (empty($location[self::COUNTRY_CODE_KEY]) ? $unknown : $location[self::COUNTRY_CODE_KEY])
  113. . "'";
  114. $expectedLocation = "'" . $expectedResult[self::CITY_NAME_KEY] . ", "
  115. . $expectedResult[self::REGION_CODE_KEY] . ", "
  116. . $expectedResult[self::COUNTRY_CODE_KEY] . "'";
  117. $bind = array($testIp, $location, $expectedLocation);
  118. return Piwik::translate('UserCountry_TestIPLocatorFailed', $bind);
  119. }
  120. return true;
  121. } catch (Exception $ex) {
  122. return $ex->getMessage();
  123. }
  124. }
  125. /**
  126. * Returns a region name for a country code + region code.
  127. *
  128. * @param string $countryCode
  129. * @param string $regionCode
  130. * @return string The region name or 'Unknown' (translated).
  131. */
  132. public static function getRegionNameFromCodes($countryCode, $regionCode)
  133. {
  134. $regionNames = self::getRegionNames();
  135. $countryCode = strtoupper($countryCode);
  136. $regionCode = strtoupper($regionCode);
  137. if (isset($regionNames[$countryCode][$regionCode])) {
  138. return $regionNames[$countryCode][$regionCode];
  139. } else {
  140. return Piwik::translate('General_Unknown');
  141. }
  142. }
  143. /**
  144. * Returns an array of region names mapped by country code & region code.
  145. *
  146. * @return array
  147. */
  148. public static function getRegionNames()
  149. {
  150. if (is_null(self::$regionNames)) {
  151. $GEOIP_REGION_NAME = array();
  152. require_once PIWIK_INCLUDE_PATH . '/libs/MaxMindGeoIP/geoipregionvars.php';
  153. self::$regionNames = $GEOIP_REGION_NAME;
  154. }
  155. return self::$regionNames;
  156. }
  157. /**
  158. * Returns the path of an existing GeoIP database or false if none can be found.
  159. *
  160. * @param array $possibleFileNames The list of possible file names for the GeoIP database.
  161. * @return string|false
  162. */
  163. public static function getPathToGeoIpDatabase($possibleFileNames)
  164. {
  165. foreach ($possibleFileNames as $filename) {
  166. $path = self::getPathForGeoIpDatabase($filename);
  167. if (file_exists($path)) {
  168. return $path;
  169. }
  170. }
  171. return false;
  172. }
  173. /**
  174. * Returns full path for a GeoIP database managed by Piwik.
  175. *
  176. * @param string $filename Name of the .dat file.
  177. * @return string
  178. */
  179. public static function getPathForGeoIpDatabase($filename)
  180. {
  181. return PIWIK_INCLUDE_PATH . '/' . self::$geoIPDatabaseDir . '/' . $filename;
  182. }
  183. /**
  184. * Returns test IP used by isWorking and expected result.
  185. *
  186. * @return array eg. array('1.2.3.4', array(self::COUNTRY_CODE_KEY => ...))
  187. */
  188. private static function getTestIpAndResult()
  189. {
  190. static $result = null;
  191. if (is_null($result)) {
  192. // TODO: what happens when IP changes? should we get this information from piwik.org?
  193. $expected = array(self::COUNTRY_CODE_KEY => 'FR',
  194. self::REGION_CODE_KEY => 'A6',
  195. self::CITY_NAME_KEY => 'Besanรงon');
  196. $result = array(self::TEST_IP, $expected);
  197. }
  198. return $result;
  199. }
  200. /**
  201. * Returns true if there is a GeoIP database in the 'misc' directory.
  202. *
  203. * @return bool
  204. */
  205. public static function isDatabaseInstalled()
  206. {
  207. return self::getPathToGeoIpDatabase(self::$dbNames['loc'])
  208. || self::getPathToGeoIpDatabase(self::$dbNames['isp'])
  209. || self::getPathToGeoIpDatabase(self::$dbNames['org']);
  210. }
  211. /**
  212. * Returns the type of GeoIP database ('loc', 'isp' or 'org') based on the
  213. * filename (eg, 'GeoLiteCity.dat', 'GeoIPISP.dat', etc).
  214. *
  215. * @param string $filename
  216. * @return string|false 'loc', 'isp', 'org', or false if cannot find a database
  217. * type.
  218. */
  219. public static function getGeoIPDatabaseTypeFromFilename($filename)
  220. {
  221. foreach (self::$dbNames as $key => $names) {
  222. foreach ($names as $name) {
  223. if ($name === $filename) {
  224. return $key;
  225. }
  226. }
  227. }
  228. return false;
  229. }
  230. }
  231. /**
  232. * @see plugins/UserCountry/LocationProvider/GeoIp/ServerBased.php
  233. */
  234. require_once PIWIK_INCLUDE_PATH . '/plugins/UserCountry/LocationProvider/GeoIp/ServerBased.php';
  235. /**
  236. * @see plugins/UserCountry/LocationProvider/GeoIp/Php.php
  237. */
  238. require_once PIWIK_INCLUDE_PATH . '/plugins/UserCountry/LocationProvider/GeoIp/Php.php';
  239. /**
  240. * @see plugins/UserCountry/LocationProvider/GeoIp/Pecl.php
  241. */
  242. require_once PIWIK_INCLUDE_PATH . '/plugins/UserCountry/LocationProvider/GeoIp/Pecl.php';