Test Setup Failed
Push — fix-qinst ( 0a57f0...9bf464 )
by Julien
03:08
created

Package::hasWizardDir()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 20
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 13
c 1
b 0
f 0
nc 5
nop 0
dl 0
loc 20
rs 8.8571
1
<?php
2
3
namespace SSpkS\Package;
4
5
/**
6
 * SPK Package class
7
 *
8
 * @property string $spk Path to SPK file
9
 * @property string $spk_url URL to SPK file
10
 * @property string $displayname Pretty printed name of package (falls back to $package if not present)
11
 * @property string $package Package name
12
 * @property string $version Package version
13
 * @property string $description Package description
14
 * @property string $maintainer Package maintainer
15
 * @property string $maintainer_url URL of maintainer's web page
16
 * @property string $distributor Package distributor
17
 * @property string $distributor_url URL of distributor's web page
18
 * @property array $arch List of supported architectures, or 'noarch'
19
 * @property array $thumbnail List of thumbnail files
20
 * @property array $thumbnail_url List of thumbnail URLs
21
 * @property array $snapshot List of screenshot files
22
 * @property array $snapshot_url List of screenshot URLs
23
 * @property bool $beta TRUE if this is a beta package.
24
 * @property string $firmware Minimum firmware needed on device.
25
 * @property string $install_dep_services Dependencies required by this package.
26
 * @property bool $silent_install Allow silent install
27
 * @property bool $silent_uninstall Allow silent uninstall
28
 * @property bool $silent_upgrade Allow silent upgrade
29
 */
