Completed
Push — master ( 0327ef...0fa796 )
by Morris
52:54 queued 37:36
created

Streamer::__construct()   B

Complexity

Conditions 4
Paths 3

Size

Total Lines 32
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 7
nc 3
nop 3
dl 0
loc 32
rs 8.5806
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Joas Schilling <[email protected]>
6
 * @author Thomas Müller <[email protected]>
7
 * @author Victor Dubiniuk <[email protected]>
8
 *
9
 * @license AGPL-3.0
10
 *
11
 * This code is free software: you can redistribute it and/or modify
12
 * it under the terms of the GNU Affero General Public License, version 3,
13
 * as published by the Free Software Foundation.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
 * GNU Affero General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU Affero General Public License, version 3,
21
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
22
 *
23
 */
24
25
namespace OC;
26
27
use OCP\IRequest;
28
use ownCloud\TarStreamer\TarStreamer;
29
use ZipStreamer\ZipStreamer;
30
31
class Streamer {
32
	// array of regexp. Matching user agents will get tar instead of zip
33
	private $preferTarFor = [ '/macintosh|mac os x/i' ];
34
35
	// streamer instance
36
	private $streamerInstance;
37
38
	/**
39
	 * Streamer constructor.
40
	 *
41
	 * @param IRequest $request
42
	 * @param int $size The size of the files in bytes
43
	 * @param int $numberOfFiles The number of files (and directories) that will
44
	 *        be included in the streamed file
45
	 */
46
	public function __construct(IRequest $request, int $size, int $numberOfFiles){
47
48
		/**
49
		 * zip32 constraints for a basic (without compression, volumes nor
50
		 * encryption) zip file according to the Zip specification:
51
		 * - No file size is larger than 4 bytes (file size < 4294967296); see
52
		 *   4.4.9 uncompressed size
53
		 * - The size of all files plus their local headers is not larger than
54
		 *   4 bytes; see 4.4.16 relative offset of local header and 4.4.24
55
		 *   offset of start of central directory with respect to the starting
56
		 *   disk number
57
		 * - The total number of entries (files and directories) in the zip file
58
		 *   is not larger than 2 bytes (number of entries < 65536); see 4.4.22
59
		 *   total number of entries in the central dir
60
		 * - The size of the central directory is not larger than 4 bytes; see
61
		 *   4.4.23 size of the central directory
62
		 *
63
		 * Due to all that, zip32 is used if the size is below 4GB and there are
64
		 * less than 65536 files; the margin between 4*1000^3 and 4*1024^3
65
		 * should give enough room for the extra zip metadata. Technically, it
66
		 * would still be possible to create an invalid zip32 file (for example,
67
		 * a zip file from files smaller than 4GB with a central directory
68
		 * larger than 4GiB), but it should not happen in the real world.
69
		 */
70
		if ($size < 4 * 1000 * 1000 * 1000 && $numberOfFiles < 65536) {
71
			$this->streamerInstance = new ZipStreamer(['zip64' => false]);
72
		} else if ($request->isUserAgent($this->preferTarFor)) {
73
			$this->streamerInstance = new TarStreamer();
74
		} else {
75
			$this->streamerInstance = new ZipStreamer(['zip64' => PHP_INT_SIZE !== 4]);
76
		}
77
	}
78
	
79
	/**
80
	 * Send HTTP headers
81
	 * @param string $name 
82
	 */
83
	public function sendHeaders($name){
84
		$extension = $this->streamerInstance instanceof ZipStreamer ? '.zip' : '.tar';
0 ignored issues
show
Bug introduced by
The class ZipStreamer\ZipStreamer does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
85
		$fullName = $name . $extension;
86
		$this->streamerInstance->sendHeaders($fullName);
87
	}
88
	
89
	/**
90
	 * Stream directory recursively
91
	 * @param string $dir
92
	 * @param string $internalDir
93
	 */
94
	public function addDirRecursive($dir, $internalDir='') {
95
		$dirname = basename($dir);
96
		$rootDir = $internalDir . $dirname;
97
		if (!empty($rootDir)) {
98
			$this->streamerInstance->addEmptyDir($rootDir);
99
		}
100
		$internalDir .= $dirname . '/';
101
		// prevent absolute dirs
102
		$internalDir = ltrim($internalDir, '/');
103
104
		$files= \OC\Files\Filesystem::getDirectoryContent($dir);
105
		foreach($files as $file) {
106
			$filename = $file['name'];
107
			$file = $dir . '/' . $filename;
108 View Code Duplication
			if(\OC\Files\Filesystem::is_file($file)) {
109
				$filesize = \OC\Files\Filesystem::filesize($file);
110
				$fileTime = \OC\Files\Filesystem::filemtime($file);
111
				$fh = \OC\Files\Filesystem::fopen($file, 'r');
112
				$this->addFileFromStream($fh, $internalDir . $filename, $filesize, $fileTime);
0 ignored issues
show
Documentation introduced by
$fh is of type resource, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
113
				fclose($fh);
114
			}elseif(\OC\Files\Filesystem::is_dir($file)) {
115
				$this->addDirRecursive($file, $internalDir);
116
			}
117
		}
118
	}
119
	
120
	/**
121
	 * Add a file to the archive at the specified location and file name.
122
	 *
123
	 * @param string $stream Stream to read data from
124
	 * @param string $internalName Filepath and name to be used in the archive.
125
	 * @param int $size Filesize
126
	 * @param int|bool $time File mtime as int, or false
127
	 * @return bool $success
128
	 */
129
	public function addFileFromStream($stream, $internalName, $size, $time) {
130
		$options = [];
131
		if ($time) {
132
			$options = [
133
				'timestamp' => $time
134
			];
135
		}
136
137
		if ($this->streamerInstance instanceof ZipStreamer) {
0 ignored issues
show
Bug introduced by
The class ZipStreamer\ZipStreamer does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
138
			return $this->streamerInstance->addFileFromStream($stream, $internalName, $options);
139
		} else {
140
			return $this->streamerInstance->addFileFromStream($stream, $internalName, $size, $options);
141
		}
142
	}
143
144
	/**
145
	 * Add an empty directory entry to the archive.
146
	 *
147
	 * @param string $dirName Directory Path and name to be added to the archive.
148
	 * @return bool $success
149
	 */
150
	public function addEmptyDir($dirName){
151
		return $this->streamerInstance->addEmptyDir($dirName);
152
	}
153
154
	/**
155
	 * Close the archive.
156
	 * A closed archive can no longer have new files added to it. After
157
	 * closing, the file is completely written to the output stream.
158
	 * @return bool $success
159
	 */
160
	public function finalize(){
161
		return $this->streamerInstance->finalize();
162
	}
163
}
164