Completed
Branch dev (4bcb34)
by Darko
13:52
created

NZBExport::__construct()   B

Complexity

Conditions 6
Paths 24

Size

Total Lines 16
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 12
nc 24
nop 1
dl 0
loc 16
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
namespace Blacklight;
4
5
use Blacklight\db\DB;
6
use Blacklight\utility\Utility;
7
8
/**
9
 * Export NZB's to a folder.
10
 * Class NZBExport.
11
 */
12
class NZBExport
13
{
14
    /**
15
     * Started from browser?
16
     * @var bool
17
     */
18
    protected $browser;
19
20
    /**
21
     * @var string Return value on browser.
22
     */
23
    protected $retVal;
24
25
    /**
26
     * @var \Blacklight\db\DB
27
     */
28
    protected $pdo;
29
30
    /**
31
     * @var NZB
32
     */
33
    protected $nzb;
34
35
    /**
36
     * @var Releases
37
     */
38
    protected $releases;
39
40
    /**
41
     * @var bool
42
     */
43
    protected $echoCLI;
44
45
    /**
46
     * @param array $options Class instances / various options.
47
     * @throws \Exception
48
     */
49
    public function __construct(array $options = [])
50
    {
51
        $defaults = [
52
			'Browser'  => false, // Started from browser?
53
			'Echo'     => true, // Echo to CLI?
54
			'NZB'      => null,
55
			'Releases' => null,
56
			'Settings' => null,
57
		];
58
        $options += $defaults;
59
60
        $this->browser = $options['Browser'];
61
        $this->echoCLI = (! $this->browser && config('nntmux.echocli') && $options['Echo']);
62
        $this->pdo = ($options['Settings'] instanceof DB ? $options['Setting'] : new DB());
63
        $this->releases = ($options['Releases'] instanceof Releases ? $options['Releases'] : new Releases(['Settings' => $this->pdo]));
64
        $this->nzb = ($options['NZB'] instanceof NZB ? $options['NZB'] : new NZB());
65
    }
66
67
    /**
68
     * Export to user specified folder.
69
     *
70
     * @param array $params
71
     *
72
     * @return bool
73
     */
74
    public function beginExport($params)
75
    {
76
        $gzip = false;
77
        if ($params[4] === true) {
78
            $gzip = true;
79
        }
80
81
        $fromDate = $toDate = '';
82
        $path = $params[0];
83
84
        // Check if the path ends with dir separator.
85
        if (substr($path, -1) !== DS) {
0 ignored issues
show
Bug introduced by
The constant Blacklight\DS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
86
            $path .= DS;
87
        }
88
89
        // Check if it's a directory.
90
        if (! is_dir($path)) {
91
            $this->echoOut('Folder does not exist: '.$path);
92
93
            return $this->returnValue();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->returnValue() also could return the type string which is incompatible with the documented return type boolean.
Loading history...
94
        }
95
96
        // Check if we can write to it.
97
        if (! is_writable($path)) {
98
            $this->echoOut('Folder is not writable: '.$path);
99
100
            return $this->returnValue();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->returnValue() also could return the type string which is incompatible with the documented return type boolean.
Loading history...
101
        }
102
103
        // Check if the from date is the proper format.
104
        if (isset($params[1]) && $params[1] !== '') {
105
            if (! $this->checkDate($params[1])) {
106
                return $this->returnValue();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->returnValue() also could return the type string which is incompatible with the documented return type boolean.
Loading history...
107
            }
108
            $fromDate = $params[1];
109
        }
110
111
        // Check if the to date is the proper format.
112
        if (isset($params[2]) && $params[2] !== '') {
113
            if (! $this->checkDate($params[2])) {
114
                return $this->returnValue();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->returnValue() also could return the type string which is incompatible with the documented return type boolean.
Loading history...
115
            }
116
            $toDate = $params[2];
117
        }
118
119
        // Check if the group_id exists.
120
        if (isset($params[3]) && $params[3] !== 0) {
121
            if (! is_numeric($params[3])) {
122
                $this->echoOut('The group ID is not a number: '.$params[3]);
123
124
                return $this->returnValue();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->returnValue() also could return the type string which is incompatible with the documented return type boolean.
Loading history...
125
            }
126
            $groups = $this->pdo->query('SELECT id, name FROM groups WHERE id = '.$params[3]);
127
            if (count($groups) === 0) {
128
                $this->echoOut('The group ID is not in the DB: '.$params[3]);
129
130
                return $this->returnValue();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->returnValue() also could return the type string which is incompatible with the documented return type boolean.
Loading history...
131
            }
132
        } else {
133
            $groups = $this->pdo->query('SELECT id, name FROM groups');
134
        }
135
136
        $exported = 0;
137
        // Loop over groups to take less RAM.
138
        foreach ($groups as $group) {
139
            $currentExport = 0;
140
            // Get all the releases based on the parameters.
141
            $releases = $this->releases->getForExport($fromDate, $toDate, $group['id']);
142
            $totalFound = count($releases);
143
            if ($totalFound === 0) {
144
                if ($this->echoCLI) {
145
                    echo 'No releases found to export for group: '.$group['name'].PHP_EOL;
146
                }
147
                continue;
148
            }
149
            if ($this->echoCLI) {
150
                echo 'Found '.$totalFound.' releases to export for group: '.$group['name'].PHP_EOL;
151
            }
152
153
            // Create a path to store the new NZB files.
154
            $currentPath = $path.$this->safeFilename($group['name']).DS;
155
            if (! is_dir($currentPath)) {
156
                mkdir($currentPath);
157
            }
158
            foreach ($releases as $release) {
159
160
				// Get path to the NZB file.
161
                $nzbFile = $this->nzb->NZBPath($release['guid']);
162
                // Check if it exists.
163
                if ($nzbFile === false) {
164
                    if ($this->echoCLI) {
165
                        echo 'Unable to find NZB for release with GUID: '.$release['guid'];
166
                    }
167
                    continue;
168
                }
169
170
                // Create path to current file.
171
                $currentFile = $currentPath.$this->safeFilename($release['searchname']);
172
173
                // Check if the user wants them in gzip, copy it if so.
174
                if ($gzip) {
175
                    if (! copy($nzbFile, $currentFile.'.nzb.gz')) {
176
                        if ($this->echoCLI) {
177
                            echo 'Unable to export NZB with GUID: '.$release['guid'];
178
                        }
179
                        continue;
180
                    }
181
                    // If not, decompress it and create a file to store it in.
182
                } else {
183
                    $nzbContents = Utility::unzipGzipFile($nzbFile);
184
                    if (! $nzbContents) {
185
                        if ($this->echoCLI) {
186
                            echo 'Unable to export NZB with GUID: '.$release['guid'];
187
                        }
188
                        continue;
189
                    }
190
                    $fh = fopen($currentFile.'.nzb', 'w');
191
                    fwrite($fh, $nzbContents);
0 ignored issues
show
Bug introduced by
It seems like $fh can also be of type false; however, parameter $handle of fwrite() does only seem to accept resource, 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

191
                    fwrite(/** @scrutinizer ignore-type */ $fh, $nzbContents);
Loading history...
192
                    fclose($fh);
0 ignored issues
show
Bug introduced by
It seems like $fh can also be of type false; however, parameter $handle of fclose() does only seem to accept resource, 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

192
                    fclose(/** @scrutinizer ignore-type */ $fh);
Loading history...
193
                }
194
195
                $currentExport++;
196
197
                if ($this->echoCLI && $currentExport % 10 === 0) {
198
                    echo 'Exported '.$currentExport.' of '.$totalFound.' nzbs for group: '.$group['name']."\r";
199
                }
200
            }
201
            if ($this->echoCLI && $currentExport > 0) {
202
                echo 'Exported '.$currentExport.' of '.$totalFound.' nzbs for group: '.$group['name'].PHP_EOL;
203
            }
204
            $exported += $currentExport;
205
        }
206
        if ($exported > 0) {
207
            $this->echoOut('Exported total of '.$exported.' NZB files to '.$path);
208
        }
209
210
        return $this->returnValue();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->returnValue() also could return the type string which is incompatible with the documented return type boolean.
Loading history...
211
    }
212
213
    /**
214
     * Return bool on CLI, string on browser.
215
     * @return bool|string
216
     */
217
    protected function returnValue()
218
    {
219
        return $this->browser ? $this->retVal : true;
220
    }
221
222
    /**
223
     * Check if date is in good format.
224
     *
225
     * @param string $date
226
     *
227
     * @return bool
228
     */
229
    protected function checkDate($date)
230
    {
231
        if (! preg_match('/^(\d{2}\/){2}\d{4}$/', $date)) {
232
            $this->echoOut('Wrong date format: '.$date);
233
234
            return false;
235
        }
236
237
        return true;
238
    }
239
240
    /**
241
     * Echo message to browser or CLI.
242
     *
243
     * @param string $message
244
     */
245
    protected function echoOut($message)
246
    {
247
        if ($this->browser) {
248
            $this->retVal .= $message.'<br />';
249
        } elseif ($this->echoCLI) {
250
            echo $message.PHP_EOL;
251
        }
252
    }
253
254
    /**
255
     * Remove unsafe chars from a filename.
256
     *
257
     * @param string $filename
258
     *
259
     * @return string
260
     */
261
    protected function safeFilename($filename)
262
    {
263
        return trim(preg_replace('/[^\w\s.-]*/i', '', $filename));
264
    }
265
}
266