Spaces:
No application file
No application file
| namespace Mautic\CoreBundle\IpLookup; | |
| use GuzzleHttp\RequestOptions; | |
| use Mautic\CoreBundle\Form\Type\IpLookupDownloadDataStoreButtonType; | |
| abstract class AbstractLocalDataLookup extends AbstractLookup implements IpLookupFormInterface | |
| { | |
| /** | |
| * @const TAR_CACHE_FOLDER | |
| */ | |
| public const TAR_CACHE_FOLDER = 'unpack'; | |
| /** | |
| * @const TAR_TEMP_FILE | |
| */ | |
| public const TAR_TEMP_FILE = 'temp.tar.gz'; | |
| /** | |
| * Path to the local data store. | |
| * | |
| * @return string | |
| */ | |
| abstract public function getLocalDataStoreFilepath(); | |
| /** | |
| * Return the URL to manually download. | |
| * | |
| * @return string | |
| */ | |
| abstract public function getRemoteDateStoreDownloadUrl(); | |
| /** | |
| * @return string | |
| */ | |
| public function getConfigFormService() | |
| { | |
| return IpLookupDownloadDataStoreButtonType::class; | |
| } | |
| /** | |
| * @return array | |
| */ | |
| public function getConfigFormThemes() | |
| { | |
| return []; | |
| } | |
| /** | |
| * Download remote data store. | |
| * | |
| * Used by the mautic:iplookup:update_data command and form fetch button (if applicable) to update local IP data stores | |
| * | |
| * @return bool | |
| */ | |
| public function downloadRemoteDataStore() | |
| { | |
| $package = $this->getRemoteDateStoreDownloadUrl(); | |
| if (empty($package)) { | |
| $this->logger->error('Failed to fetch remote IP data: Invalid or inactive MaxMind license key'); | |
| return false; | |
| } | |
| try { | |
| $data = $this->client->get($package, [ | |
| RequestOptions::ALLOW_REDIRECTS => true, | |
| ]); | |
| } catch (\Exception $exception) { | |
| $this->logger->error('Failed to fetch remote IP data: '.$exception->getMessage()); | |
| } | |
| $tempTarget = $this->cacheDir.'/'.basename($package); | |
| $tempExt = strtolower(pathinfo($package, PATHINFO_EXTENSION)); | |
| $localTarget = $this->getLocalDataStoreFilepath(); | |
| $localTargetExt = strtolower(pathinfo($localTarget, PATHINFO_EXTENSION)); | |
| try { | |
| $success = false; | |
| switch (true) { | |
| case $localTargetExt === $tempExt: | |
| $success = (bool) file_put_contents($localTarget, $data->getBody()); | |
| break; | |
| case $this->endsWith($package, 'tar.gz'): | |
| /** | |
| * If tar.gz it loops whole folder structure and copy the file which has the same basename as | |
| * desired localTarget. | |
| */ | |
| $tempTargetFolder = $this->cacheDir.'/'.self::TAR_CACHE_FOLDER; | |
| $temporaryPhar = $tempTargetFolder.'/'.self::TAR_TEMP_FILE; | |
| if (!is_dir($tempTargetFolder)) { | |
| // dir doesn't exist, make it | |
| mkdir($tempTargetFolder); | |
| } | |
| file_put_contents($temporaryPhar, $data->getBody()); | |
| $pharData = new \PharData($temporaryPhar); | |
| foreach (new \RecursiveIteratorIterator($pharData) as $file) { | |
| /** @var \PharFileInfo $file */ | |
| if ($file->getBasename() === basename($localTarget)) { | |
| $success = copy($file->getPathname(), $localTarget); | |
| } | |
| } | |
| @unlink($temporaryPhar); | |
| break; | |
| case 'gz' == $tempExt: | |
| $memLimit = $this->sizeInByte(ini_get('memory_limit')); | |
| $freeMem = $memLimit - memory_get_peak_usage(); | |
| // check whether there is enough memory to handle large iplookp DB | |
| // or will throw iplookup exception | |
| if (function_exists('gzdecode') && strlen($data->getBody()) < ($freeMem / 3)) { | |
| $success = (bool) file_put_contents($localTarget, gzdecode($data->getBody())); | |
| } elseif (function_exists('gzopen')) { | |
| if (file_put_contents($tempTarget, $data->getBody())) { | |
| $bufferSize = 4096; // read 4kb at a time | |
| $file = gzopen($tempTarget, 'rb'); | |
| $outFile = fopen($localTarget, 'wb'); | |
| while (!gzeof($file)) { | |
| fwrite($outFile, gzread($file, $bufferSize)); | |
| } | |
| fclose($outFile); | |
| gzclose($file); | |
| @unlink($tempTarget); | |
| $success = true; | |
| } | |
| } | |
| break; | |
| case 'zip' == $tempExt: | |
| file_put_contents($tempTarget, $data->getBody()); | |
| $zipper = new \ZipArchive(); | |
| $zipper->open($tempTarget); | |
| $success = $zipper->extractTo($localTarget); | |
| $zipper->close(); | |
| @unlink($tempTarget); | |
| break; | |
| } | |
| } catch (\Exception $exception) { | |
| error_log($exception); | |
| $success = false; | |
| } | |
| return $success; | |
| } | |
| /** | |
| * Get the common directory for data. | |
| * | |
| * @return string|null | |
| */ | |
| protected function getDataDir() | |
| { | |
| if (null !== $this->cacheDir) { | |
| if (!file_exists($this->cacheDir)) { | |
| mkdir($this->cacheDir); | |
| } | |
| $dataDir = $this->cacheDir.'/../ip_data'; | |
| if (!file_exists($dataDir)) { | |
| mkdir($dataDir); | |
| } | |
| return $dataDir; | |
| } | |
| return null; | |
| } | |
| protected function sizeInByte($size) | |
| { | |
| $data = (int) substr($size, 0, -1); | |
| switch (strtoupper(substr($size, -1))) { | |
| case 'K': | |
| return $data * 1024; | |
| case 'M': | |
| return $data * 1024 * 1024; | |
| case 'G': | |
| return $data * 1024 * 1024 * 1024; | |
| } | |
| } | |
| /** | |
| * Get if the string ends with. | |
| * | |
| * @param string $haystack | |
| * @param string $needle | |
| */ | |
| private function endsWith($haystack, $needle): bool | |
| { | |
| return str_ends_with($haystack, $needle); | |
| } | |
| } | |