Issues (3627)

CoreBundle/IpLookup/AbstractLocalDataLookup.php (3 issues)

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));
0 ignored issues
show
It seems like pathinfo($package, Mauti...kup\PATHINFO_EXTENSION) can also be of type array; however, parameter $string of strtolower() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

86
        $tempExt           = strtolower(/** @scrutinizer ignore-type */ pathinfo($package, PATHINFO_EXTENSION));
Loading history...
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'):
0 ignored issues
show
$this->endsWith($package, 'tar.gz') is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
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