Issues (3627)

CoreBundle/IpLookup/AbstractLocalDataLookup.php (1 issue)

1
<?php
2
3
/*
4
 * @copyright   2015 Mautic Contributors. All rights reserved
5
 * @author      Mautic
6
 *
7
 * @link        http://mautic.org
8
 *
9
 * @license     GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
10
 */
11
12
namespace Mautic\CoreBundle\IpLookup;
13
14
use Joomla\Http\HttpFactory;
15
use Mautic\CoreBundle\Form\Type\IpLookupDownloadDataStoreButtonType;
16
use PharData;
17
use PharFileInfo;
18
use RecursiveIteratorIterator;
19
20
/**
21
 * Class AbstractLocalDataLookup.
22
 */
23
abstract class AbstractLocalDataLookup extends AbstractLookup implements IpLookupFormInterface
24
{
25
    /**
26
     * @const TAR_CACHE_FOLDER
27
     */
28
    const TAR_CACHE_FOLDER = 'unpack';
29
30
    /**
31
     * @const TAR_TEMP_FILE
32
     */
33
    const TAR_TEMP_FILE = 'temp.tar.gz';
34
35
    /**
36
     * Path to the local data store.
37
     *
38
     * @return string
39
     */
40
    abstract public function getLocalDataStoreFilepath();
41
42
    /**
43
     * Return the URL to manually download.
44
     *
45
     * @return string
46
     */
47
    abstract public function getRemoteDateStoreDownloadUrl();
48
49
    /**
50
     * @return string
51
     */
52
    public function getConfigFormService()
53
    {
54
        return IpLookupDownloadDataStoreButtonType::class;
55
    }
56
57
    /**
58
     * {@inheritdoc}
59
     *
60
     * @return array
61
     */
62
    public function getConfigFormThemes()
63
    {
64
        return [];
65
    }
66
67
    /**
68
     * Download remote data store.
69
     *
70
     * Used by the mautic:iplookup:update_data command and form fetch button (if applicable) to update local IP data stores
71
     *
72
     * @return bool
73
     */
74
    public function downloadRemoteDataStore()
75
    {
76
        $connector = HttpFactory::getHttp();
77
        $package   = $this->getRemoteDateStoreDownloadUrl();
78
79
        try {
80
            $data = $connector->get($package);
81
        } catch (\Exception $exception) {
82
            $this->logger->error('Failed to fetch remote IP data: '.$exception->getMessage());
83
        }
84
85
        $tempTarget        = $this->cacheDir.'/'.basename($package);
86
        $tempExt           = strtolower(pathinfo($package, PATHINFO_EXTENSION));
87
        $localTarget       = $this->getLocalDataStoreFilepath();
88
        $localTargetExt    = strtolower(pathinfo($localTarget, PATHINFO_EXTENSION));
89
90
        try {
91
            $success = false;
92
93
            switch (true) {
94
                case $localTargetExt === $tempExt:
95
                    $success = (bool) file_put_contents($localTarget, $data->body);
96
97
                    break;
98
99
                case $this->endsWith($package, 'tar.gz'):
100
                    /**
101
                     * If tar.gz it loops whole folder structure and copy the file which has the same basename as
102
                     * desired localTarget.
103
                     */
104
                    $tempTargetFolder = $this->cacheDir.'/'.self::TAR_CACHE_FOLDER;
105
                    $temporaryPhar    = $tempTargetFolder.'/'.self::TAR_TEMP_FILE;
106
                    if (!is_dir($tempTargetFolder)) {
107
                        // dir doesn't exist, make it
108
                        mkdir($tempTargetFolder);
109
                    }
110
                    file_put_contents($temporaryPhar, $data->body);
111
                    $pharData = new PharData($temporaryPhar);
112
                    foreach (new RecursiveIteratorIterator($pharData) as $file) {
113
                        /** @var PharFileInfo $file */
114
                        if ($file->getBasename() === basename($localTarget)) {
115
                            $success = copy($file->getPathname(), $localTarget);
116
                        }
117
                    }
118
                    @unlink($temporaryPhar);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

118
                    /** @scrutinizer ignore-unhandled */ @unlink($temporaryPhar);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
119
120
                    break;
121
122
                case 'gz' == $tempExt:
123
                    $memLimit = $this->sizeInByte(ini_get('memory_limit'));
124
                    $freeMem  = $memLimit - memory_get_peak_usage();
125
                    //check whether there is enough memory to handle large iplookp DB
126
                    // or will throw iplookup exception
127
                    if (function_exists('gzdecode') && strlen($data->body) < ($freeMem / 3)) {
128
                        $success = (bool) file_put_contents($localTarget, gzdecode($data->body));
129
                    } elseif (function_exists('gzopen')) {
130
                        if (file_put_contents($tempTarget, $data->body)) {
131
                            $bufferSize = 4096; // read 4kb at a time
132
                            $file       = gzopen($tempTarget, 'rb');
133
                            $outFile    = fopen($localTarget, 'wb');
134
                            while (!gzeof($file)) {
135
                                fwrite($outFile, gzread($file, $bufferSize));
136
                            }
137
                            fclose($outFile);
138
                            gzclose($file);
139
                            @unlink($tempTarget);
140
                            $success = true;
141
                        }
142
                    }
143
144
                    break;
145
146
                case 'zip' == $tempExt:
147
                    file_put_contents($tempTarget, $data->body);
148
149
                    $zipper = new \ZipArchive();
150
151
                    $zipper->open($tempTarget);
152
                    $success = $zipper->extractTo($localTarget);
153
                    $zipper->close();
154
                    @unlink($tempTarget);
155
                    break;
156
            }
157
        } catch (\Exception $exception) {
158
            error_log($exception);
159
160
            $success = false;
161
        }
162
163
        return $success;
164
    }
165
166
    /**
167
     * Get the common directory for data.
168
     *
169
     * @return string|null
170
     */
171
    protected function getDataDir()
172
    {
173
        if (null !== $this->cacheDir) {
174
            if (!file_exists($this->cacheDir)) {
175
                mkdir($this->cacheDir);
176
            }
177
178
            $dataDir = $this->cacheDir.'/../ip_data';
179
180
            if (!file_exists($dataDir)) {
181
                mkdir($dataDir);
182
            }
183
184
            return $dataDir;
185
        }
186
187
        return null;
188
    }
189
190
    protected function sizeInByte($size)
191
    {
192
        $data = (int) substr($size, 0, -1);
193
        switch (strtoupper(substr($size, -1))) {
194
            case 'K':
195
                return $data * 1024;
196
            case 'M':
197
                return $data * 1024 * 1024;
198
            case 'G':
199
                return $data * 1024 * 1024 * 1024;
200
        }
201
    }
202
203
    /**
204
     * Get if the string ends with.
205
     *
206
     * @param string $haystack
207
     * @param string $needle
208
     *
209
     * @return bool
210
     */
211
    private function endsWith($haystack, $needle)
212
    {
213
        return 0 === substr_compare($haystack, $needle, -strlen($needle));
214
    }
215
}
216