30
class Package
31
{
32
    private $config;
33
    private $filepath;
34
    private $filepathNoExt;
35
    private $filename;
36
    private $filenameNoExt;
37
    private $metafile;
38
    private $wizfile;
39
    private $metadata;
40
41
    /**
42
     * @param \SSpkS\Config $config Config object
43
     * @param string $filename Filename of SPK file
44
     */
45
    public function __construct(\SSpkS\Config $config, $filename)
46
    {
47
        $this->config = $config;
48
        if (!preg_match('/\.spk$/', $filename)) {
49
            throw new \Exception('File ' . $filename . ' doesn\'t have .spk extension!');
50
        }
51
        if (!file_exists($filename)) {
52
            throw new \Exception('File ' . $filename . ' not found!');
53
        }
54
        $this->filepath      = $filename;
55
        $this->filename      = basename($filename);
56
        $this->filenameNoExt = basename($filename, '.spk');
57
        $this->filepathNoExt = $this->config->paths['cache'] . $this->filenameNoExt;
58
        $this->metafile      = $this->filepathNoExt . '.nfo';
59
        $this->wizfile      = $this->filepathNoExt . '.wiz';
60
    }
61
62
    /**
63
     * Getter magic method.
64
     *
65
     * @param string $name Name of requested value.
66
     * @return mixed Requested value.
67
     */
68
    public function __get($name)
69
    {
70
        $this->collectMetadata();
71
        return $this->metadata[$name];
72
    }
73
74
    /**
75
     * Setter magic method.
76
     *
77
     * @param string $name Name of variable to set.
78
     * @param mixed $value Value to set.
79
     */
80
    public function __set($name, $value)
81
    {
82
        $this->collectMetadata();
83
        $this->metadata[$name] = $value;
84
    }
85
86
    /**
87
     * Isset feature magic method.
88
     *
89
     * @param string $name Name of requested value.
90
     * @return bool TRUE if value exists, FALSE otherwise.
91
     */
92
    public function __isset($name)
93
    {
94
        $this->collectMetadata();
95
        return isset($this->metadata[$name]);
96
    }
97
98
    /**
99
     * Unset feature magic method.
100
     *
101
     * @param string $name Name of value to unset.
102
     */
103
    public function __unset($name)
104
    {
105
        $this->collectMetadata();
106
        unset($this->metadata[$name]);
107
    }
108
109
    /**
110
     * Parses boolean value ('yes', '1', 'true') into
111
     * boolean type.
112
     *
113
     * @param mixed $value Input value
114
     * @return bool Boolean interpretation of $value.
115
     */
116
    public function parseBool($value)
117
    {
118
        return in_array($value, array('true', 'yes', '1', 1));
119
    }
120
121
    /**
122
     * Checks if given property $prop exists and converts it
123
     * into a boolean value.
124
     *
125
     * @param string $prop Property to convert
126
     */
127
    private function fixBoolIfExist($prop)
128
    {
129
        if (isset($this->metadata[$prop])) {
130
            $this->metadata[$prop] = $this->parseBool($this->metadata[$prop]);
131
        }
132
    }
133
134
    /**
135
     * Gathers metadata from package. Extracts INFO file if neccessary.
136
     */
137
    private function collectMetadata()
138
    {
139
        if (!is_null($this->metadata)) {
140
            // metadata already collected
141
            return;
142
        }
143
        $this->extractIfMissing('INFO', $this->metafile);
144
        $this->metadata = parse_ini_file($this->metafile);
145
        if (!isset($this->metadata['displayname'])) {
146
            $this->metadata['displayname'] = $this->metadata['package'];
147
        }
148
        $this->metadata['spk'] = $this->filepath;
149
150
        // Convert architecture(s) to array, as multiple architectures can be specified
151
        $this->metadata['arch'] = explode(' ', $this->metadata['arch']);
152
153
        $this->fixBoolIfExist('silent_install');
154
        $this->fixBoolIfExist('silent_uninstall');
155
        $this->fixBoolIfExist('silent_upgrade');
156
157
        if (isset($this->metadata['beta']) && in_array($this->metadata['beta'], array('true', '1', 'beta'))) {
158
            $this->metadata['beta'] = true;
159
        } else {
160
            $this->metadata['beta'] = false;
161
        }
162
163
        $qValues = $this->hasWizardDir()? false : true;
164
        $this->metadata['thumbnail'] = $this->getThumbnails();
165
        $this->metadata['snapshot']  = $this->getSnapshots();
166
        $this->metadata['qinst']     = $qValues;
167
        $this->metadata['qupgrade']  = $qValues;
168
        $this->metadata['qstart']    = $qValues;
169
    }
170
171
    /**
172
     * Returns metadata for this package.
173
     *
174
     * @return array Metadata.
175
     */
176
    public function getMetadata()
177
    {
178
        $this->collectMetadata();
179
        return $this->metadata;
180
    }
181
      
182
    /**
183
     * Extracts $inPkgName from package to $targetFile, if it doesn't
184
     * already exist. Needs the phar.so extension and allow_url_fopen.
185
     *
186
     * @param string $inPkgName Filename in package
187
     * @param string $targetFile Path to destination
188
     * @throws \Exception if the file couldn't get extracted.
189
     * @return bool TRUE if successful or no action needed.
190
     */
191
    public function extractIfMissing($inPkgName, $targetFile)
192
    {
193
        if (file_exists($targetFile)) {
194
            // Everything in working order
195
            return true;
196
        }
197
        // Try to extract file
198
        $tmp_dir = sys_get_temp_dir();
199
        $free_tmp = disk_free_space($tmp_dir);
200
        if ($free_tmp < 2048) {
201
            throw new \Exception('TMP folder only has ' . $free_tmp . ' Bytes space available. Disk full!');
202
        }
203
        $free = disk_free_space(dirname($targetFile));
204
        if ($free < 2048) {
205
            throw new \Exception('Package folder only has ' . $free . ' Bytes space available. Disk full!');
206
        }
207
        try {
208
            $p = new \PharData($this->filepath, \Phar::CURRENT_AS_FILEINFO | \Phar::KEY_AS_FILENAME);
209
        } catch (\UnexpectedValueException $e) {
210
            rename($this->filepath, $this->filepath . '.invalid');
211
            throw new \Exception('Package ' . $this->filepath . ' not readable! Will be ignored in the future. Please try again!');
212
        }
213
        $tmpExtractedFilepath = $tmp_dir . DIRECTORY_SEPARATOR . $inPkgName;
214
        if (file_exists($tmpExtractedFilepath)) {
215
            // stale file from before - unlink first
216
            unlink($tmpExtractedFilepath);
217
        }
218
        $p->extractTo($tmp_dir, $inPkgName);
219
        if (file_exists($tmpExtractedFilepath)) {
220
            rename($tmpExtractedFilepath, $targetFile);
221
        }
222
        return true;
223
    }
224
225
    /**
226
     * Returns a true if the package contains WIZARD_UIFILES.
227
     *
228
     * @return bool Package has a wizard
229
     */
230
    public function hasWizardDir()
231
    {
232
        if (file_exists($this->wizfile)) {
233
            return true;
234
        }
235
236
        try {
237
            $p = new \PharData($this->filepath, \Phar::CURRENT_AS_FILEINFO | \Phar::KEY_AS_FILENAME);
238
        } catch (\UnexpectedValueException $e) {
239
            rename($this->filepath, $this->filepath . '.invalid');
240
            throw new \Exception('Package ' . $this->filepath . ' not readable! Will be ignored in the future. Please try again!');
241
        }
242
        foreach($p as $file) {
243
           if (substr($file, strrpos($file, "/")+1) == 'WIZARD_UIFILES') {
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal / does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
244
               touch($this->wizfile);
245
               return true;
246
           }
247
        }
248
        return false;
249
    }
250
251
    /**
252
     * Returns a list of thumbnails for the specified package.
253
     *
254
     * @param string $pathPrefix Prefix to put before file path
255
     * @return array List of thumbnail urls
256
     */
257
    public function getThumbnails($pathPrefix = '')
258
    {
259
        $thumbnailSources = array(
260
            '72' => array(
261
                'file' => 'PACKAGE_ICON.PNG',
262
                'info' => 'package_icon',
263
            ),
264
            '120' => array(
265
                'file' => 'PACKAGE_ICON_256.PNG',
266
                'info' => 'package_icon_256',
267
            ),
268
        );
269
        $thumbnails = array();
270
        foreach ($thumbnailSources as $size => $sourceList) {
271
            $thumbName = $this->filepathNoExt . '_thumb_' . $size . '.png';
272
            // Try to find file in package, otherwise check if defined in INFO
273
            try {
274
                $this->extractIfMissing($sourceList['file'], $thumbName);
275
            } catch (\Exception $e) {
276
                // Check if icon is in metadata
277
                $this->collectMetadata();
278
                if (isset($this->metadata[$sourceList['info']])) {
279
                    file_put_contents($thumbName, base64_decode($this->metadata[$sourceList['info']]));
280
                }
281
            }
282
283
            // Use $size px thumbnail, if available
284
            if (file_exists($thumbName)) {
285
                $thumbnails[] = $pathPrefix . $thumbName;
286
            } else {
287
                // Use theme's default pictures
288
                $themeUrl = $this->config->paths['themes'] . $this->config->site['theme'] . '/';
289
                $thumbnails[] = $pathPrefix . $themeUrl . 'images/default_package_icon_' . $size . '.png';
290
            }
291
        }
292
        return $thumbnails;
293
    }
294
295
    /**
296
     * Returns a list of screenshots for the specified package.
297
     *
298
     * @param string $pathPrefix Prefix to put before file path
299
     * @return array List of screenshots
300
     */
301
    public function getSnapshots($pathPrefix = '')
302
    {
303
        /* Let's first try to extract screenshots from package (SSpkS feature) */
304
        $i = 1;
305
        while (true) {
306
            try {
307
                $this->extractIfMissing('screen_' . $i . '.png', $this->filepathNoExt . '_screen_' . $i . '.png');
308
                $i++;
309
            } catch (\Exception $e) {
310
                break;
311
            }
312
        }
313
        $snapshots = array();
314
        // Add screenshots, if available
315
        foreach (glob($this->filepathNoExt . '*_screen_*.png') as $snapshot) {
316
            $snapshots[] = $pathPrefix . $snapshot;
317
        }
318
        return $snapshots;
319
    }
320
321
    /**
322
     * Checks compatibility to the given $arch-itecture.
323
     *
324
     * @param string $arch Architecture to check against (or "noarch")
325
     * @return bool TRUE if compatible, otherwise FALSE.
326
     */
327
    public function isCompatibleToArch($arch)
328
    {
329
        // Make sure we have metadata available
330
        $this->collectMetadata();
331
        // TODO: Check arch family, too?
332
        return (in_array($arch, $this->metadata['arch']) || in_array('noarch', $this->metadata['arch']));
333
    }
334
335
    /**
336
     * Checks compatibility to the given firmware $version.
337
     *
338
     * @param string $version Target firmware version.
339
     * @return bool TRUE if compatible, otherwise FALSE.
340
     */
341
    public function isCompatibleToFirmware($version)
342
    {
343
        $this->collectMetadata();
344
        return version_compare($this->metadata['firmware'], $version, '<=');
345
    }
346
347
    /**
348
     * Checks if this package is a beta version or not.
349
     *
350
     * @return bool TRUE if this is a beta version, FALSE otherwise.
351
     */
352
    public function isBeta()
353
    {
354
        $this->collectMetadata();
355
        return (isset($this->metadata['beta']) && $this->metadata['beta'] == true);
356
    }
357
}
358