Passed
Push — master ( daee22...bbb168 )
by Morris
13:11 queued 10s
created
apps/files/composer/composer/autoload_static.php 1 patch
Spacing   +35 added lines, -35 removed lines patch added patch discarded remove patch
@@ -6,54 +6,54 @@
 block discarded – undo
6 6
 
7 7
 class ComposerStaticInitFiles
8 8
 {
9
-    public static $prefixLengthsPsr4 = array (
9
+    public static $prefixLengthsPsr4 = array(
10 10
         'O' => 
11
-        array (
11
+        array(
12 12
             'OCA\\Files\\' => 10,
13 13
         ),
14 14
     );
15 15
 
16
-    public static $prefixDirsPsr4 = array (
16
+    public static $prefixDirsPsr4 = array(
17 17
         'OCA\\Files\\' => 
18
-        array (
19
-            0 => __DIR__ . '/..' . '/../lib',
18
+        array(
19
+            0 => __DIR__.'/..'.'/../lib',
20 20
         ),
21 21
     );
22 22
 
23
-    public static $classMap = array (
24
-        'OCA\\Files\\Activity\\FavoriteProvider' => __DIR__ . '/..' . '/../lib/Activity/FavoriteProvider.php',
25
-        'OCA\\Files\\Activity\\Filter\\Favorites' => __DIR__ . '/..' . '/../lib/Activity/Filter/Favorites.php',
26
-        'OCA\\Files\\Activity\\Filter\\FileChanges' => __DIR__ . '/..' . '/../lib/Activity/Filter/FileChanges.php',
27
-        'OCA\\Files\\Activity\\Helper' => __DIR__ . '/..' . '/../lib/Activity/Helper.php',
28
-        'OCA\\Files\\Activity\\Provider' => __DIR__ . '/..' . '/../lib/Activity/Provider.php',
29
-        'OCA\\Files\\Activity\\Settings\\FavoriteAction' => __DIR__ . '/..' . '/../lib/Activity/Settings/FavoriteAction.php',
30
-        'OCA\\Files\\Activity\\Settings\\FileChanged' => __DIR__ . '/..' . '/../lib/Activity/Settings/FileChanged.php',
31
-        'OCA\\Files\\Activity\\Settings\\FileCreated' => __DIR__ . '/..' . '/../lib/Activity/Settings/FileCreated.php',
32
-        'OCA\\Files\\Activity\\Settings\\FileDeleted' => __DIR__ . '/..' . '/../lib/Activity/Settings/FileDeleted.php',
33
-        'OCA\\Files\\Activity\\Settings\\FileFavorite' => __DIR__ . '/..' . '/../lib/Activity/Settings/FileFavorite.php',
34
-        'OCA\\Files\\Activity\\Settings\\FileRestored' => __DIR__ . '/..' . '/../lib/Activity/Settings/FileRestored.php',
35
-        'OCA\\Files\\App' => __DIR__ . '/..' . '/../lib/App.php',
36
-        'OCA\\Files\\AppInfo\\Application' => __DIR__ . '/..' . '/../lib/AppInfo/Application.php',
37
-        'OCA\\Files\\BackgroundJob\\CleanupFileLocks' => __DIR__ . '/..' . '/../lib/BackgroundJob/CleanupFileLocks.php',
38
-        'OCA\\Files\\BackgroundJob\\DeleteOrphanedItems' => __DIR__ . '/..' . '/../lib/BackgroundJob/DeleteOrphanedItems.php',
39
-        'OCA\\Files\\BackgroundJob\\ScanFiles' => __DIR__ . '/..' . '/../lib/BackgroundJob/ScanFiles.php',
40
-        'OCA\\Files\\Capabilities' => __DIR__ . '/..' . '/../lib/Capabilities.php',
41
-        'OCA\\Files\\Collaboration\\Resources\\Listener' => __DIR__ . '/..' . '/../lib/Collaboration/Resources/Listener.php',
42
-        'OCA\\Files\\Collaboration\\Resources\\ResourceProvider' => __DIR__ . '/..' . '/../lib/Collaboration/Resources/ResourceProvider.php',
43
-        'OCA\\Files\\Command\\DeleteOrphanedFiles' => __DIR__ . '/..' . '/../lib/Command/DeleteOrphanedFiles.php',
44
-        'OCA\\Files\\Command\\Scan' => __DIR__ . '/..' . '/../lib/Command/Scan.php',
45
-        'OCA\\Files\\Command\\ScanAppData' => __DIR__ . '/..' . '/../lib/Command/ScanAppData.php',
46
-        'OCA\\Files\\Command\\TransferOwnership' => __DIR__ . '/..' . '/../lib/Command/TransferOwnership.php',
47
-        'OCA\\Files\\Controller\\AjaxController' => __DIR__ . '/..' . '/../lib/Controller/AjaxController.php',
48
-        'OCA\\Files\\Controller\\ApiController' => __DIR__ . '/..' . '/../lib/Controller/ApiController.php',
49
-        'OCA\\Files\\Controller\\ViewController' => __DIR__ . '/..' . '/../lib/Controller/ViewController.php',
50
-        'OCA\\Files\\Helper' => __DIR__ . '/..' . '/../lib/Helper.php',
51
-        'OCA\\Files\\Service\\TagService' => __DIR__ . '/..' . '/../lib/Service/TagService.php',
23
+    public static $classMap = array(
24
+        'OCA\\Files\\Activity\\FavoriteProvider' => __DIR__.'/..'.'/../lib/Activity/FavoriteProvider.php',
25
+        'OCA\\Files\\Activity\\Filter\\Favorites' => __DIR__.'/..'.'/../lib/Activity/Filter/Favorites.php',
26
+        'OCA\\Files\\Activity\\Filter\\FileChanges' => __DIR__.'/..'.'/../lib/Activity/Filter/FileChanges.php',
27
+        'OCA\\Files\\Activity\\Helper' => __DIR__.'/..'.'/../lib/Activity/Helper.php',
28
+        'OCA\\Files\\Activity\\Provider' => __DIR__.'/..'.'/../lib/Activity/Provider.php',
29
+        'OCA\\Files\\Activity\\Settings\\FavoriteAction' => __DIR__.'/..'.'/../lib/Activity/Settings/FavoriteAction.php',
30
+        'OCA\\Files\\Activity\\Settings\\FileChanged' => __DIR__.'/..'.'/../lib/Activity/Settings/FileChanged.php',
31
+        'OCA\\Files\\Activity\\Settings\\FileCreated' => __DIR__.'/..'.'/../lib/Activity/Settings/FileCreated.php',
32
+        'OCA\\Files\\Activity\\Settings\\FileDeleted' => __DIR__.'/..'.'/../lib/Activity/Settings/FileDeleted.php',
33
+        'OCA\\Files\\Activity\\Settings\\FileFavorite' => __DIR__.'/..'.'/../lib/Activity/Settings/FileFavorite.php',
34
+        'OCA\\Files\\Activity\\Settings\\FileRestored' => __DIR__.'/..'.'/../lib/Activity/Settings/FileRestored.php',
35
+        'OCA\\Files\\App' => __DIR__.'/..'.'/../lib/App.php',
36
+        'OCA\\Files\\AppInfo\\Application' => __DIR__.'/..'.'/../lib/AppInfo/Application.php',
37
+        'OCA\\Files\\BackgroundJob\\CleanupFileLocks' => __DIR__.'/..'.'/../lib/BackgroundJob/CleanupFileLocks.php',
38
+        'OCA\\Files\\BackgroundJob\\DeleteOrphanedItems' => __DIR__.'/..'.'/../lib/BackgroundJob/DeleteOrphanedItems.php',
39
+        'OCA\\Files\\BackgroundJob\\ScanFiles' => __DIR__.'/..'.'/../lib/BackgroundJob/ScanFiles.php',
40
+        'OCA\\Files\\Capabilities' => __DIR__.'/..'.'/../lib/Capabilities.php',
41
+        'OCA\\Files\\Collaboration\\Resources\\Listener' => __DIR__.'/..'.'/../lib/Collaboration/Resources/Listener.php',
42
+        'OCA\\Files\\Collaboration\\Resources\\ResourceProvider' => __DIR__.'/..'.'/../lib/Collaboration/Resources/ResourceProvider.php',
43
+        'OCA\\Files\\Command\\DeleteOrphanedFiles' => __DIR__.'/..'.'/../lib/Command/DeleteOrphanedFiles.php',
44
+        'OCA\\Files\\Command\\Scan' => __DIR__.'/..'.'/../lib/Command/Scan.php',
45
+        'OCA\\Files\\Command\\ScanAppData' => __DIR__.'/..'.'/../lib/Command/ScanAppData.php',
46
+        'OCA\\Files\\Command\\TransferOwnership' => __DIR__.'/..'.'/../lib/Command/TransferOwnership.php',
47
+        'OCA\\Files\\Controller\\AjaxController' => __DIR__.'/..'.'/../lib/Controller/AjaxController.php',
48
+        'OCA\\Files\\Controller\\ApiController' => __DIR__.'/..'.'/../lib/Controller/ApiController.php',
49
+        'OCA\\Files\\Controller\\ViewController' => __DIR__.'/..'.'/../lib/Controller/ViewController.php',
50
+        'OCA\\Files\\Helper' => __DIR__.'/..'.'/../lib/Helper.php',
51
+        'OCA\\Files\\Service\\TagService' => __DIR__.'/..'.'/../lib/Service/TagService.php',
52 52
     );
53 53
 
54 54
     public static function getInitializer(ClassLoader $loader)
55 55
     {
56
-        return \Closure::bind(function () use ($loader) {
56
+        return \Closure::bind(function() use ($loader) {
57 57
             $loader->prefixLengthsPsr4 = ComposerStaticInitFiles::$prefixLengthsPsr4;
58 58
             $loader->prefixDirsPsr4 = ComposerStaticInitFiles::$prefixDirsPsr4;
59 59
             $loader->classMap = ComposerStaticInitFiles::$classMap;
Please login to merge, or discard this patch.
apps/files/composer/composer/autoload_classmap.php 1 patch
Spacing   +28 added lines, -28 removed lines patch added patch discarded remove patch
@@ -6,32 +6,32 @@
 block discarded – undo
6 6
 $baseDir = $vendorDir;
7 7
 
8 8
 return array(
9
-    'OCA\\Files\\Activity\\FavoriteProvider' => $baseDir . '/../lib/Activity/FavoriteProvider.php',
10
-    'OCA\\Files\\Activity\\Filter\\Favorites' => $baseDir . '/../lib/Activity/Filter/Favorites.php',
11
-    'OCA\\Files\\Activity\\Filter\\FileChanges' => $baseDir . '/../lib/Activity/Filter/FileChanges.php',
12
-    'OCA\\Files\\Activity\\Helper' => $baseDir . '/../lib/Activity/Helper.php',
13
-    'OCA\\Files\\Activity\\Provider' => $baseDir . '/../lib/Activity/Provider.php',
14
-    'OCA\\Files\\Activity\\Settings\\FavoriteAction' => $baseDir . '/../lib/Activity/Settings/FavoriteAction.php',
15
-    'OCA\\Files\\Activity\\Settings\\FileChanged' => $baseDir . '/../lib/Activity/Settings/FileChanged.php',
16
-    'OCA\\Files\\Activity\\Settings\\FileCreated' => $baseDir . '/../lib/Activity/Settings/FileCreated.php',
17
-    'OCA\\Files\\Activity\\Settings\\FileDeleted' => $baseDir . '/../lib/Activity/Settings/FileDeleted.php',
18
-    'OCA\\Files\\Activity\\Settings\\FileFavorite' => $baseDir . '/../lib/Activity/Settings/FileFavorite.php',
19
-    'OCA\\Files\\Activity\\Settings\\FileRestored' => $baseDir . '/../lib/Activity/Settings/FileRestored.php',
20
-    'OCA\\Files\\App' => $baseDir . '/../lib/App.php',
21
-    'OCA\\Files\\AppInfo\\Application' => $baseDir . '/../lib/AppInfo/Application.php',
22
-    'OCA\\Files\\BackgroundJob\\CleanupFileLocks' => $baseDir . '/../lib/BackgroundJob/CleanupFileLocks.php',
23
-    'OCA\\Files\\BackgroundJob\\DeleteOrphanedItems' => $baseDir . '/../lib/BackgroundJob/DeleteOrphanedItems.php',
24
-    'OCA\\Files\\BackgroundJob\\ScanFiles' => $baseDir . '/../lib/BackgroundJob/ScanFiles.php',
25
-    'OCA\\Files\\Capabilities' => $baseDir . '/../lib/Capabilities.php',
26
-    'OCA\\Files\\Collaboration\\Resources\\Listener' => $baseDir . '/../lib/Collaboration/Resources/Listener.php',
27
-    'OCA\\Files\\Collaboration\\Resources\\ResourceProvider' => $baseDir . '/../lib/Collaboration/Resources/ResourceProvider.php',
28
-    'OCA\\Files\\Command\\DeleteOrphanedFiles' => $baseDir . '/../lib/Command/DeleteOrphanedFiles.php',
29
-    'OCA\\Files\\Command\\Scan' => $baseDir . '/../lib/Command/Scan.php',
30
-    'OCA\\Files\\Command\\ScanAppData' => $baseDir . '/../lib/Command/ScanAppData.php',
31
-    'OCA\\Files\\Command\\TransferOwnership' => $baseDir . '/../lib/Command/TransferOwnership.php',
32
-    'OCA\\Files\\Controller\\AjaxController' => $baseDir . '/../lib/Controller/AjaxController.php',
33
-    'OCA\\Files\\Controller\\ApiController' => $baseDir . '/../lib/Controller/ApiController.php',
34
-    'OCA\\Files\\Controller\\ViewController' => $baseDir . '/../lib/Controller/ViewController.php',
35
-    'OCA\\Files\\Helper' => $baseDir . '/../lib/Helper.php',
36
-    'OCA\\Files\\Service\\TagService' => $baseDir . '/../lib/Service/TagService.php',
9
+    'OCA\\Files\\Activity\\FavoriteProvider' => $baseDir.'/../lib/Activity/FavoriteProvider.php',
10
+    'OCA\\Files\\Activity\\Filter\\Favorites' => $baseDir.'/../lib/Activity/Filter/Favorites.php',
11
+    'OCA\\Files\\Activity\\Filter\\FileChanges' => $baseDir.'/../lib/Activity/Filter/FileChanges.php',
12
+    'OCA\\Files\\Activity\\Helper' => $baseDir.'/../lib/Activity/Helper.php',
13
+    'OCA\\Files\\Activity\\Provider' => $baseDir.'/../lib/Activity/Provider.php',
14
+    'OCA\\Files\\Activity\\Settings\\FavoriteAction' => $baseDir.'/../lib/Activity/Settings/FavoriteAction.php',
15
+    'OCA\\Files\\Activity\\Settings\\FileChanged' => $baseDir.'/../lib/Activity/Settings/FileChanged.php',
16
+    'OCA\\Files\\Activity\\Settings\\FileCreated' => $baseDir.'/../lib/Activity/Settings/FileCreated.php',
17
+    'OCA\\Files\\Activity\\Settings\\FileDeleted' => $baseDir.'/../lib/Activity/Settings/FileDeleted.php',
18
+    'OCA\\Files\\Activity\\Settings\\FileFavorite' => $baseDir.'/../lib/Activity/Settings/FileFavorite.php',
19
+    'OCA\\Files\\Activity\\Settings\\FileRestored' => $baseDir.'/../lib/Activity/Settings/FileRestored.php',
20
+    'OCA\\Files\\App' => $baseDir.'/../lib/App.php',
21
+    'OCA\\Files\\AppInfo\\Application' => $baseDir.'/../lib/AppInfo/Application.php',
22
+    'OCA\\Files\\BackgroundJob\\CleanupFileLocks' => $baseDir.'/../lib/BackgroundJob/CleanupFileLocks.php',
23
+    'OCA\\Files\\BackgroundJob\\DeleteOrphanedItems' => $baseDir.'/../lib/BackgroundJob/DeleteOrphanedItems.php',
24
+    'OCA\\Files\\BackgroundJob\\ScanFiles' => $baseDir.'/../lib/BackgroundJob/ScanFiles.php',
25
+    'OCA\\Files\\Capabilities' => $baseDir.'/../lib/Capabilities.php',
26
+    'OCA\\Files\\Collaboration\\Resources\\Listener' => $baseDir.'/../lib/Collaboration/Resources/Listener.php',
27
+    'OCA\\Files\\Collaboration\\Resources\\ResourceProvider' => $baseDir.'/../lib/Collaboration/Resources/ResourceProvider.php',
28
+    'OCA\\Files\\Command\\DeleteOrphanedFiles' => $baseDir.'/../lib/Command/DeleteOrphanedFiles.php',
29
+    'OCA\\Files\\Command\\Scan' => $baseDir.'/../lib/Command/Scan.php',
30
+    'OCA\\Files\\Command\\ScanAppData' => $baseDir.'/../lib/Command/ScanAppData.php',
31
+    'OCA\\Files\\Command\\TransferOwnership' => $baseDir.'/../lib/Command/TransferOwnership.php',
32
+    'OCA\\Files\\Controller\\AjaxController' => $baseDir.'/../lib/Controller/AjaxController.php',
33
+    'OCA\\Files\\Controller\\ApiController' => $baseDir.'/../lib/Controller/ApiController.php',
34
+    'OCA\\Files\\Controller\\ViewController' => $baseDir.'/../lib/Controller/ViewController.php',
35
+    'OCA\\Files\\Helper' => $baseDir.'/../lib/Helper.php',
36
+    'OCA\\Files\\Service\\TagService' => $baseDir.'/../lib/Service/TagService.php',
37 37
 );
Please login to merge, or discard this patch.
apps/files/appinfo/routes.php 1 patch
Indentation   +64 added lines, -64 removed lines patch added patch discarded remove patch
@@ -31,73 +31,73 @@
 block discarded – undo
31 31
 
32 32
 $application = new Application();
33 33
 $application->registerRoutes(
34
-	$this,
35
-	[
36
-		'routes' => [
37
-			[
38
-				'name' => 'API#getThumbnail',
39
-				'url' => '/api/v1/thumbnail/{x}/{y}/{file}',
40
-				'verb' => 'GET',
41
-				'requirements' => ['file' => '.+']
42
-			],
43
-			[
44
-				'name' => 'API#updateFileTags',
45
-				'url' => '/api/v1/files/{path}',
46
-				'verb' => 'POST',
47
-				'requirements' => ['path' => '.+'],
48
-			],
49
-			[
50
-				'name' => 'API#getRecentFiles',
51
-				'url' => '/api/v1/recent/',
52
-				'verb' => 'GET'
53
-			],
54
-			[
55
-				'name' => 'API#updateFileSorting',
56
-				'url' => '/api/v1/sorting',
57
-				'verb' => 'POST'
58
-			],
59
-			[
60
-				'name' => 'API#showHiddenFiles',
61
-				'url' => '/api/v1/showhidden',
62
-				'verb' => 'POST'
63
-			],
64
-			[
65
-				'name' => 'API#showGridView',
66
-				'url' => '/api/v1/showgridview',
67
-				'verb' => 'POST'
68
-			],
69
-			[
70
-				'name' => 'API#getGridView',
71
-				'url' => '/api/v1/showgridview',
72
-				'verb' => 'GET'
73
-			],
74
-			[
75
-				'name' => 'view#index',
76
-				'url' => '/',
77
-				'verb' => 'GET',
78
-			],
79
-			[
80
-				'name' => 'ajax#getStorageStats',
81
-				'url' => '/ajax/getstoragestats.php',
82
-				'verb' => 'GET',
83
-			],
84
-			[
85
-				'name' => 'API#toggleShowFolder',
86
-				'url' => '/api/v1/toggleShowFolder/{key}',
87
-				'verb' => 'POST'
88
-			],
89
-			[
90
-				'name' => 'API#getNodeType',
91
-				'url' => '/api/v1/quickaccess/get/NodeType',
92
-				'verb' => 'GET',
93
-			],
94
-		]
95
-	]
34
+    $this,
35
+    [
36
+        'routes' => [
37
+            [
38
+                'name' => 'API#getThumbnail',
39
+                'url' => '/api/v1/thumbnail/{x}/{y}/{file}',
40
+                'verb' => 'GET',
41
+                'requirements' => ['file' => '.+']
42
+            ],
43
+            [
44
+                'name' => 'API#updateFileTags',
45
+                'url' => '/api/v1/files/{path}',
46
+                'verb' => 'POST',
47
+                'requirements' => ['path' => '.+'],
48
+            ],
49
+            [
50
+                'name' => 'API#getRecentFiles',
51
+                'url' => '/api/v1/recent/',
52
+                'verb' => 'GET'
53
+            ],
54
+            [
55
+                'name' => 'API#updateFileSorting',
56
+                'url' => '/api/v1/sorting',
57
+                'verb' => 'POST'
58
+            ],
59
+            [
60
+                'name' => 'API#showHiddenFiles',
61
+                'url' => '/api/v1/showhidden',
62
+                'verb' => 'POST'
63
+            ],
64
+            [
65
+                'name' => 'API#showGridView',
66
+                'url' => '/api/v1/showgridview',
67
+                'verb' => 'POST'
68
+            ],
69
+            [
70
+                'name' => 'API#getGridView',
71
+                'url' => '/api/v1/showgridview',
72
+                'verb' => 'GET'
73
+            ],
74
+            [
75
+                'name' => 'view#index',
76
+                'url' => '/',
77
+                'verb' => 'GET',
78
+            ],
79
+            [
80
+                'name' => 'ajax#getStorageStats',
81
+                'url' => '/ajax/getstoragestats.php',
82
+                'verb' => 'GET',
83
+            ],
84
+            [
85
+                'name' => 'API#toggleShowFolder',
86
+                'url' => '/api/v1/toggleShowFolder/{key}',
87
+                'verb' => 'POST'
88
+            ],
89
+            [
90
+                'name' => 'API#getNodeType',
91
+                'url' => '/api/v1/quickaccess/get/NodeType',
92
+                'verb' => 'GET',
93
+            ],
94
+        ]
95
+    ]
96 96
 );
97 97
 
98 98
 /** @var $this \OC\Route\Router */
99 99
 
100 100
 $this->create('files_ajax_download', 'ajax/download.php')
101
-	->actionInclude('files/ajax/download.php');
101
+    ->actionInclude('files/ajax/download.php');
102 102
 $this->create('files_ajax_list', 'ajax/list.php')
103
-	->actionInclude('files/ajax/list.php');
103
+    ->actionInclude('files/ajax/list.php');
Please login to merge, or discard this patch.
lib/private/legacy/files.php 2 patches
Indentation   +348 added lines, -348 removed lines patch added patch discarded remove patch
@@ -49,356 +49,356 @@
 block discarded – undo
49 49
  *
50 50
  */
51 51
 class OC_Files {
52
-	const FILE = 1;
53
-	const ZIP_FILES = 2;
54
-	const ZIP_DIR = 3;
55
-
56
-	const UPLOAD_MIN_LIMIT_BYTES = 1048576; // 1 MiB
57
-
58
-
59
-	private static $multipartBoundary = '';
60
-
61
-	/**
62
-	 * @return string
63
-	 */
64
-	private static function getBoundary() {
65
-		if (empty(self::$multipartBoundary)) {
66
-			self::$multipartBoundary = md5(mt_rand());
67
-		}
68
-		return self::$multipartBoundary;
69
-	}
70
-
71
-	/**
72
-	 * @param string $filename
73
-	 * @param string $name
74
-	 * @param array $rangeArray ('from'=>int,'to'=>int), ...
75
-	 */
76
-	private static function sendHeaders($filename, $name, array $rangeArray) {
77
-		OC_Response::setContentDispositionHeader($name, 'attachment');
78
-		header('Content-Transfer-Encoding: binary', true);
79
-		header('Pragma: public');// enable caching in IE
80
-		header('Expires: 0');
81
-		header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
82
-		$fileSize = \OC\Files\Filesystem::filesize($filename);
83
-		$type = \OC::$server->getMimeTypeDetector()->getSecureMimeType(\OC\Files\Filesystem::getMimeType($filename));
84
-		if ($fileSize > -1) {
85
-			if (!empty($rangeArray)) {
86
-			    http_response_code(206);
87
-			    header('Accept-Ranges: bytes', true);
88
-			    if (count($rangeArray) > 1) {
89
-				$type = 'multipart/byteranges; boundary='.self::getBoundary();
90
-				// no Content-Length header here
91
-			    }
92
-			    else {
93
-				header(sprintf('Content-Range: bytes %d-%d/%d', $rangeArray[0]['from'], $rangeArray[0]['to'], $fileSize), true);
94
-				OC_Response::setContentLengthHeader($rangeArray[0]['to'] - $rangeArray[0]['from'] + 1);
95
-			    }
96
-			}
97
-			else {
98
-			    OC_Response::setContentLengthHeader($fileSize);
99
-			}
100
-		}
101
-		header('Content-Type: '.$type, true);
102
-	}
103
-
104
-	/**
105
-	 * return the content of a file or return a zip file containing multiple files
106
-	 *
107
-	 * @param string $dir
108
-	 * @param string $files ; separated list of files to download
109
-	 * @param array $params ; 'head' boolean to only send header of the request ; 'range' http range header
110
-	 */
111
-	public static function get($dir, $files, $params = null) {
112
-
113
-		$view = \OC\Files\Filesystem::getView();
114
-		$getType = self::FILE;
115
-		$filename = $dir;
116
-		try {
117
-
118
-			if (is_array($files) && count($files) === 1) {
119
-				$files = $files[0];
120
-			}
121
-
122
-			if (!is_array($files)) {
123
-				$filename = $dir . '/' . $files;
124
-				if (!$view->is_dir($filename)) {
125
-					self::getSingleFile($view, $dir, $files, is_null($params) ? array() : $params);
126
-					return;
127
-				}
128
-			}
129
-
130
-			$name = 'download';
131
-			if (is_array($files)) {
132
-				$getType = self::ZIP_FILES;
133
-				$basename = basename($dir);
134
-				if ($basename) {
135
-					$name = $basename;
136
-				}
137
-
138
-				$filename = $dir . '/' . $name;
139
-			} else {
140
-				$filename = $dir . '/' . $files;
141
-				$getType = self::ZIP_DIR;
142
-				// downloading root ?
143
-				if ($files !== '') {
144
-					$name = $files;
145
-				}
146
-			}
147
-
148
-			self::lockFiles($view, $dir, $files);
149
-
150
-			/* Calculate filesize and number of files */
151
-			if ($getType === self::ZIP_FILES) {
152
-				$fileInfos = array();
153
-				$fileSize = 0;
154
-				foreach ($files as $file) {
155
-					$fileInfo = \OC\Files\Filesystem::getFileInfo($dir . '/' . $file);
156
-					$fileSize += $fileInfo->getSize();
157
-					$fileInfos[] = $fileInfo;
158
-				}
159
-				$numberOfFiles = self::getNumberOfFiles($fileInfos);
160
-			} elseif ($getType === self::ZIP_DIR) {
161
-				$fileInfo = \OC\Files\Filesystem::getFileInfo($dir . '/' . $files);
162
-				$fileSize = $fileInfo->getSize();
163
-				$numberOfFiles = self::getNumberOfFiles(array($fileInfo));
164
-			}
165
-
166
-			$streamer = new Streamer(\OC::$server->getRequest(), $fileSize, $numberOfFiles);
167
-			OC_Util::obEnd();
168
-
169
-			$streamer->sendHeaders($name);
170
-			$executionTime = (int)OC::$server->getIniWrapper()->getNumeric('max_execution_time');
171
-			if (strpos(@ini_get('disable_functions'), 'set_time_limit') === false) {
172
-				@set_time_limit(0);
173
-			}
174
-			ignore_user_abort(true);
175
-
176
-			if ($getType === self::ZIP_FILES) {
177
-				foreach ($files as $file) {
178
-					$file = $dir . '/' . $file;
179
-					if (\OC\Files\Filesystem::is_file($file)) {
180
-						$fileSize = \OC\Files\Filesystem::filesize($file);
181
-						$fileTime = \OC\Files\Filesystem::filemtime($file);
182
-						$fh = \OC\Files\Filesystem::fopen($file, 'r');
183
-						$streamer->addFileFromStream($fh, basename($file), $fileSize, $fileTime);
184
-						fclose($fh);
185
-					} elseif (\OC\Files\Filesystem::is_dir($file)) {
186
-						$streamer->addDirRecursive($file);
187
-					}
188
-				}
189
-			} elseif ($getType === self::ZIP_DIR) {
190
-				$file = $dir . '/' . $files;
191
-				$streamer->addDirRecursive($file);
192
-			}
193
-			$streamer->finalize();
194
-			set_time_limit($executionTime);
195
-			self::unlockAllTheFiles($dir, $files, $getType, $view, $filename);
196
-		} catch (\OCP\Lock\LockedException $ex) {
197
-			self::unlockAllTheFiles($dir, $files, $getType, $view, $filename);
198
-			OC::$server->getLogger()->logException($ex);
199
-			$l = \OC::$server->getL10N('core');
200
-			$hint = method_exists($ex, 'getHint') ? $ex->getHint() : '';
201
-			\OC_Template::printErrorPage($l->t('File is currently busy, please try again later'), $hint, 200);
202
-		} catch (\OCP\Files\ForbiddenException $ex) {
203
-			self::unlockAllTheFiles($dir, $files, $getType, $view, $filename);
204
-			OC::$server->getLogger()->logException($ex);
205
-			$l = \OC::$server->getL10N('core');
206
-			\OC_Template::printErrorPage($l->t('Can\'t read file'), $ex->getMessage(), 200);
207
-		} catch (\Exception $ex) {
208
-			self::unlockAllTheFiles($dir, $files, $getType, $view, $filename);
209
-			OC::$server->getLogger()->logException($ex);
210
-			$l = \OC::$server->getL10N('core');
211
-			$hint = method_exists($ex, 'getHint') ? $ex->getHint() : '';
212
-			\OC_Template::printErrorPage($l->t('Can\'t read file'), $hint, 200);
213
-		}
214
-	}
215
-
216
-	/**
217
-	 * @param string $rangeHeaderPos
218
-	 * @param int $fileSize
219
-	 * @return array $rangeArray ('from'=>int,'to'=>int), ...
220
-	 */
221
-	private static function parseHttpRangeHeader($rangeHeaderPos, $fileSize) {
222
-		$rArray=explode(',', $rangeHeaderPos);
223
-		$minOffset = 0;
224
-		$ind = 0;
225
-
226
-		$rangeArray = array();
227
-
228
-		foreach ($rArray as $value) {
229
-			$ranges = explode('-', $value);
230
-			if (is_numeric($ranges[0])) {
231
-				if ($ranges[0] < $minOffset) { // case: bytes=500-700,601-999
232
-					$ranges[0] = $minOffset;
233
-				}
234
-				if ($ind > 0 && $rangeArray[$ind-1]['to']+1 == $ranges[0]) { // case: bytes=500-600,601-999
235
-					$ind--;
236
-					$ranges[0] = $rangeArray[$ind]['from'];
237
-				}
238
-			}
239
-
240
-			if (is_numeric($ranges[0]) && is_numeric($ranges[1]) && $ranges[0] < $fileSize && $ranges[0] <= $ranges[1]) {
241
-				// case: x-x
242
-				if ($ranges[1] >= $fileSize) {
243
-					$ranges[1] = $fileSize-1;
244
-				}
245
-				$rangeArray[$ind++] = array( 'from' => $ranges[0], 'to' => $ranges[1], 'size' => $fileSize );
246
-				$minOffset = $ranges[1] + 1;
247
-				if ($minOffset >= $fileSize) {
248
-					break;
249
-				}
250
-			}
251
-			elseif (is_numeric($ranges[0]) && $ranges[0] < $fileSize) {
252
-				// case: x-
253
-				$rangeArray[$ind++] = array( 'from' => $ranges[0], 'to' => $fileSize-1, 'size' => $fileSize );
254
-				break;
255
-			}
256
-			elseif (is_numeric($ranges[1])) {
257
-				// case: -x
258
-				if ($ranges[1] > $fileSize) {
259
-					$ranges[1] = $fileSize;
260
-				}
261
-				$rangeArray[$ind++] = array( 'from' => $fileSize-$ranges[1], 'to' => $fileSize-1, 'size' => $fileSize );
262
-				break;
263
-			}
264
-		}
265
-		return $rangeArray;
266
-	}
267
-
268
-	/**
269
-	 * @param View $view
270
-	 * @param string $name
271
-	 * @param string $dir
272
-	 * @param array $params ; 'head' boolean to only send header of the request ; 'range' http range header
273
-	 */
274
-	private static function getSingleFile($view, $dir, $name, $params) {
275
-		$filename = $dir . '/' . $name;
276
-		OC_Util::obEnd();
277
-		$view->lockFile($filename, ILockingProvider::LOCK_SHARED);
52
+    const FILE = 1;
53
+    const ZIP_FILES = 2;
54
+    const ZIP_DIR = 3;
55
+
56
+    const UPLOAD_MIN_LIMIT_BYTES = 1048576; // 1 MiB
57
+
58
+
59
+    private static $multipartBoundary = '';
60
+
61
+    /**
62
+     * @return string
63
+     */
64
+    private static function getBoundary() {
65
+        if (empty(self::$multipartBoundary)) {
66
+            self::$multipartBoundary = md5(mt_rand());
67
+        }
68
+        return self::$multipartBoundary;
69
+    }
70
+
71
+    /**
72
+     * @param string $filename
73
+     * @param string $name
74
+     * @param array $rangeArray ('from'=>int,'to'=>int), ...
75
+     */
76
+    private static function sendHeaders($filename, $name, array $rangeArray) {
77
+        OC_Response::setContentDispositionHeader($name, 'attachment');
78
+        header('Content-Transfer-Encoding: binary', true);
79
+        header('Pragma: public');// enable caching in IE
80
+        header('Expires: 0');
81
+        header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
82
+        $fileSize = \OC\Files\Filesystem::filesize($filename);
83
+        $type = \OC::$server->getMimeTypeDetector()->getSecureMimeType(\OC\Files\Filesystem::getMimeType($filename));
84
+        if ($fileSize > -1) {
85
+            if (!empty($rangeArray)) {
86
+                http_response_code(206);
87
+                header('Accept-Ranges: bytes', true);
88
+                if (count($rangeArray) > 1) {
89
+                $type = 'multipart/byteranges; boundary='.self::getBoundary();
90
+                // no Content-Length header here
91
+                }
92
+                else {
93
+                header(sprintf('Content-Range: bytes %d-%d/%d', $rangeArray[0]['from'], $rangeArray[0]['to'], $fileSize), true);
94
+                OC_Response::setContentLengthHeader($rangeArray[0]['to'] - $rangeArray[0]['from'] + 1);
95
+                }
96
+            }
97
+            else {
98
+                OC_Response::setContentLengthHeader($fileSize);
99
+            }
100
+        }
101
+        header('Content-Type: '.$type, true);
102
+    }
103
+
104
+    /**
105
+     * return the content of a file or return a zip file containing multiple files
106
+     *
107
+     * @param string $dir
108
+     * @param string $files ; separated list of files to download
109
+     * @param array $params ; 'head' boolean to only send header of the request ; 'range' http range header
110
+     */
111
+    public static function get($dir, $files, $params = null) {
112
+
113
+        $view = \OC\Files\Filesystem::getView();
114
+        $getType = self::FILE;
115
+        $filename = $dir;
116
+        try {
117
+
118
+            if (is_array($files) && count($files) === 1) {
119
+                $files = $files[0];
120
+            }
121
+
122
+            if (!is_array($files)) {
123
+                $filename = $dir . '/' . $files;
124
+                if (!$view->is_dir($filename)) {
125
+                    self::getSingleFile($view, $dir, $files, is_null($params) ? array() : $params);
126
+                    return;
127
+                }
128
+            }
129
+
130
+            $name = 'download';
131
+            if (is_array($files)) {
132
+                $getType = self::ZIP_FILES;
133
+                $basename = basename($dir);
134
+                if ($basename) {
135
+                    $name = $basename;
136
+                }
137
+
138
+                $filename = $dir . '/' . $name;
139
+            } else {
140
+                $filename = $dir . '/' . $files;
141
+                $getType = self::ZIP_DIR;
142
+                // downloading root ?
143
+                if ($files !== '') {
144
+                    $name = $files;
145
+                }
146
+            }
147
+
148
+            self::lockFiles($view, $dir, $files);
149
+
150
+            /* Calculate filesize and number of files */
151
+            if ($getType === self::ZIP_FILES) {
152
+                $fileInfos = array();
153
+                $fileSize = 0;
154
+                foreach ($files as $file) {
155
+                    $fileInfo = \OC\Files\Filesystem::getFileInfo($dir . '/' . $file);
156
+                    $fileSize += $fileInfo->getSize();
157
+                    $fileInfos[] = $fileInfo;
158
+                }
159
+                $numberOfFiles = self::getNumberOfFiles($fileInfos);
160
+            } elseif ($getType === self::ZIP_DIR) {
161
+                $fileInfo = \OC\Files\Filesystem::getFileInfo($dir . '/' . $files);
162
+                $fileSize = $fileInfo->getSize();
163
+                $numberOfFiles = self::getNumberOfFiles(array($fileInfo));
164
+            }
165
+
166
+            $streamer = new Streamer(\OC::$server->getRequest(), $fileSize, $numberOfFiles);
167
+            OC_Util::obEnd();
168
+
169
+            $streamer->sendHeaders($name);
170
+            $executionTime = (int)OC::$server->getIniWrapper()->getNumeric('max_execution_time');
171
+            if (strpos(@ini_get('disable_functions'), 'set_time_limit') === false) {
172
+                @set_time_limit(0);
173
+            }
174
+            ignore_user_abort(true);
175
+
176
+            if ($getType === self::ZIP_FILES) {
177
+                foreach ($files as $file) {
178
+                    $file = $dir . '/' . $file;
179
+                    if (\OC\Files\Filesystem::is_file($file)) {
180
+                        $fileSize = \OC\Files\Filesystem::filesize($file);
181
+                        $fileTime = \OC\Files\Filesystem::filemtime($file);
182
+                        $fh = \OC\Files\Filesystem::fopen($file, 'r');
183
+                        $streamer->addFileFromStream($fh, basename($file), $fileSize, $fileTime);
184
+                        fclose($fh);
185
+                    } elseif (\OC\Files\Filesystem::is_dir($file)) {
186
+                        $streamer->addDirRecursive($file);
187
+                    }
188
+                }
189
+            } elseif ($getType === self::ZIP_DIR) {
190
+                $file = $dir . '/' . $files;
191
+                $streamer->addDirRecursive($file);
192
+            }
193
+            $streamer->finalize();
194
+            set_time_limit($executionTime);
195
+            self::unlockAllTheFiles($dir, $files, $getType, $view, $filename);
196
+        } catch (\OCP\Lock\LockedException $ex) {
197
+            self::unlockAllTheFiles($dir, $files, $getType, $view, $filename);
198
+            OC::$server->getLogger()->logException($ex);
199
+            $l = \OC::$server->getL10N('core');
200
+            $hint = method_exists($ex, 'getHint') ? $ex->getHint() : '';
201
+            \OC_Template::printErrorPage($l->t('File is currently busy, please try again later'), $hint, 200);
202
+        } catch (\OCP\Files\ForbiddenException $ex) {
203
+            self::unlockAllTheFiles($dir, $files, $getType, $view, $filename);
204
+            OC::$server->getLogger()->logException($ex);
205
+            $l = \OC::$server->getL10N('core');
206
+            \OC_Template::printErrorPage($l->t('Can\'t read file'), $ex->getMessage(), 200);
207
+        } catch (\Exception $ex) {
208
+            self::unlockAllTheFiles($dir, $files, $getType, $view, $filename);
209
+            OC::$server->getLogger()->logException($ex);
210
+            $l = \OC::$server->getL10N('core');
211
+            $hint = method_exists($ex, 'getHint') ? $ex->getHint() : '';
212
+            \OC_Template::printErrorPage($l->t('Can\'t read file'), $hint, 200);
213
+        }
214
+    }
215
+
216
+    /**
217
+     * @param string $rangeHeaderPos
218
+     * @param int $fileSize
219
+     * @return array $rangeArray ('from'=>int,'to'=>int), ...
220
+     */
221
+    private static function parseHttpRangeHeader($rangeHeaderPos, $fileSize) {
222
+        $rArray=explode(',', $rangeHeaderPos);
223
+        $minOffset = 0;
224
+        $ind = 0;
225
+
226
+        $rangeArray = array();
227
+
228
+        foreach ($rArray as $value) {
229
+            $ranges = explode('-', $value);
230
+            if (is_numeric($ranges[0])) {
231
+                if ($ranges[0] < $minOffset) { // case: bytes=500-700,601-999
232
+                    $ranges[0] = $minOffset;
233
+                }
234
+                if ($ind > 0 && $rangeArray[$ind-1]['to']+1 == $ranges[0]) { // case: bytes=500-600,601-999
235
+                    $ind--;
236
+                    $ranges[0] = $rangeArray[$ind]['from'];
237
+                }
238
+            }
239
+
240
+            if (is_numeric($ranges[0]) && is_numeric($ranges[1]) && $ranges[0] < $fileSize && $ranges[0] <= $ranges[1]) {
241
+                // case: x-x
242
+                if ($ranges[1] >= $fileSize) {
243
+                    $ranges[1] = $fileSize-1;
244
+                }
245
+                $rangeArray[$ind++] = array( 'from' => $ranges[0], 'to' => $ranges[1], 'size' => $fileSize );
246
+                $minOffset = $ranges[1] + 1;
247
+                if ($minOffset >= $fileSize) {
248
+                    break;
249
+                }
250
+            }
251
+            elseif (is_numeric($ranges[0]) && $ranges[0] < $fileSize) {
252
+                // case: x-
253
+                $rangeArray[$ind++] = array( 'from' => $ranges[0], 'to' => $fileSize-1, 'size' => $fileSize );
254
+                break;
255
+            }
256
+            elseif (is_numeric($ranges[1])) {
257
+                // case: -x
258
+                if ($ranges[1] > $fileSize) {
259
+                    $ranges[1] = $fileSize;
260
+                }
261
+                $rangeArray[$ind++] = array( 'from' => $fileSize-$ranges[1], 'to' => $fileSize-1, 'size' => $fileSize );
262
+                break;
263
+            }
264
+        }
265
+        return $rangeArray;
266
+    }
267
+
268
+    /**
269
+     * @param View $view
270
+     * @param string $name
271
+     * @param string $dir
272
+     * @param array $params ; 'head' boolean to only send header of the request ; 'range' http range header
273
+     */
274
+    private static function getSingleFile($view, $dir, $name, $params) {
275
+        $filename = $dir . '/' . $name;
276
+        OC_Util::obEnd();
277
+        $view->lockFile($filename, ILockingProvider::LOCK_SHARED);
278 278
 		
279
-		$rangeArray = array();
279
+        $rangeArray = array();
280 280
 
281
-		if (isset($params['range']) && substr($params['range'], 0, 6) === 'bytes=') {
282
-			$rangeArray = self::parseHttpRangeHeader(substr($params['range'], 6), 
283
-								 \OC\Files\Filesystem::filesize($filename));
284
-		}
281
+        if (isset($params['range']) && substr($params['range'], 0, 6) === 'bytes=') {
282
+            $rangeArray = self::parseHttpRangeHeader(substr($params['range'], 6), 
283
+                                    \OC\Files\Filesystem::filesize($filename));
284
+        }
285 285
 		
286
-		if (\OC\Files\Filesystem::isReadable($filename)) {
287
-			self::sendHeaders($filename, $name, $rangeArray);
288
-		} elseif (!\OC\Files\Filesystem::file_exists($filename)) {
289
-			http_response_code(404);
290
-			$tmpl = new OC_Template('', '404', 'guest');
291
-			$tmpl->printPage();
292
-			exit();
293
-		} else {
294
-			http_response_code(403);
295
-			die('403 Forbidden');
296
-		}
297
-		if (isset($params['head']) && $params['head']) {
298
-			return;
299
-		}
300
-		if (!empty($rangeArray)) {
301
-			try {
302
-			    if (count($rangeArray) == 1) {
303
-				$view->readfilePart($filename, $rangeArray[0]['from'], $rangeArray[0]['to']);
304
-			    }
305
-			    else {
306
-				// check if file is seekable (if not throw UnseekableException)
307
-				// we have to check it before body contents
308
-				$view->readfilePart($filename, $rangeArray[0]['size'], $rangeArray[0]['size']);
309
-
310
-				$type = \OC::$server->getMimeTypeDetector()->getSecureMimeType(\OC\Files\Filesystem::getMimeType($filename));
311
-
312
-				foreach ($rangeArray as $range) {
313
-				    echo "\r\n--".self::getBoundary()."\r\n".
314
-				         "Content-type: ".$type."\r\n".
315
-				         "Content-range: bytes ".$range['from']."-".$range['to']."/".$range['size']."\r\n\r\n";
316
-				    $view->readfilePart($filename, $range['from'], $range['to']);
317
-				}
318
-				echo "\r\n--".self::getBoundary()."--\r\n";
319
-			    }
320
-			} catch (\OCP\Files\UnseekableException $ex) {
321
-			    // file is unseekable
322
-			    header_remove('Accept-Ranges');
323
-			    header_remove('Content-Range');
324
-			    http_response_code(200);
325
-			    self::sendHeaders($filename, $name, array());
326
-			    $view->readfile($filename);
327
-			}
328
-		}
329
-		else {
330
-		    $view->readfile($filename);
331
-		}
332
-	}
333
-
334
-	/**
335
-	 * Returns the total (recursive) number of files and folders in the given
336
-	 * FileInfos.
337
-	 *
338
-	 * @param \OCP\Files\FileInfo[] $fileInfos the FileInfos to count
339
-	 * @return int the total number of files and folders
340
-	 */
341
-	private static function getNumberOfFiles($fileInfos) {
342
-		$numberOfFiles = 0;
343
-
344
-		$view = new View();
345
-
346
-		while ($fileInfo = array_pop($fileInfos)) {
347
-			$numberOfFiles++;
348
-
349
-			if ($fileInfo->getType() === \OCP\Files\FileInfo::TYPE_FOLDER) {
350
-				$fileInfos = array_merge($fileInfos, $view->getDirectoryContent($fileInfo->getPath()));
351
-			}
352
-		}
353
-
354
-		return $numberOfFiles;
355
-	}
356
-
357
-	/**
358
-	 * @param View $view
359
-	 * @param string $dir
360
-	 * @param string[]|string $files
361
-	 */
362
-	public static function lockFiles($view, $dir, $files) {
363
-		if (!is_array($files)) {
364
-			$file = $dir . '/' . $files;
365
-			$files = [$file];
366
-		}
367
-		foreach ($files as $file) {
368
-			$file = $dir . '/' . $file;
369
-			$view->lockFile($file, ILockingProvider::LOCK_SHARED);
370
-			if ($view->is_dir($file)) {
371
-				$contents = $view->getDirectoryContent($file);
372
-				$contents = array_map(function($fileInfo) use ($file) {
373
-					/** @var \OCP\Files\FileInfo $fileInfo */
374
-					return $file . '/' . $fileInfo->getName();
375
-				}, $contents);
376
-				self::lockFiles($view, $dir, $contents);
377
-			}
378
-		}
379
-	}
380
-
381
-	/**
382
-	 * @param string $dir
383
-	 * @param $files
384
-	 * @param integer $getType
385
-	 * @param View $view
386
-	 * @param string $filename
387
-	 */
388
-	private static function unlockAllTheFiles($dir, $files, $getType, $view, $filename) {
389
-		if ($getType === self::FILE) {
390
-			$view->unlockFile($filename, ILockingProvider::LOCK_SHARED);
391
-		}
392
-		if ($getType === self::ZIP_FILES) {
393
-			foreach ($files as $file) {
394
-				$file = $dir . '/' . $file;
395
-				$view->unlockFile($file, ILockingProvider::LOCK_SHARED);
396
-			}
397
-		}
398
-		if ($getType === self::ZIP_DIR) {
399
-			$file = $dir . '/' . $files;
400
-			$view->unlockFile($file, ILockingProvider::LOCK_SHARED);
401
-		}
402
-	}
286
+        if (\OC\Files\Filesystem::isReadable($filename)) {
287
+            self::sendHeaders($filename, $name, $rangeArray);
288
+        } elseif (!\OC\Files\Filesystem::file_exists($filename)) {
289
+            http_response_code(404);
290
+            $tmpl = new OC_Template('', '404', 'guest');
291
+            $tmpl->printPage();
292
+            exit();
293
+        } else {
294
+            http_response_code(403);
295
+            die('403 Forbidden');
296
+        }
297
+        if (isset($params['head']) && $params['head']) {
298
+            return;
299
+        }
300
+        if (!empty($rangeArray)) {
301
+            try {
302
+                if (count($rangeArray) == 1) {
303
+                $view->readfilePart($filename, $rangeArray[0]['from'], $rangeArray[0]['to']);
304
+                }
305
+                else {
306
+                // check if file is seekable (if not throw UnseekableException)
307
+                // we have to check it before body contents
308
+                $view->readfilePart($filename, $rangeArray[0]['size'], $rangeArray[0]['size']);
309
+
310
+                $type = \OC::$server->getMimeTypeDetector()->getSecureMimeType(\OC\Files\Filesystem::getMimeType($filename));
311
+
312
+                foreach ($rangeArray as $range) {
313
+                    echo "\r\n--".self::getBoundary()."\r\n".
314
+                            "Content-type: ".$type."\r\n".
315
+                            "Content-range: bytes ".$range['from']."-".$range['to']."/".$range['size']."\r\n\r\n";
316
+                    $view->readfilePart($filename, $range['from'], $range['to']);
317
+                }
318
+                echo "\r\n--".self::getBoundary()."--\r\n";
319
+                }
320
+            } catch (\OCP\Files\UnseekableException $ex) {
321
+                // file is unseekable
322
+                header_remove('Accept-Ranges');
323
+                header_remove('Content-Range');
324
+                http_response_code(200);
325
+                self::sendHeaders($filename, $name, array());
326
+                $view->readfile($filename);
327
+            }
328
+        }
329
+        else {
330
+            $view->readfile($filename);
331
+        }
332
+    }
333
+
334
+    /**
335
+     * Returns the total (recursive) number of files and folders in the given
336
+     * FileInfos.
337
+     *
338
+     * @param \OCP\Files\FileInfo[] $fileInfos the FileInfos to count
339
+     * @return int the total number of files and folders
340
+     */
341
+    private static function getNumberOfFiles($fileInfos) {
342
+        $numberOfFiles = 0;
343
+
344
+        $view = new View();
345
+
346
+        while ($fileInfo = array_pop($fileInfos)) {
347
+            $numberOfFiles++;
348
+
349
+            if ($fileInfo->getType() === \OCP\Files\FileInfo::TYPE_FOLDER) {
350
+                $fileInfos = array_merge($fileInfos, $view->getDirectoryContent($fileInfo->getPath()));
351
+            }
352
+        }
353
+
354
+        return $numberOfFiles;
355
+    }
356
+
357
+    /**
358
+     * @param View $view
359
+     * @param string $dir
360
+     * @param string[]|string $files
361
+     */
362
+    public static function lockFiles($view, $dir, $files) {
363
+        if (!is_array($files)) {
364
+            $file = $dir . '/' . $files;
365
+            $files = [$file];
366
+        }
367
+        foreach ($files as $file) {
368
+            $file = $dir . '/' . $file;
369
+            $view->lockFile($file, ILockingProvider::LOCK_SHARED);
370
+            if ($view->is_dir($file)) {
371
+                $contents = $view->getDirectoryContent($file);
372
+                $contents = array_map(function($fileInfo) use ($file) {
373
+                    /** @var \OCP\Files\FileInfo $fileInfo */
374
+                    return $file . '/' . $fileInfo->getName();
375
+                }, $contents);
376
+                self::lockFiles($view, $dir, $contents);
377
+            }
378
+        }
379
+    }
380
+
381
+    /**
382
+     * @param string $dir
383
+     * @param $files
384
+     * @param integer $getType
385
+     * @param View $view
386
+     * @param string $filename
387
+     */
388
+    private static function unlockAllTheFiles($dir, $files, $getType, $view, $filename) {
389
+        if ($getType === self::FILE) {
390
+            $view->unlockFile($filename, ILockingProvider::LOCK_SHARED);
391
+        }
392
+        if ($getType === self::ZIP_FILES) {
393
+            foreach ($files as $file) {
394
+                $file = $dir . '/' . $file;
395
+                $view->unlockFile($file, ILockingProvider::LOCK_SHARED);
396
+            }
397
+        }
398
+        if ($getType === self::ZIP_DIR) {
399
+            $file = $dir . '/' . $files;
400
+            $view->unlockFile($file, ILockingProvider::LOCK_SHARED);
401
+        }
402
+    }
403 403
 
404 404
 }
Please login to merge, or discard this patch.
Spacing   +21 added lines, -21 removed lines patch added patch discarded remove patch
@@ -76,7 +76,7 @@  discard block
 block discarded – undo
76 76
 	private static function sendHeaders($filename, $name, array $rangeArray) {
77 77
 		OC_Response::setContentDispositionHeader($name, 'attachment');
78 78
 		header('Content-Transfer-Encoding: binary', true);
79
-		header('Pragma: public');// enable caching in IE
79
+		header('Pragma: public'); // enable caching in IE
80 80
 		header('Expires: 0');
81 81
 		header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
82 82
 		$fileSize = \OC\Files\Filesystem::filesize($filename);
@@ -120,7 +120,7 @@  discard block
 block discarded – undo
120 120
 			}
121 121
 
122 122
 			if (!is_array($files)) {
123
-				$filename = $dir . '/' . $files;
123
+				$filename = $dir.'/'.$files;
124 124
 				if (!$view->is_dir($filename)) {
125 125
 					self::getSingleFile($view, $dir, $files, is_null($params) ? array() : $params);
126 126
 					return;
@@ -135,9 +135,9 @@  discard block
 block discarded – undo
135 135
 					$name = $basename;
136 136
 				}
137 137
 
138
-				$filename = $dir . '/' . $name;
138
+				$filename = $dir.'/'.$name;
139 139
 			} else {
140
-				$filename = $dir . '/' . $files;
140
+				$filename = $dir.'/'.$files;
141 141
 				$getType = self::ZIP_DIR;
142 142
 				// downloading root ?
143 143
 				if ($files !== '') {
@@ -152,13 +152,13 @@  discard block
 block discarded – undo
152 152
 				$fileInfos = array();
153 153
 				$fileSize = 0;
154 154
 				foreach ($files as $file) {
155
-					$fileInfo = \OC\Files\Filesystem::getFileInfo($dir . '/' . $file);
155
+					$fileInfo = \OC\Files\Filesystem::getFileInfo($dir.'/'.$file);
156 156
 					$fileSize += $fileInfo->getSize();
157 157
 					$fileInfos[] = $fileInfo;
158 158
 				}
159 159
 				$numberOfFiles = self::getNumberOfFiles($fileInfos);
160 160
 			} elseif ($getType === self::ZIP_DIR) {
161
-				$fileInfo = \OC\Files\Filesystem::getFileInfo($dir . '/' . $files);
161
+				$fileInfo = \OC\Files\Filesystem::getFileInfo($dir.'/'.$files);
162 162
 				$fileSize = $fileInfo->getSize();
163 163
 				$numberOfFiles = self::getNumberOfFiles(array($fileInfo));
164 164
 			}
@@ -167,7 +167,7 @@  discard block
 block discarded – undo
167 167
 			OC_Util::obEnd();
168 168
 
169 169
 			$streamer->sendHeaders($name);
170
-			$executionTime = (int)OC::$server->getIniWrapper()->getNumeric('max_execution_time');
170
+			$executionTime = (int) OC::$server->getIniWrapper()->getNumeric('max_execution_time');
171 171
 			if (strpos(@ini_get('disable_functions'), 'set_time_limit') === false) {
172 172
 				@set_time_limit(0);
173 173
 			}
@@ -175,7 +175,7 @@  discard block
 block discarded – undo
175 175
 
176 176
 			if ($getType === self::ZIP_FILES) {
177 177
 				foreach ($files as $file) {
178
-					$file = $dir . '/' . $file;
178
+					$file = $dir.'/'.$file;
179 179
 					if (\OC\Files\Filesystem::is_file($file)) {
180 180
 						$fileSize = \OC\Files\Filesystem::filesize($file);
181 181
 						$fileTime = \OC\Files\Filesystem::filemtime($file);
@@ -187,7 +187,7 @@  discard block
 block discarded – undo
187 187
 					}
188 188
 				}
189 189
 			} elseif ($getType === self::ZIP_DIR) {
190
-				$file = $dir . '/' . $files;
190
+				$file = $dir.'/'.$files;
191 191
 				$streamer->addDirRecursive($file);
192 192
 			}
193 193
 			$streamer->finalize();
@@ -219,7 +219,7 @@  discard block
 block discarded – undo
219 219
 	 * @return array $rangeArray ('from'=>int,'to'=>int), ...
220 220
 	 */
221 221
 	private static function parseHttpRangeHeader($rangeHeaderPos, $fileSize) {
222
-		$rArray=explode(',', $rangeHeaderPos);
222
+		$rArray = explode(',', $rangeHeaderPos);
223 223
 		$minOffset = 0;
224 224
 		$ind = 0;
225 225
 
@@ -231,7 +231,7 @@  discard block
 block discarded – undo
231 231
 				if ($ranges[0] < $minOffset) { // case: bytes=500-700,601-999
232 232
 					$ranges[0] = $minOffset;
233 233
 				}
234
-				if ($ind > 0 && $rangeArray[$ind-1]['to']+1 == $ranges[0]) { // case: bytes=500-600,601-999
234
+				if ($ind > 0 && $rangeArray[$ind - 1]['to'] + 1 == $ranges[0]) { // case: bytes=500-600,601-999
235 235
 					$ind--;
236 236
 					$ranges[0] = $rangeArray[$ind]['from'];
237 237
 				}
@@ -240,9 +240,9 @@  discard block
 block discarded – undo
240 240
 			if (is_numeric($ranges[0]) && is_numeric($ranges[1]) && $ranges[0] < $fileSize && $ranges[0] <= $ranges[1]) {
241 241
 				// case: x-x
242 242
 				if ($ranges[1] >= $fileSize) {
243
-					$ranges[1] = $fileSize-1;
243
+					$ranges[1] = $fileSize - 1;
244 244
 				}
245
-				$rangeArray[$ind++] = array( 'from' => $ranges[0], 'to' => $ranges[1], 'size' => $fileSize );
245
+				$rangeArray[$ind++] = array('from' => $ranges[0], 'to' => $ranges[1], 'size' => $fileSize);
246 246
 				$minOffset = $ranges[1] + 1;
247 247
 				if ($minOffset >= $fileSize) {
248 248
 					break;
@@ -250,7 +250,7 @@  discard block
 block discarded – undo
250 250
 			}
251 251
 			elseif (is_numeric($ranges[0]) && $ranges[0] < $fileSize) {
252 252
 				// case: x-
253
-				$rangeArray[$ind++] = array( 'from' => $ranges[0], 'to' => $fileSize-1, 'size' => $fileSize );
253
+				$rangeArray[$ind++] = array('from' => $ranges[0], 'to' => $fileSize - 1, 'size' => $fileSize);
254 254
 				break;
255 255
 			}
256 256
 			elseif (is_numeric($ranges[1])) {
@@ -258,7 +258,7 @@  discard block
 block discarded – undo
258 258
 				if ($ranges[1] > $fileSize) {
259 259
 					$ranges[1] = $fileSize;
260 260
 				}
261
-				$rangeArray[$ind++] = array( 'from' => $fileSize-$ranges[1], 'to' => $fileSize-1, 'size' => $fileSize );
261
+				$rangeArray[$ind++] = array('from' => $fileSize - $ranges[1], 'to' => $fileSize - 1, 'size' => $fileSize);
262 262
 				break;
263 263
 			}
264 264
 		}
@@ -272,7 +272,7 @@  discard block
 block discarded – undo
272 272
 	 * @param array $params ; 'head' boolean to only send header of the request ; 'range' http range header
273 273
 	 */
274 274
 	private static function getSingleFile($view, $dir, $name, $params) {
275
-		$filename = $dir . '/' . $name;
275
+		$filename = $dir.'/'.$name;
276 276
 		OC_Util::obEnd();
277 277
 		$view->lockFile($filename, ILockingProvider::LOCK_SHARED);
278 278
 		
@@ -361,17 +361,17 @@  discard block
 block discarded – undo
361 361
 	 */
362 362
 	public static function lockFiles($view, $dir, $files) {
363 363
 		if (!is_array($files)) {
364
-			$file = $dir . '/' . $files;
364
+			$file = $dir.'/'.$files;
365 365
 			$files = [$file];
366 366
 		}
367 367
 		foreach ($files as $file) {
368
-			$file = $dir . '/' . $file;
368
+			$file = $dir.'/'.$file;
369 369
 			$view->lockFile($file, ILockingProvider::LOCK_SHARED);
370 370
 			if ($view->is_dir($file)) {
371 371
 				$contents = $view->getDirectoryContent($file);
372 372
 				$contents = array_map(function($fileInfo) use ($file) {
373 373
 					/** @var \OCP\Files\FileInfo $fileInfo */
374
-					return $file . '/' . $fileInfo->getName();
374
+					return $file.'/'.$fileInfo->getName();
375 375
 				}, $contents);
376 376
 				self::lockFiles($view, $dir, $contents);
377 377
 			}
@@ -391,12 +391,12 @@  discard block
 block discarded – undo
391 391
 		}
392 392
 		if ($getType === self::ZIP_FILES) {
393 393
 			foreach ($files as $file) {
394
-				$file = $dir . '/' . $file;
394
+				$file = $dir.'/'.$file;
395 395
 				$view->unlockFile($file, ILockingProvider::LOCK_SHARED);
396 396
 			}
397 397
 		}
398 398
 		if ($getType === self::ZIP_DIR) {
399
-			$file = $dir . '/' . $files;
399
+			$file = $dir.'/'.$files;
400 400
 			$view->unlockFile($file, ILockingProvider::LOCK_SHARED);
401 401
 		}
402 402
 	}
Please login to merge, or discard this patch.
lib/private/legacy/helper.php 2 patches
Indentation   +547 added lines, -547 removed lines patch added patch discarded remove patch
@@ -49,551 +49,551 @@
 block discarded – undo
49 49
  * Collection of useful functions
50 50
  */
51 51
 class OC_Helper {
52
-	private static $templateManager;
53
-
54
-	/**
55
-	 * Make a human file size
56
-	 * @param int $bytes file size in bytes
57
-	 * @return string a human readable file size
58
-	 *
59
-	 * Makes 2048 to 2 kB.
60
-	 */
61
-	public static function humanFileSize($bytes) {
62
-		if ($bytes < 0) {
63
-			return "?";
64
-		}
65
-		if ($bytes < 1024) {
66
-			return "$bytes B";
67
-		}
68
-		$bytes = round($bytes / 1024, 0);
69
-		if ($bytes < 1024) {
70
-			return "$bytes KB";
71
-		}
72
-		$bytes = round($bytes / 1024, 1);
73
-		if ($bytes < 1024) {
74
-			return "$bytes MB";
75
-		}
76
-		$bytes = round($bytes / 1024, 1);
77
-		if ($bytes < 1024) {
78
-			return "$bytes GB";
79
-		}
80
-		$bytes = round($bytes / 1024, 1);
81
-		if ($bytes < 1024) {
82
-			return "$bytes TB";
83
-		}
84
-
85
-		$bytes = round($bytes / 1024, 1);
86
-		return "$bytes PB";
87
-	}
88
-
89
-	/**
90
-	 * Make a computer file size
91
-	 * @param string $str file size in human readable format
92
-	 * @return float|bool a file size in bytes
93
-	 *
94
-	 * Makes 2kB to 2048.
95
-	 *
96
-	 * Inspired by: http://www.php.net/manual/en/function.filesize.php#92418
97
-	 */
98
-	public static function computerFileSize($str) {
99
-		$str = strtolower($str);
100
-		if (is_numeric($str)) {
101
-			return (float)$str;
102
-		}
103
-
104
-		$bytes_array = array(
105
-			'b' => 1,
106
-			'k' => 1024,
107
-			'kb' => 1024,
108
-			'mb' => 1024 * 1024,
109
-			'm' => 1024 * 1024,
110
-			'gb' => 1024 * 1024 * 1024,
111
-			'g' => 1024 * 1024 * 1024,
112
-			'tb' => 1024 * 1024 * 1024 * 1024,
113
-			't' => 1024 * 1024 * 1024 * 1024,
114
-			'pb' => 1024 * 1024 * 1024 * 1024 * 1024,
115
-			'p' => 1024 * 1024 * 1024 * 1024 * 1024,
116
-		);
117
-
118
-		$bytes = (float)$str;
119
-
120
-		if (preg_match('#([kmgtp]?b?)$#si', $str, $matches) && !empty($bytes_array[$matches[1]])) {
121
-			$bytes *= $bytes_array[$matches[1]];
122
-		} else {
123
-			return false;
124
-		}
125
-
126
-		$bytes = round($bytes);
127
-
128
-		return $bytes;
129
-	}
130
-
131
-	/**
132
-	 * Recursive copying of folders
133
-	 * @param string $src source folder
134
-	 * @param string $dest target folder
135
-	 *
136
-	 */
137
-	static function copyr($src, $dest) {
138
-		if (is_dir($src)) {
139
-			if (!is_dir($dest)) {
140
-				mkdir($dest);
141
-			}
142
-			$files = scandir($src);
143
-			foreach ($files as $file) {
144
-				if ($file != "." && $file != "..") {
145
-					self::copyr("$src/$file", "$dest/$file");
146
-				}
147
-			}
148
-		} elseif (file_exists($src) && !\OC\Files\Filesystem::isFileBlacklisted($src)) {
149
-			copy($src, $dest);
150
-		}
151
-	}
152
-
153
-	/**
154
-	 * Recursive deletion of folders
155
-	 * @param string $dir path to the folder
156
-	 * @param bool $deleteSelf if set to false only the content of the folder will be deleted
157
-	 * @return bool
158
-	 */
159
-	static function rmdirr($dir, $deleteSelf = true) {
160
-		if (is_dir($dir)) {
161
-			$files = new RecursiveIteratorIterator(
162
-				new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
163
-				RecursiveIteratorIterator::CHILD_FIRST
164
-			);
165
-
166
-			foreach ($files as $fileInfo) {
167
-				/** @var SplFileInfo $fileInfo */
168
-				if ($fileInfo->isLink()) {
169
-					unlink($fileInfo->getPathname());
170
-				} else if ($fileInfo->isDir()) {
171
-					rmdir($fileInfo->getRealPath());
172
-				} else {
173
-					unlink($fileInfo->getRealPath());
174
-				}
175
-			}
176
-			if ($deleteSelf) {
177
-				rmdir($dir);
178
-			}
179
-		} elseif (file_exists($dir)) {
180
-			if ($deleteSelf) {
181
-				unlink($dir);
182
-			}
183
-		}
184
-		if (!$deleteSelf) {
185
-			return true;
186
-		}
187
-
188
-		return !file_exists($dir);
189
-	}
190
-
191
-	/**
192
-	 * @return \OC\Files\Type\TemplateManager
193
-	 */
194
-	static public function getFileTemplateManager() {
195
-		if (!self::$templateManager) {
196
-			self::$templateManager = new \OC\Files\Type\TemplateManager();
197
-		}
198
-		return self::$templateManager;
199
-	}
200
-
201
-	/**
202
-	 * detect if a given program is found in the search PATH
203
-	 *
204
-	 * @param string $name
205
-	 * @param bool $path
206
-	 * @internal param string $program name
207
-	 * @internal param string $optional search path, defaults to $PATH
208
-	 * @return bool    true if executable program found in path
209
-	 */
210
-	public static function canExecute($name, $path = false) {
211
-		// path defaults to PATH from environment if not set
212
-		if ($path === false) {
213
-			$path = getenv("PATH");
214
-		}
215
-		// we look for an executable file of that name
216
-		$exts = [""];
217
-		$check_fn = "is_executable";
218
-		// Default check will be done with $path directories :
219
-		$dirs = explode(PATH_SEPARATOR, $path);
220
-		// WARNING : We have to check if open_basedir is enabled :
221
-		$obd = OC::$server->getIniWrapper()->getString('open_basedir');
222
-		if ($obd != "none") {
223
-			$obd_values = explode(PATH_SEPARATOR, $obd);
224
-			if (count($obd_values) > 0 and $obd_values[0]) {
225
-				// open_basedir is in effect !
226
-				// We need to check if the program is in one of these dirs :
227
-				$dirs = $obd_values;
228
-			}
229
-		}
230
-		foreach ($dirs as $dir) {
231
-			foreach ($exts as $ext) {
232
-				if ($check_fn("$dir/$name" . $ext))
233
-					return true;
234
-			}
235
-		}
236
-		return false;
237
-	}
238
-
239
-	/**
240
-	 * copy the contents of one stream to another
241
-	 *
242
-	 * @param resource $source
243
-	 * @param resource $target
244
-	 * @return array the number of bytes copied and result
245
-	 */
246
-	public static function streamCopy($source, $target) {
247
-		if (!$source or !$target) {
248
-			return array(0, false);
249
-		}
250
-		$bufSize = 8192;
251
-		$result = true;
252
-		$count = 0;
253
-		while (!feof($source)) {
254
-			$buf = fread($source, $bufSize);
255
-			$bytesWritten = fwrite($target, $buf);
256
-			if ($bytesWritten !== false) {
257
-				$count += $bytesWritten;
258
-			}
259
-			// note: strlen is expensive so only use it when necessary,
260
-			// on the last block
261
-			if ($bytesWritten === false
262
-				|| ($bytesWritten < $bufSize && $bytesWritten < strlen($buf))
263
-			) {
264
-				// write error, could be disk full ?
265
-				$result = false;
266
-				break;
267
-			}
268
-		}
269
-		return array($count, $result);
270
-	}
271
-
272
-	/**
273
-	 * Adds a suffix to the name in case the file exists
274
-	 *
275
-	 * @param string $path
276
-	 * @param string $filename
277
-	 * @return string
278
-	 */
279
-	public static function buildNotExistingFileName($path, $filename) {
280
-		$view = \OC\Files\Filesystem::getView();
281
-		return self::buildNotExistingFileNameForView($path, $filename, $view);
282
-	}
283
-
284
-	/**
285
-	 * Adds a suffix to the name in case the file exists
286
-	 *
287
-	 * @param string $path
288
-	 * @param string $filename
289
-	 * @return string
290
-	 */
291
-	public static function buildNotExistingFileNameForView($path, $filename, \OC\Files\View $view) {
292
-		if ($path === '/') {
293
-			$path = '';
294
-		}
295
-		if ($pos = strrpos($filename, '.')) {
296
-			$name = substr($filename, 0, $pos);
297
-			$ext = substr($filename, $pos);
298
-		} else {
299
-			$name = $filename;
300
-			$ext = '';
301
-		}
302
-
303
-		$newpath = $path . '/' . $filename;
304
-		if ($view->file_exists($newpath)) {
305
-			if (preg_match_all('/\((\d+)\)/', $name, $matches, PREG_OFFSET_CAPTURE)) {
306
-				//Replace the last "(number)" with "(number+1)"
307
-				$last_match = count($matches[0]) - 1;
308
-				$counter = $matches[1][$last_match][0] + 1;
309
-				$offset = $matches[0][$last_match][1];
310
-				$match_length = strlen($matches[0][$last_match][0]);
311
-			} else {
312
-				$counter = 2;
313
-				$match_length = 0;
314
-				$offset = false;
315
-			}
316
-			do {
317
-				if ($offset) {
318
-					//Replace the last "(number)" with "(number+1)"
319
-					$newname = substr_replace($name, '(' . $counter . ')', $offset, $match_length);
320
-				} else {
321
-					$newname = $name . ' (' . $counter . ')';
322
-				}
323
-				$newpath = $path . '/' . $newname . $ext;
324
-				$counter++;
325
-			} while ($view->file_exists($newpath));
326
-		}
327
-
328
-		return $newpath;
329
-	}
330
-
331
-	/**
332
-	 * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is.
333
-	 *
334
-	 * @param array $input The array to work on
335
-	 * @param int $case Either MB_CASE_UPPER or MB_CASE_LOWER (default)
336
-	 * @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8
337
-	 * @return array
338
-	 *
339
-	 * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is.
340
-	 * based on http://www.php.net/manual/en/function.array-change-key-case.php#107715
341
-	 *
342
-	 */
343
-	public static function mb_array_change_key_case($input, $case = MB_CASE_LOWER, $encoding = 'UTF-8') {
344
-		$case = ($case != MB_CASE_UPPER) ? MB_CASE_LOWER : MB_CASE_UPPER;
345
-		$ret = array();
346
-		foreach ($input as $k => $v) {
347
-			$ret[mb_convert_case($k, $case, $encoding)] = $v;
348
-		}
349
-		return $ret;
350
-	}
351
-
352
-	/**
353
-	 * performs a search in a nested array
354
-	 * @param array $haystack the array to be searched
355
-	 * @param string $needle the search string
356
-	 * @param mixed $index optional, only search this key name
357
-	 * @return mixed the key of the matching field, otherwise false
358
-	 *
359
-	 * performs a search in a nested array
360
-	 *
361
-	 * taken from http://www.php.net/manual/en/function.array-search.php#97645
362
-	 */
363
-	public static function recursiveArraySearch($haystack, $needle, $index = null) {
364
-		$aIt = new RecursiveArrayIterator($haystack);
365
-		$it = new RecursiveIteratorIterator($aIt);
366
-
367
-		while ($it->valid()) {
368
-			if (((isset($index) AND ($it->key() == $index)) OR !isset($index)) AND ($it->current() == $needle)) {
369
-				return $aIt->key();
370
-			}
371
-
372
-			$it->next();
373
-		}
374
-
375
-		return false;
376
-	}
377
-
378
-	/**
379
-	 * calculates the maximum upload size respecting system settings, free space and user quota
380
-	 *
381
-	 * @param string $dir the current folder where the user currently operates
382
-	 * @param int $freeSpace the number of bytes free on the storage holding $dir, if not set this will be received from the storage directly
383
-	 * @return int number of bytes representing
384
-	 */
385
-	public static function maxUploadFilesize($dir, $freeSpace = null) {
386
-		if (is_null($freeSpace) || $freeSpace < 0){
387
-			$freeSpace = self::freeSpace($dir);
388
-		}
389
-		return min($freeSpace, self::uploadLimit());
390
-	}
391
-
392
-	/**
393
-	 * Calculate free space left within user quota
394
-	 *
395
-	 * @param string $dir the current folder where the user currently operates
396
-	 * @return int number of bytes representing
397
-	 */
398
-	public static function freeSpace($dir) {
399
-		$freeSpace = \OC\Files\Filesystem::free_space($dir);
400
-		if ($freeSpace < \OCP\Files\FileInfo::SPACE_UNLIMITED) {
401
-			$freeSpace = max($freeSpace, 0);
402
-			return $freeSpace;
403
-		} else {
404
-			return (INF > 0)? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188
405
-		}
406
-	}
407
-
408
-	/**
409
-	 * Calculate PHP upload limit
410
-	 *
411
-	 * @return int PHP upload file size limit
412
-	 */
413
-	public static function uploadLimit() {
414
-		$ini = \OC::$server->getIniWrapper();
415
-		$upload_max_filesize = OCP\Util::computerFileSize($ini->get('upload_max_filesize'));
416
-		$post_max_size = OCP\Util::computerFileSize($ini->get('post_max_size'));
417
-		if ((int)$upload_max_filesize === 0 and (int)$post_max_size === 0) {
418
-			return INF;
419
-		} elseif ((int)$upload_max_filesize === 0 or (int)$post_max_size === 0) {
420
-			return max($upload_max_filesize, $post_max_size); //only the non 0 value counts
421
-		} else {
422
-			return min($upload_max_filesize, $post_max_size);
423
-		}
424
-	}
425
-
426
-	/**
427
-	 * Checks if a function is available
428
-	 *
429
-	 * @param string $function_name
430
-	 * @return bool
431
-	 */
432
-	public static function is_function_enabled($function_name) {
433
-		if (!function_exists($function_name)) {
434
-			return false;
435
-		}
436
-		$ini = \OC::$server->getIniWrapper();
437
-		$disabled = explode(',', $ini->get('disable_functions') ?: '');
438
-		$disabled = array_map('trim', $disabled);
439
-		if (in_array($function_name, $disabled)) {
440
-			return false;
441
-		}
442
-		$disabled = explode(',', $ini->get('suhosin.executor.func.blacklist') ?: '');
443
-		$disabled = array_map('trim', $disabled);
444
-		if (in_array($function_name, $disabled)) {
445
-			return false;
446
-		}
447
-		return true;
448
-	}
449
-
450
-	/**
451
-	 * Try to find a program
452
-	 *
453
-	 * @param string $program
454
-	 * @return null|string
455
-	 */
456
-	public static function findBinaryPath($program) {
457
-		$memcache = \OC::$server->getMemCacheFactory()->createDistributed('findBinaryPath');
458
-		if ($memcache->hasKey($program)) {
459
-			return $memcache->get($program);
460
-		}
461
-		$result = null;
462
-		if (self::is_function_enabled('exec')) {
463
-			$exeSniffer = new ExecutableFinder();
464
-			// Returns null if nothing is found
465
-			$result = $exeSniffer->find($program, null, ['/usr/local/sbin', '/usr/local/bin', '/usr/sbin', '/usr/bin', '/sbin', '/bin', '/opt/bin']);
466
-		}
467
-		// store the value for 5 minutes
468
-		$memcache->set($program, $result, 300);
469
-		return $result;
470
-	}
471
-
472
-	/**
473
-	 * Calculate the disc space for the given path
474
-	 *
475
-	 * @param string $path
476
-	 * @param \OCP\Files\FileInfo $rootInfo (optional)
477
-	 * @return array
478
-	 * @throws \OCP\Files\NotFoundException
479
-	 */
480
-	public static function getStorageInfo($path, $rootInfo = null) {
481
-		// return storage info without adding mount points
482
-		$includeExtStorage = \OC::$server->getSystemConfig()->getValue('quota_include_external_storage', false);
483
-
484
-		if (!$rootInfo) {
485
-			$rootInfo = \OC\Files\Filesystem::getFileInfo($path, $includeExtStorage ? 'ext' : false);
486
-		}
487
-		if (!$rootInfo instanceof \OCP\Files\FileInfo) {
488
-			throw new \OCP\Files\NotFoundException();
489
-		}
490
-		$used = $rootInfo->getSize();
491
-		if ($used < 0) {
492
-			$used = 0;
493
-		}
494
-		$quota = \OCP\Files\FileInfo::SPACE_UNLIMITED;
495
-		$storage = $rootInfo->getStorage();
496
-		$sourceStorage = $storage;
497
-		if ($storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage')) {
498
-			$includeExtStorage = false;
499
-			$sourceStorage = $storage->getSourceStorage();
500
-		}
501
-		if ($includeExtStorage) {
502
-			if ($storage->instanceOfStorage('\OC\Files\Storage\Home')
503
-				|| $storage->instanceOfStorage('\OC\Files\ObjectStore\HomeObjectStoreStorage')
504
-			) {
505
-				/** @var \OC\Files\Storage\Home $storage */
506
-				$userInstance = $storage->getUser();
507
-				$user = ($userInstance === null) ? null : $userInstance->getUID();
508
-			} else {
509
-				$user = \OC::$server->getUserSession()->getUser()->getUID();
510
-			}
511
-			if ($user) {
512
-				$quota = OC_Util::getUserQuota($user);
513
-			} else {
514
-				$quota = \OCP\Files\FileInfo::SPACE_UNLIMITED;
515
-			}
516
-			if ($quota !== \OCP\Files\FileInfo::SPACE_UNLIMITED) {
517
-				// always get free space / total space from root + mount points
518
-				return self::getGlobalStorageInfo();
519
-			}
520
-		}
521
-
522
-		// TODO: need a better way to get total space from storage
523
-		if ($sourceStorage->instanceOfStorage('\OC\Files\Storage\Wrapper\Quota')) {
524
-			/** @var \OC\Files\Storage\Wrapper\Quota $storage */
525
-			$quota = $sourceStorage->getQuota();
526
-		}
527
-		$free = $sourceStorage->free_space($rootInfo->getInternalPath());
528
-		if ($free >= 0) {
529
-			$total = $free + $used;
530
-		} else {
531
-			$total = $free; //either unknown or unlimited
532
-		}
533
-		if ($total > 0) {
534
-			if ($quota > 0 && $total > $quota) {
535
-				$total = $quota;
536
-			}
537
-			// prevent division by zero or error codes (negative values)
538
-			$relative = round(($used / $total) * 10000) / 100;
539
-		} else {
540
-			$relative = 0;
541
-		}
542
-
543
-		$ownerId = $storage->getOwner($path);
544
-		$ownerDisplayName = '';
545
-		$owner = \OC::$server->getUserManager()->get($ownerId);
546
-		if($owner) {
547
-			$ownerDisplayName = $owner->getDisplayName();
548
-		}
549
-
550
-		return [
551
-			'free' => $free,
552
-			'used' => $used,
553
-			'quota' => $quota,
554
-			'total' => $total,
555
-			'relative' => $relative,
556
-			'owner' => $ownerId,
557
-			'ownerDisplayName' => $ownerDisplayName,
558
-		];
559
-	}
560
-
561
-	/**
562
-	 * Get storage info including all mount points and quota
563
-	 *
564
-	 * @return array
565
-	 */
566
-	private static function getGlobalStorageInfo() {
567
-		$quota = OC_Util::getUserQuota(\OCP\User::getUser());
568
-
569
-		$rootInfo = \OC\Files\Filesystem::getFileInfo('', 'ext');
570
-		$used = $rootInfo['size'];
571
-		if ($used < 0) {
572
-			$used = 0;
573
-		}
574
-
575
-		$total = $quota;
576
-		$free = $quota - $used;
577
-
578
-		if ($total > 0) {
579
-			if ($quota > 0 && $total > $quota) {
580
-				$total = $quota;
581
-			}
582
-			// prevent division by zero or error codes (negative values)
583
-			$relative = round(($used / $total) * 10000) / 100;
584
-		} else {
585
-			$relative = 0;
586
-		}
587
-
588
-		return array('free' => $free, 'used' => $used, 'total' => $total, 'relative' => $relative);
589
-
590
-	}
591
-
592
-	/**
593
-	 * Returns whether the config file is set manually to read-only
594
-	 * @return bool
595
-	 */
596
-	public static function isReadOnlyConfigEnabled() {
597
-		return \OC::$server->getConfig()->getSystemValue('config_is_read_only', false);
598
-	}
52
+    private static $templateManager;
53
+
54
+    /**
55
+     * Make a human file size
56
+     * @param int $bytes file size in bytes
57
+     * @return string a human readable file size
58
+     *
59
+     * Makes 2048 to 2 kB.
60
+     */
61
+    public static function humanFileSize($bytes) {
62
+        if ($bytes < 0) {
63
+            return "?";
64
+        }
65
+        if ($bytes < 1024) {
66
+            return "$bytes B";
67
+        }
68
+        $bytes = round($bytes / 1024, 0);
69
+        if ($bytes < 1024) {
70
+            return "$bytes KB";
71
+        }
72
+        $bytes = round($bytes / 1024, 1);
73
+        if ($bytes < 1024) {
74
+            return "$bytes MB";
75
+        }
76
+        $bytes = round($bytes / 1024, 1);
77
+        if ($bytes < 1024) {
78
+            return "$bytes GB";
79
+        }
80
+        $bytes = round($bytes / 1024, 1);
81
+        if ($bytes < 1024) {
82
+            return "$bytes TB";
83
+        }
84
+
85
+        $bytes = round($bytes / 1024, 1);
86
+        return "$bytes PB";
87
+    }
88
+
89
+    /**
90
+     * Make a computer file size
91
+     * @param string $str file size in human readable format
92
+     * @return float|bool a file size in bytes
93
+     *
94
+     * Makes 2kB to 2048.
95
+     *
96
+     * Inspired by: http://www.php.net/manual/en/function.filesize.php#92418
97
+     */
98
+    public static function computerFileSize($str) {
99
+        $str = strtolower($str);
100
+        if (is_numeric($str)) {
101
+            return (float)$str;
102
+        }
103
+
104
+        $bytes_array = array(
105
+            'b' => 1,
106
+            'k' => 1024,
107
+            'kb' => 1024,
108
+            'mb' => 1024 * 1024,
109
+            'm' => 1024 * 1024,
110
+            'gb' => 1024 * 1024 * 1024,
111
+            'g' => 1024 * 1024 * 1024,
112
+            'tb' => 1024 * 1024 * 1024 * 1024,
113
+            't' => 1024 * 1024 * 1024 * 1024,
114
+            'pb' => 1024 * 1024 * 1024 * 1024 * 1024,
115
+            'p' => 1024 * 1024 * 1024 * 1024 * 1024,
116
+        );
117
+
118
+        $bytes = (float)$str;
119
+
120
+        if (preg_match('#([kmgtp]?b?)$#si', $str, $matches) && !empty($bytes_array[$matches[1]])) {
121
+            $bytes *= $bytes_array[$matches[1]];
122
+        } else {
123
+            return false;
124
+        }
125
+
126
+        $bytes = round($bytes);
127
+
128
+        return $bytes;
129
+    }
130
+
131
+    /**
132
+     * Recursive copying of folders
133
+     * @param string $src source folder
134
+     * @param string $dest target folder
135
+     *
136
+     */
137
+    static function copyr($src, $dest) {
138
+        if (is_dir($src)) {
139
+            if (!is_dir($dest)) {
140
+                mkdir($dest);
141
+            }
142
+            $files = scandir($src);
143
+            foreach ($files as $file) {
144
+                if ($file != "." && $file != "..") {
145
+                    self::copyr("$src/$file", "$dest/$file");
146
+                }
147
+            }
148
+        } elseif (file_exists($src) && !\OC\Files\Filesystem::isFileBlacklisted($src)) {
149
+            copy($src, $dest);
150
+        }
151
+    }
152
+
153
+    /**
154
+     * Recursive deletion of folders
155
+     * @param string $dir path to the folder
156
+     * @param bool $deleteSelf if set to false only the content of the folder will be deleted
157
+     * @return bool
158
+     */
159
+    static function rmdirr($dir, $deleteSelf = true) {
160
+        if (is_dir($dir)) {
161
+            $files = new RecursiveIteratorIterator(
162
+                new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
163
+                RecursiveIteratorIterator::CHILD_FIRST
164
+            );
165
+
166
+            foreach ($files as $fileInfo) {
167
+                /** @var SplFileInfo $fileInfo */
168
+                if ($fileInfo->isLink()) {
169
+                    unlink($fileInfo->getPathname());
170
+                } else if ($fileInfo->isDir()) {
171
+                    rmdir($fileInfo->getRealPath());
172
+                } else {
173
+                    unlink($fileInfo->getRealPath());
174
+                }
175
+            }
176
+            if ($deleteSelf) {
177
+                rmdir($dir);
178
+            }
179
+        } elseif (file_exists($dir)) {
180
+            if ($deleteSelf) {
181
+                unlink($dir);
182
+            }
183
+        }
184
+        if (!$deleteSelf) {
185
+            return true;
186
+        }
187
+
188
+        return !file_exists($dir);
189
+    }
190
+
191
+    /**
192
+     * @return \OC\Files\Type\TemplateManager
193
+     */
194
+    static public function getFileTemplateManager() {
195
+        if (!self::$templateManager) {
196
+            self::$templateManager = new \OC\Files\Type\TemplateManager();
197
+        }
198
+        return self::$templateManager;
199
+    }
200
+
201
+    /**
202
+     * detect if a given program is found in the search PATH
203
+     *
204
+     * @param string $name
205
+     * @param bool $path
206
+     * @internal param string $program name
207
+     * @internal param string $optional search path, defaults to $PATH
208
+     * @return bool    true if executable program found in path
209
+     */
210
+    public static function canExecute($name, $path = false) {
211
+        // path defaults to PATH from environment if not set
212
+        if ($path === false) {
213
+            $path = getenv("PATH");
214
+        }
215
+        // we look for an executable file of that name
216
+        $exts = [""];
217
+        $check_fn = "is_executable";
218
+        // Default check will be done with $path directories :
219
+        $dirs = explode(PATH_SEPARATOR, $path);
220
+        // WARNING : We have to check if open_basedir is enabled :
221
+        $obd = OC::$server->getIniWrapper()->getString('open_basedir');
222
+        if ($obd != "none") {
223
+            $obd_values = explode(PATH_SEPARATOR, $obd);
224
+            if (count($obd_values) > 0 and $obd_values[0]) {
225
+                // open_basedir is in effect !
226
+                // We need to check if the program is in one of these dirs :
227
+                $dirs = $obd_values;
228
+            }
229
+        }
230
+        foreach ($dirs as $dir) {
231
+            foreach ($exts as $ext) {
232
+                if ($check_fn("$dir/$name" . $ext))
233
+                    return true;
234
+            }
235
+        }
236
+        return false;
237
+    }
238
+
239
+    /**
240
+     * copy the contents of one stream to another
241
+     *
242
+     * @param resource $source
243
+     * @param resource $target
244
+     * @return array the number of bytes copied and result
245
+     */
246
+    public static function streamCopy($source, $target) {
247
+        if (!$source or !$target) {
248
+            return array(0, false);
249
+        }
250
+        $bufSize = 8192;
251
+        $result = true;
252
+        $count = 0;
253
+        while (!feof($source)) {
254
+            $buf = fread($source, $bufSize);
255
+            $bytesWritten = fwrite($target, $buf);
256
+            if ($bytesWritten !== false) {
257
+                $count += $bytesWritten;
258
+            }
259
+            // note: strlen is expensive so only use it when necessary,
260
+            // on the last block
261
+            if ($bytesWritten === false
262
+                || ($bytesWritten < $bufSize && $bytesWritten < strlen($buf))
263
+            ) {
264
+                // write error, could be disk full ?
265
+                $result = false;
266
+                break;
267
+            }
268
+        }
269
+        return array($count, $result);
270
+    }
271
+
272
+    /**
273
+     * Adds a suffix to the name in case the file exists
274
+     *
275
+     * @param string $path
276
+     * @param string $filename
277
+     * @return string
278
+     */
279
+    public static function buildNotExistingFileName($path, $filename) {
280
+        $view = \OC\Files\Filesystem::getView();
281
+        return self::buildNotExistingFileNameForView($path, $filename, $view);
282
+    }
283
+
284
+    /**
285
+     * Adds a suffix to the name in case the file exists
286
+     *
287
+     * @param string $path
288
+     * @param string $filename
289
+     * @return string
290
+     */
291
+    public static function buildNotExistingFileNameForView($path, $filename, \OC\Files\View $view) {
292
+        if ($path === '/') {
293
+            $path = '';
294
+        }
295
+        if ($pos = strrpos($filename, '.')) {
296
+            $name = substr($filename, 0, $pos);
297
+            $ext = substr($filename, $pos);
298
+        } else {
299
+            $name = $filename;
300
+            $ext = '';
301
+        }
302
+
303
+        $newpath = $path . '/' . $filename;
304
+        if ($view->file_exists($newpath)) {
305
+            if (preg_match_all('/\((\d+)\)/', $name, $matches, PREG_OFFSET_CAPTURE)) {
306
+                //Replace the last "(number)" with "(number+1)"
307
+                $last_match = count($matches[0]) - 1;
308
+                $counter = $matches[1][$last_match][0] + 1;
309
+                $offset = $matches[0][$last_match][1];
310
+                $match_length = strlen($matches[0][$last_match][0]);
311
+            } else {
312
+                $counter = 2;
313
+                $match_length = 0;
314
+                $offset = false;
315
+            }
316
+            do {
317
+                if ($offset) {
318
+                    //Replace the last "(number)" with "(number+1)"
319
+                    $newname = substr_replace($name, '(' . $counter . ')', $offset, $match_length);
320
+                } else {
321
+                    $newname = $name . ' (' . $counter . ')';
322
+                }
323
+                $newpath = $path . '/' . $newname . $ext;
324
+                $counter++;
325
+            } while ($view->file_exists($newpath));
326
+        }
327
+
328
+        return $newpath;
329
+    }
330
+
331
+    /**
332
+     * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is.
333
+     *
334
+     * @param array $input The array to work on
335
+     * @param int $case Either MB_CASE_UPPER or MB_CASE_LOWER (default)
336
+     * @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8
337
+     * @return array
338
+     *
339
+     * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is.
340
+     * based on http://www.php.net/manual/en/function.array-change-key-case.php#107715
341
+     *
342
+     */
343
+    public static function mb_array_change_key_case($input, $case = MB_CASE_LOWER, $encoding = 'UTF-8') {
344
+        $case = ($case != MB_CASE_UPPER) ? MB_CASE_LOWER : MB_CASE_UPPER;
345
+        $ret = array();
346
+        foreach ($input as $k => $v) {
347
+            $ret[mb_convert_case($k, $case, $encoding)] = $v;
348
+        }
349
+        return $ret;
350
+    }
351
+
352
+    /**
353
+     * performs a search in a nested array
354
+     * @param array $haystack the array to be searched
355
+     * @param string $needle the search string
356
+     * @param mixed $index optional, only search this key name
357
+     * @return mixed the key of the matching field, otherwise false
358
+     *
359
+     * performs a search in a nested array
360
+     *
361
+     * taken from http://www.php.net/manual/en/function.array-search.php#97645
362
+     */
363
+    public static function recursiveArraySearch($haystack, $needle, $index = null) {
364
+        $aIt = new RecursiveArrayIterator($haystack);
365
+        $it = new RecursiveIteratorIterator($aIt);
366
+
367
+        while ($it->valid()) {
368
+            if (((isset($index) AND ($it->key() == $index)) OR !isset($index)) AND ($it->current() == $needle)) {
369
+                return $aIt->key();
370
+            }
371
+
372
+            $it->next();
373
+        }
374
+
375
+        return false;
376
+    }
377
+
378
+    /**
379
+     * calculates the maximum upload size respecting system settings, free space and user quota
380
+     *
381
+     * @param string $dir the current folder where the user currently operates
382
+     * @param int $freeSpace the number of bytes free on the storage holding $dir, if not set this will be received from the storage directly
383
+     * @return int number of bytes representing
384
+     */
385
+    public static function maxUploadFilesize($dir, $freeSpace = null) {
386
+        if (is_null($freeSpace) || $freeSpace < 0){
387
+            $freeSpace = self::freeSpace($dir);
388
+        }
389
+        return min($freeSpace, self::uploadLimit());
390
+    }
391
+
392
+    /**
393
+     * Calculate free space left within user quota
394
+     *
395
+     * @param string $dir the current folder where the user currently operates
396
+     * @return int number of bytes representing
397
+     */
398
+    public static function freeSpace($dir) {
399
+        $freeSpace = \OC\Files\Filesystem::free_space($dir);
400
+        if ($freeSpace < \OCP\Files\FileInfo::SPACE_UNLIMITED) {
401
+            $freeSpace = max($freeSpace, 0);
402
+            return $freeSpace;
403
+        } else {
404
+            return (INF > 0)? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188
405
+        }
406
+    }
407
+
408
+    /**
409
+     * Calculate PHP upload limit
410
+     *
411
+     * @return int PHP upload file size limit
412
+     */
413
+    public static function uploadLimit() {
414
+        $ini = \OC::$server->getIniWrapper();
415
+        $upload_max_filesize = OCP\Util::computerFileSize($ini->get('upload_max_filesize'));
416
+        $post_max_size = OCP\Util::computerFileSize($ini->get('post_max_size'));
417
+        if ((int)$upload_max_filesize === 0 and (int)$post_max_size === 0) {
418
+            return INF;
419
+        } elseif ((int)$upload_max_filesize === 0 or (int)$post_max_size === 0) {
420
+            return max($upload_max_filesize, $post_max_size); //only the non 0 value counts
421
+        } else {
422
+            return min($upload_max_filesize, $post_max_size);
423
+        }
424
+    }
425
+
426
+    /**
427
+     * Checks if a function is available
428
+     *
429
+     * @param string $function_name
430
+     * @return bool
431
+     */
432
+    public static function is_function_enabled($function_name) {
433
+        if (!function_exists($function_name)) {
434
+            return false;
435
+        }
436
+        $ini = \OC::$server->getIniWrapper();
437
+        $disabled = explode(',', $ini->get('disable_functions') ?: '');
438
+        $disabled = array_map('trim', $disabled);
439
+        if (in_array($function_name, $disabled)) {
440
+            return false;
441
+        }
442
+        $disabled = explode(',', $ini->get('suhosin.executor.func.blacklist') ?: '');
443
+        $disabled = array_map('trim', $disabled);
444
+        if (in_array($function_name, $disabled)) {
445
+            return false;
446
+        }
447
+        return true;
448
+    }
449
+
450
+    /**
451
+     * Try to find a program
452
+     *
453
+     * @param string $program
454
+     * @return null|string
455
+     */
456
+    public static function findBinaryPath($program) {
457
+        $memcache = \OC::$server->getMemCacheFactory()->createDistributed('findBinaryPath');
458
+        if ($memcache->hasKey($program)) {
459
+            return $memcache->get($program);
460
+        }
461
+        $result = null;
462
+        if (self::is_function_enabled('exec')) {
463
+            $exeSniffer = new ExecutableFinder();
464
+            // Returns null if nothing is found
465
+            $result = $exeSniffer->find($program, null, ['/usr/local/sbin', '/usr/local/bin', '/usr/sbin', '/usr/bin', '/sbin', '/bin', '/opt/bin']);
466
+        }
467
+        // store the value for 5 minutes
468
+        $memcache->set($program, $result, 300);
469
+        return $result;
470
+    }
471
+
472
+    /**
473
+     * Calculate the disc space for the given path
474
+     *
475
+     * @param string $path
476
+     * @param \OCP\Files\FileInfo $rootInfo (optional)
477
+     * @return array
478
+     * @throws \OCP\Files\NotFoundException
479
+     */
480
+    public static function getStorageInfo($path, $rootInfo = null) {
481
+        // return storage info without adding mount points
482
+        $includeExtStorage = \OC::$server->getSystemConfig()->getValue('quota_include_external_storage', false);
483
+
484
+        if (!$rootInfo) {
485
+            $rootInfo = \OC\Files\Filesystem::getFileInfo($path, $includeExtStorage ? 'ext' : false);
486
+        }
487
+        if (!$rootInfo instanceof \OCP\Files\FileInfo) {
488
+            throw new \OCP\Files\NotFoundException();
489
+        }
490
+        $used = $rootInfo->getSize();
491
+        if ($used < 0) {
492
+            $used = 0;
493
+        }
494
+        $quota = \OCP\Files\FileInfo::SPACE_UNLIMITED;
495
+        $storage = $rootInfo->getStorage();
496
+        $sourceStorage = $storage;
497
+        if ($storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage')) {
498
+            $includeExtStorage = false;
499
+            $sourceStorage = $storage->getSourceStorage();
500
+        }
501
+        if ($includeExtStorage) {
502
+            if ($storage->instanceOfStorage('\OC\Files\Storage\Home')
503
+                || $storage->instanceOfStorage('\OC\Files\ObjectStore\HomeObjectStoreStorage')
504
+            ) {
505
+                /** @var \OC\Files\Storage\Home $storage */
506
+                $userInstance = $storage->getUser();
507
+                $user = ($userInstance === null) ? null : $userInstance->getUID();
508
+            } else {
509
+                $user = \OC::$server->getUserSession()->getUser()->getUID();
510
+            }
511
+            if ($user) {
512
+                $quota = OC_Util::getUserQuota($user);
513
+            } else {
514
+                $quota = \OCP\Files\FileInfo::SPACE_UNLIMITED;
515
+            }
516
+            if ($quota !== \OCP\Files\FileInfo::SPACE_UNLIMITED) {
517
+                // always get free space / total space from root + mount points
518
+                return self::getGlobalStorageInfo();
519
+            }
520
+        }
521
+
522
+        // TODO: need a better way to get total space from storage
523
+        if ($sourceStorage->instanceOfStorage('\OC\Files\Storage\Wrapper\Quota')) {
524
+            /** @var \OC\Files\Storage\Wrapper\Quota $storage */
525
+            $quota = $sourceStorage->getQuota();
526
+        }
527
+        $free = $sourceStorage->free_space($rootInfo->getInternalPath());
528
+        if ($free >= 0) {
529
+            $total = $free + $used;
530
+        } else {
531
+            $total = $free; //either unknown or unlimited
532
+        }
533
+        if ($total > 0) {
534
+            if ($quota > 0 && $total > $quota) {
535
+                $total = $quota;
536
+            }
537
+            // prevent division by zero or error codes (negative values)
538
+            $relative = round(($used / $total) * 10000) / 100;
539
+        } else {
540
+            $relative = 0;
541
+        }
542
+
543
+        $ownerId = $storage->getOwner($path);
544
+        $ownerDisplayName = '';
545
+        $owner = \OC::$server->getUserManager()->get($ownerId);
546
+        if($owner) {
547
+            $ownerDisplayName = $owner->getDisplayName();
548
+        }
549
+
550
+        return [
551
+            'free' => $free,
552
+            'used' => $used,
553
+            'quota' => $quota,
554
+            'total' => $total,
555
+            'relative' => $relative,
556
+            'owner' => $ownerId,
557
+            'ownerDisplayName' => $ownerDisplayName,
558
+        ];
559
+    }
560
+
561
+    /**
562
+     * Get storage info including all mount points and quota
563
+     *
564
+     * @return array
565
+     */
566
+    private static function getGlobalStorageInfo() {
567
+        $quota = OC_Util::getUserQuota(\OCP\User::getUser());
568
+
569
+        $rootInfo = \OC\Files\Filesystem::getFileInfo('', 'ext');
570
+        $used = $rootInfo['size'];
571
+        if ($used < 0) {
572
+            $used = 0;
573
+        }
574
+
575
+        $total = $quota;
576
+        $free = $quota - $used;
577
+
578
+        if ($total > 0) {
579
+            if ($quota > 0 && $total > $quota) {
580
+                $total = $quota;
581
+            }
582
+            // prevent division by zero or error codes (negative values)
583
+            $relative = round(($used / $total) * 10000) / 100;
584
+        } else {
585
+            $relative = 0;
586
+        }
587
+
588
+        return array('free' => $free, 'used' => $used, 'total' => $total, 'relative' => $relative);
589
+
590
+    }
591
+
592
+    /**
593
+     * Returns whether the config file is set manually to read-only
594
+     * @return bool
595
+     */
596
+    public static function isReadOnlyConfigEnabled() {
597
+        return \OC::$server->getConfig()->getSystemValue('config_is_read_only', false);
598
+    }
599 599
 }
Please login to merge, or discard this patch.
Spacing   +12 added lines, -12 removed lines patch added patch discarded remove patch
@@ -98,7 +98,7 @@  discard block
 block discarded – undo
98 98
 	public static function computerFileSize($str) {
99 99
 		$str = strtolower($str);
100 100
 		if (is_numeric($str)) {
101
-			return (float)$str;
101
+			return (float) $str;
102 102
 		}
103 103
 
104 104
 		$bytes_array = array(
@@ -115,7 +115,7 @@  discard block
 block discarded – undo
115 115
 			'p' => 1024 * 1024 * 1024 * 1024 * 1024,
116 116
 		);
117 117
 
118
-		$bytes = (float)$str;
118
+		$bytes = (float) $str;
119 119
 
120 120
 		if (preg_match('#([kmgtp]?b?)$#si', $str, $matches) && !empty($bytes_array[$matches[1]])) {
121 121
 			$bytes *= $bytes_array[$matches[1]];
@@ -229,7 +229,7 @@  discard block
 block discarded – undo
229 229
 		}
230 230
 		foreach ($dirs as $dir) {
231 231
 			foreach ($exts as $ext) {
232
-				if ($check_fn("$dir/$name" . $ext))
232
+				if ($check_fn("$dir/$name".$ext))
233 233
 					return true;
234 234
 			}
235 235
 		}
@@ -300,7 +300,7 @@  discard block
 block discarded – undo
300 300
 			$ext = '';
301 301
 		}
302 302
 
303
-		$newpath = $path . '/' . $filename;
303
+		$newpath = $path.'/'.$filename;
304 304
 		if ($view->file_exists($newpath)) {
305 305
 			if (preg_match_all('/\((\d+)\)/', $name, $matches, PREG_OFFSET_CAPTURE)) {
306 306
 				//Replace the last "(number)" with "(number+1)"
@@ -316,11 +316,11 @@  discard block
 block discarded – undo
316 316
 			do {
317 317
 				if ($offset) {
318 318
 					//Replace the last "(number)" with "(number+1)"
319
-					$newname = substr_replace($name, '(' . $counter . ')', $offset, $match_length);
319
+					$newname = substr_replace($name, '('.$counter.')', $offset, $match_length);
320 320
 				} else {
321
-					$newname = $name . ' (' . $counter . ')';
321
+					$newname = $name.' ('.$counter.')';
322 322
 				}
323
-				$newpath = $path . '/' . $newname . $ext;
323
+				$newpath = $path.'/'.$newname.$ext;
324 324
 				$counter++;
325 325
 			} while ($view->file_exists($newpath));
326 326
 		}
@@ -383,7 +383,7 @@  discard block
 block discarded – undo
383 383
 	 * @return int number of bytes representing
384 384
 	 */
385 385
 	public static function maxUploadFilesize($dir, $freeSpace = null) {
386
-		if (is_null($freeSpace) || $freeSpace < 0){
386
+		if (is_null($freeSpace) || $freeSpace < 0) {
387 387
 			$freeSpace = self::freeSpace($dir);
388 388
 		}
389 389
 		return min($freeSpace, self::uploadLimit());
@@ -401,7 +401,7 @@  discard block
 block discarded – undo
401 401
 			$freeSpace = max($freeSpace, 0);
402 402
 			return $freeSpace;
403 403
 		} else {
404
-			return (INF > 0)? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188
404
+			return (INF > 0) ? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188
405 405
 		}
406 406
 	}
407 407
 
@@ -414,9 +414,9 @@  discard block
 block discarded – undo
414 414
 		$ini = \OC::$server->getIniWrapper();
415 415
 		$upload_max_filesize = OCP\Util::computerFileSize($ini->get('upload_max_filesize'));
416 416
 		$post_max_size = OCP\Util::computerFileSize($ini->get('post_max_size'));
417
-		if ((int)$upload_max_filesize === 0 and (int)$post_max_size === 0) {
417
+		if ((int) $upload_max_filesize === 0 and (int) $post_max_size === 0) {
418 418
 			return INF;
419
-		} elseif ((int)$upload_max_filesize === 0 or (int)$post_max_size === 0) {
419
+		} elseif ((int) $upload_max_filesize === 0 or (int) $post_max_size === 0) {
420 420
 			return max($upload_max_filesize, $post_max_size); //only the non 0 value counts
421 421
 		} else {
422 422
 			return min($upload_max_filesize, $post_max_size);
@@ -543,7 +543,7 @@  discard block
 block discarded – undo
543 543
 		$ownerId = $storage->getOwner($path);
544 544
 		$ownerDisplayName = '';
545 545
 		$owner = \OC::$server->getUserManager()->get($ownerId);
546
-		if($owner) {
546
+		if ($owner) {
547 547
 			$ownerDisplayName = $owner->getDisplayName();
548 548
 		}
549 549
 
Please login to merge, or discard this patch.
lib/private/IntegrityCheck/Checker.php 2 patches
Indentation   +523 added lines, -523 removed lines patch added patch discarded remove patch
@@ -52,527 +52,527 @@
 block discarded – undo
52 52
  * @package OC\IntegrityCheck
53 53
  */
54 54
 class Checker {
55
-	const CACHE_KEY = 'oc.integritycheck.checker';
56
-	/** @var EnvironmentHelper */
57
-	private $environmentHelper;
58
-	/** @var AppLocator */
59
-	private $appLocator;
60
-	/** @var FileAccessHelper */
61
-	private $fileAccessHelper;
62
-	/** @var IConfig */
63
-	private $config;
64
-	/** @var ICache */
65
-	private $cache;
66
-	/** @var IAppManager */
67
-	private $appManager;
68
-	/** @var ITempManager */
69
-	private $tempManager;
70
-
71
-	/**
72
-	 * @param EnvironmentHelper $environmentHelper
73
-	 * @param FileAccessHelper $fileAccessHelper
74
-	 * @param AppLocator $appLocator
75
-	 * @param IConfig $config
76
-	 * @param ICacheFactory $cacheFactory
77
-	 * @param IAppManager $appManager
78
-	 * @param ITempManager $tempManager
79
-	 */
80
-	public function __construct(EnvironmentHelper $environmentHelper,
81
-								FileAccessHelper $fileAccessHelper,
82
-								AppLocator $appLocator,
83
-								IConfig $config = null,
84
-								ICacheFactory $cacheFactory,
85
-								IAppManager $appManager = null,
86
-								ITempManager $tempManager) {
87
-		$this->environmentHelper = $environmentHelper;
88
-		$this->fileAccessHelper = $fileAccessHelper;
89
-		$this->appLocator = $appLocator;
90
-		$this->config = $config;
91
-		$this->cache = $cacheFactory->createDistributed(self::CACHE_KEY);
92
-		$this->appManager = $appManager;
93
-		$this->tempManager = $tempManager;
94
-	}
95
-
96
-	/**
97
-	 * Whether code signing is enforced or not.
98
-	 *
99
-	 * @return bool
100
-	 */
101
-	public function isCodeCheckEnforced(): bool {
102
-		$notSignedChannels = [ '', 'git'];
103
-		if (\in_array($this->environmentHelper->getChannel(), $notSignedChannels, true)) {
104
-			return false;
105
-		}
106
-
107
-		/**
108
-		 * This config option is undocumented and supposed to be so, it's only
109
-		 * applicable for very specific scenarios and we should not advertise it
110
-		 * too prominent. So please do not add it to config.sample.php.
111
-		 */
112
-		$isIntegrityCheckDisabled = false;
113
-		if ($this->config !== null) {
114
-			$isIntegrityCheckDisabled = $this->config->getSystemValue('integrity.check.disabled', false);
115
-		}
116
-		if ($isIntegrityCheckDisabled === true) {
117
-			return false;
118
-		}
119
-
120
-		return true;
121
-	}
122
-
123
-	/**
124
-	 * Enumerates all files belonging to the folder. Sensible defaults are excluded.
125
-	 *
126
-	 * @param string $folderToIterate
127
-	 * @param string $root
128
-	 * @return \RecursiveIteratorIterator
129
-	 * @throws \Exception
130
-	 */
131
-	private function getFolderIterator(string $folderToIterate, string $root = ''): \RecursiveIteratorIterator {
132
-		$dirItr = new \RecursiveDirectoryIterator(
133
-			$folderToIterate,
134
-			\RecursiveDirectoryIterator::SKIP_DOTS
135
-		);
136
-		if($root === '') {
137
-			$root = \OC::$SERVERROOT;
138
-		}
139
-		$root = rtrim($root, '/');
140
-
141
-		$excludeGenericFilesIterator = new ExcludeFileByNameFilterIterator($dirItr);
142
-		$excludeFoldersIterator = new ExcludeFoldersByPathFilterIterator($excludeGenericFilesIterator, $root);
143
-
144
-		return new \RecursiveIteratorIterator(
145
-			$excludeFoldersIterator,
146
-			\RecursiveIteratorIterator::SELF_FIRST
147
-		);
148
-	}
149
-
150
-	/**
151
-	 * Returns an array of ['filename' => 'SHA512-hash-of-file'] for all files found
152
-	 * in the iterator.
153
-	 *
154
-	 * @param \RecursiveIteratorIterator $iterator
155
-	 * @param string $path
156
-	 * @return array Array of hashes.
157
-	 */
158
-	private function generateHashes(\RecursiveIteratorIterator $iterator,
159
-									string $path): array {
160
-		$hashes = [];
161
-
162
-		$baseDirectoryLength = \strlen($path);
163
-		foreach($iterator as $filename => $data) {
164
-			/** @var \DirectoryIterator $data */
165
-			if($data->isDir()) {
166
-				continue;
167
-			}
168
-
169
-			$relativeFileName = substr($filename, $baseDirectoryLength);
170
-			$relativeFileName = ltrim($relativeFileName, '/');
171
-
172
-			// Exclude signature.json files in the appinfo and root folder
173
-			if($relativeFileName === 'appinfo/signature.json') {
174
-				continue;
175
-			}
176
-			// Exclude signature.json files in the appinfo and core folder
177
-			if($relativeFileName === 'core/signature.json') {
178
-				continue;
179
-			}
180
-
181
-			// The .htaccess file in the root folder of ownCloud can contain
182
-			// custom content after the installation due to the fact that dynamic
183
-			// content is written into it at installation time as well. This
184
-			// includes for example the 404 and 403 instructions.
185
-			// Thus we ignore everything below the first occurrence of
186
-			// "#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####" and have the
187
-			// hash generated based on this.
188
-			if($filename === $this->environmentHelper->getServerRoot() . '/.htaccess') {
189
-				$fileContent = file_get_contents($filename);
190
-				$explodedArray = explode('#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####', $fileContent);
191
-				if(\count($explodedArray) === 2) {
192
-					$hashes[$relativeFileName] = hash('sha512', $explodedArray[0]);
193
-					continue;
194
-				}
195
-			}
196
-
197
-			$hashes[$relativeFileName] = hash_file('sha512', $filename);
198
-		}
199
-
200
-		return $hashes;
201
-	}
202
-
203
-	/**
204
-	 * Creates the signature data
205
-	 *
206
-	 * @param array $hashes
207
-	 * @param X509 $certificate
208
-	 * @param RSA $privateKey
209
-	 * @return array
210
-	 */
211
-	private function createSignatureData(array $hashes,
212
-										 X509 $certificate,
213
-										 RSA $privateKey): array {
214
-		ksort($hashes);
215
-
216
-		$privateKey->setSignatureMode(RSA::SIGNATURE_PSS);
217
-		$privateKey->setMGFHash('sha512');
218
-		// See https://tools.ietf.org/html/rfc3447#page-38
219
-		$privateKey->setSaltLength(0);
220
-		$signature = $privateKey->sign(json_encode($hashes));
221
-
222
-		return [
223
-				'hashes' => $hashes,
224
-				'signature' => base64_encode($signature),
225
-				'certificate' => $certificate->saveX509($certificate->currentCert),
226
-			];
227
-	}
228
-
229
-	/**
230
-	 * Write the signature of the app in the specified folder
231
-	 *
232
-	 * @param string $path
233
-	 * @param X509 $certificate
234
-	 * @param RSA $privateKey
235
-	 * @throws \Exception
236
-	 */
237
-	public function writeAppSignature($path,
238
-									  X509 $certificate,
239
-									  RSA $privateKey) {
240
-		$appInfoDir = $path . '/appinfo';
241
-		try {
242
-			$this->fileAccessHelper->assertDirectoryExists($appInfoDir);
243
-
244
-			$iterator = $this->getFolderIterator($path);
245
-			$hashes = $this->generateHashes($iterator, $path);
246
-			$signature = $this->createSignatureData($hashes, $certificate, $privateKey);
247
-				$this->fileAccessHelper->file_put_contents(
248
-					$appInfoDir . '/signature.json',
249
-				json_encode($signature, JSON_PRETTY_PRINT)
250
-			);
251
-		} catch (\Exception $e){
252
-			if (!$this->fileAccessHelper->is_writable($appInfoDir)) {
253
-				throw new \Exception($appInfoDir . ' is not writable');
254
-			}
255
-			throw $e;
256
-		}
257
-	}
258
-
259
-	/**
260
-	 * Write the signature of core
261
-	 *
262
-	 * @param X509 $certificate
263
-	 * @param RSA $rsa
264
-	 * @param string $path
265
-	 * @throws \Exception
266
-	 */
267
-	public function writeCoreSignature(X509 $certificate,
268
-									   RSA $rsa,
269
-									   $path) {
270
-		$coreDir = $path . '/core';
271
-		try {
272
-
273
-			$this->fileAccessHelper->assertDirectoryExists($coreDir);
274
-			$iterator = $this->getFolderIterator($path, $path);
275
-			$hashes = $this->generateHashes($iterator, $path);
276
-			$signatureData = $this->createSignatureData($hashes, $certificate, $rsa);
277
-			$this->fileAccessHelper->file_put_contents(
278
-				$coreDir . '/signature.json',
279
-				json_encode($signatureData, JSON_PRETTY_PRINT)
280
-			);
281
-		} catch (\Exception $e){
282
-			if (!$this->fileAccessHelper->is_writable($coreDir)) {
283
-				throw new \Exception($coreDir . ' is not writable');
284
-			}
285
-			throw $e;
286
-		}
287
-	}
288
-
289
-	/**
290
-	 * Verifies the signature for the specified path.
291
-	 *
292
-	 * @param string $signaturePath
293
-	 * @param string $basePath
294
-	 * @param string $certificateCN
295
-	 * @return array
296
-	 * @throws InvalidSignatureException
297
-	 * @throws \Exception
298
-	 */
299
-	private function verify(string $signaturePath, string $basePath, string $certificateCN): array {
300
-		if(!$this->isCodeCheckEnforced()) {
301
-			return [];
302
-		}
303
-
304
-		$content = $this->fileAccessHelper->file_get_contents($signaturePath);
305
-		$signatureData = null;
306
-
307
-		if (\is_string($content)) {
308
-			$signatureData = json_decode($content, true);
309
-		}
310
-		if(!\is_array($signatureData)) {
311
-			throw new InvalidSignatureException('Signature data not found.');
312
-		}
313
-
314
-		$expectedHashes = $signatureData['hashes'];
315
-		ksort($expectedHashes);
316
-		$signature = base64_decode($signatureData['signature']);
317
-		$certificate = $signatureData['certificate'];
318
-
319
-		// Check if certificate is signed by Nextcloud Root Authority
320
-		$x509 = new \phpseclib\File\X509();
321
-		$rootCertificatePublicKey = $this->fileAccessHelper->file_get_contents($this->environmentHelper->getServerRoot().'/resources/codesigning/root.crt');
322
-		$x509->loadCA($rootCertificatePublicKey);
323
-		$x509->loadX509($certificate);
324
-		if(!$x509->validateSignature()) {
325
-			throw new InvalidSignatureException('Certificate is not valid.');
326
-		}
327
-		// Verify if certificate has proper CN. "core" CN is always trusted.
328
-		if($x509->getDN(X509::DN_OPENSSL)['CN'] !== $certificateCN && $x509->getDN(X509::DN_OPENSSL)['CN'] !== 'core') {
329
-			throw new InvalidSignatureException(
330
-					sprintf('Certificate is not valid for required scope. (Requested: %s, current: CN=%s)', $certificateCN, $x509->getDN(true)['CN'])
331
-			);
332
-		}
333
-
334
-		// Check if the signature of the files is valid
335
-		$rsa = new \phpseclib\Crypt\RSA();
336
-		$rsa->loadKey($x509->currentCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']);
337
-		$rsa->setSignatureMode(RSA::SIGNATURE_PSS);
338
-		$rsa->setMGFHash('sha512');
339
-		// See https://tools.ietf.org/html/rfc3447#page-38
340
-		$rsa->setSaltLength(0);
341
-		if(!$rsa->verify(json_encode($expectedHashes), $signature)) {
342
-			throw new InvalidSignatureException('Signature could not get verified.');
343
-		}
344
-
345
-		// Fixes for the updater as shipped with ownCloud 9.0.x: The updater is
346
-		// replaced after the code integrity check is performed.
347
-		//
348
-		// Due to this reason we exclude the whole updater/ folder from the code
349
-		// integrity check.
350
-		if($basePath === $this->environmentHelper->getServerRoot()) {
351
-			foreach($expectedHashes as $fileName => $hash) {
352
-				if(strpos($fileName, 'updater/') === 0) {
353
-					unset($expectedHashes[$fileName]);
354
-				}
355
-			}
356
-		}
357
-
358
-		// Compare the list of files which are not identical
359
-		$currentInstanceHashes = $this->generateHashes($this->getFolderIterator($basePath), $basePath);
360
-		$differencesA = array_diff($expectedHashes, $currentInstanceHashes);
361
-		$differencesB = array_diff($currentInstanceHashes, $expectedHashes);
362
-		$differences = array_unique(array_merge($differencesA, $differencesB));
363
-		$differenceArray = [];
364
-		foreach($differences as $filename => $hash) {
365
-			// Check if file should not exist in the new signature table
366
-			if(!array_key_exists($filename, $expectedHashes)) {
367
-				$differenceArray['EXTRA_FILE'][$filename]['expected'] = '';
368
-				$differenceArray['EXTRA_FILE'][$filename]['current'] = $hash;
369
-				continue;
370
-			}
371
-
372
-			// Check if file is missing
373
-			if(!array_key_exists($filename, $currentInstanceHashes)) {
374
-				$differenceArray['FILE_MISSING'][$filename]['expected'] = $expectedHashes[$filename];
375
-				$differenceArray['FILE_MISSING'][$filename]['current'] = '';
376
-				continue;
377
-			}
378
-
379
-			// Check if hash does mismatch
380
-			if($expectedHashes[$filename] !== $currentInstanceHashes[$filename]) {
381
-				$differenceArray['INVALID_HASH'][$filename]['expected'] = $expectedHashes[$filename];
382
-				$differenceArray['INVALID_HASH'][$filename]['current'] = $currentInstanceHashes[$filename];
383
-				continue;
384
-			}
385
-
386
-			// Should never happen.
387
-			throw new \Exception('Invalid behaviour in file hash comparison experienced. Please report this error to the developers.');
388
-		}
389
-
390
-		return $differenceArray;
391
-	}
392
-
393
-	/**
394
-	 * Whether the code integrity check has passed successful or not
395
-	 *
396
-	 * @return bool
397
-	 */
398
-	public function hasPassedCheck(): bool {
399
-		$results = $this->getResults();
400
-		if(empty($results)) {
401
-			return true;
402
-		}
403
-
404
-		return false;
405
-	}
406
-
407
-	/**
408
-	 * @return array
409
-	 */
410
-	public function getResults(): array {
411
-		$cachedResults = $this->cache->get(self::CACHE_KEY);
412
-		if(!\is_null($cachedResults)) {
413
-			return json_decode($cachedResults, true);
414
-		}
415
-
416
-		if ($this->config !== null) {
417
-			return json_decode($this->config->getAppValue('core', self::CACHE_KEY, '{}'), true);
418
-		}
419
-		return [];
420
-	}
421
-
422
-	/**
423
-	 * Stores the results in the app config as well as cache
424
-	 *
425
-	 * @param string $scope
426
-	 * @param array $result
427
-	 */
428
-	private function storeResults(string $scope, array $result) {
429
-		$resultArray = $this->getResults();
430
-		unset($resultArray[$scope]);
431
-		if(!empty($result)) {
432
-			$resultArray[$scope] = $result;
433
-		}
434
-		if ($this->config !== null) {
435
-			$this->config->setAppValue('core', self::CACHE_KEY, json_encode($resultArray));
436
-		}
437
-		$this->cache->set(self::CACHE_KEY, json_encode($resultArray));
438
-	}
439
-
440
-	/**
441
-	 *
442
-	 * Clean previous results for a proper rescanning. Otherwise
443
-	 */
444
-	private function cleanResults() {
445
-		$this->config->deleteAppValue('core', self::CACHE_KEY);
446
-		$this->cache->remove(self::CACHE_KEY);
447
-	}
448
-
449
-	/**
450
-	 * Verify the signature of $appId. Returns an array with the following content:
451
-	 * [
452
-	 * 	'FILE_MISSING' =>
453
-	 * 	[
454
-	 * 		'filename' => [
455
-	 * 			'expected' => 'expectedSHA512',
456
-	 * 			'current' => 'currentSHA512',
457
-	 * 		],
458
-	 * 	],
459
-	 * 	'EXTRA_FILE' =>
460
-	 * 	[
461
-	 * 		'filename' => [
462
-	 * 			'expected' => 'expectedSHA512',
463
-	 * 			'current' => 'currentSHA512',
464
-	 * 		],
465
-	 * 	],
466
-	 * 	'INVALID_HASH' =>
467
-	 * 	[
468
-	 * 		'filename' => [
469
-	 * 			'expected' => 'expectedSHA512',
470
-	 * 			'current' => 'currentSHA512',
471
-	 * 		],
472
-	 * 	],
473
-	 * ]
474
-	 *
475
-	 * Array may be empty in case no problems have been found.
476
-	 *
477
-	 * @param string $appId
478
-	 * @param string $path Optional path. If none is given it will be guessed.
479
-	 * @return array
480
-	 */
481
-	public function verifyAppSignature(string $appId, string $path = ''): array {
482
-		try {
483
-			if($path === '') {
484
-				$path = $this->appLocator->getAppPath($appId);
485
-			}
486
-			$result = $this->verify(
487
-					$path . '/appinfo/signature.json',
488
-					$path,
489
-					$appId
490
-			);
491
-		} catch (\Exception $e) {
492
-			$result = [
493
-					'EXCEPTION' => [
494
-							'class' => \get_class($e),
495
-							'message' => $e->getMessage(),
496
-					],
497
-			];
498
-		}
499
-		$this->storeResults($appId, $result);
500
-
501
-		return $result;
502
-	}
503
-
504
-	/**
505
-	 * Verify the signature of core. Returns an array with the following content:
506
-	 * [
507
-	 * 	'FILE_MISSING' =>
508
-	 * 	[
509
-	 * 		'filename' => [
510
-	 * 			'expected' => 'expectedSHA512',
511
-	 * 			'current' => 'currentSHA512',
512
-	 * 		],
513
-	 * 	],
514
-	 * 	'EXTRA_FILE' =>
515
-	 * 	[
516
-	 * 		'filename' => [
517
-	 * 			'expected' => 'expectedSHA512',
518
-	 * 			'current' => 'currentSHA512',
519
-	 * 		],
520
-	 * 	],
521
-	 * 	'INVALID_HASH' =>
522
-	 * 	[
523
-	 * 		'filename' => [
524
-	 * 			'expected' => 'expectedSHA512',
525
-	 * 			'current' => 'currentSHA512',
526
-	 * 		],
527
-	 * 	],
528
-	 * ]
529
-	 *
530
-	 * Array may be empty in case no problems have been found.
531
-	 *
532
-	 * @return array
533
-	 */
534
-	public function verifyCoreSignature(): array {
535
-		try {
536
-			$result = $this->verify(
537
-					$this->environmentHelper->getServerRoot() . '/core/signature.json',
538
-					$this->environmentHelper->getServerRoot(),
539
-					'core'
540
-			);
541
-		} catch (\Exception $e) {
542
-			$result = [
543
-					'EXCEPTION' => [
544
-							'class' => \get_class($e),
545
-							'message' => $e->getMessage(),
546
-					],
547
-			];
548
-		}
549
-		$this->storeResults('core', $result);
550
-
551
-		return $result;
552
-	}
553
-
554
-	/**
555
-	 * Verify the core code of the instance as well as all applicable applications
556
-	 * and store the results.
557
-	 */
558
-	public function runInstanceVerification() {
559
-		$this->cleanResults();
560
-		$this->verifyCoreSignature();
561
-		$appIds = $this->appLocator->getAllApps();
562
-		foreach($appIds as $appId) {
563
-			// If an application is shipped a valid signature is required
564
-			$isShipped = $this->appManager->isShipped($appId);
565
-			$appNeedsToBeChecked = false;
566
-			if ($isShipped) {
567
-				$appNeedsToBeChecked = true;
568
-			} elseif ($this->fileAccessHelper->file_exists($this->appLocator->getAppPath($appId) . '/appinfo/signature.json')) {
569
-				// Otherwise only if the application explicitly ships a signature.json file
570
-				$appNeedsToBeChecked = true;
571
-			}
572
-
573
-			if($appNeedsToBeChecked) {
574
-				$this->verifyAppSignature($appId);
575
-			}
576
-		}
577
-	}
55
+    const CACHE_KEY = 'oc.integritycheck.checker';
56
+    /** @var EnvironmentHelper */
57
+    private $environmentHelper;
58
+    /** @var AppLocator */
59
+    private $appLocator;
60
+    /** @var FileAccessHelper */
61
+    private $fileAccessHelper;
62
+    /** @var IConfig */
63
+    private $config;
64
+    /** @var ICache */
65
+    private $cache;
66
+    /** @var IAppManager */
67
+    private $appManager;
68
+    /** @var ITempManager */
69
+    private $tempManager;
70
+
71
+    /**
72
+     * @param EnvironmentHelper $environmentHelper
73
+     * @param FileAccessHelper $fileAccessHelper
74
+     * @param AppLocator $appLocator
75
+     * @param IConfig $config
76
+     * @param ICacheFactory $cacheFactory
77
+     * @param IAppManager $appManager
78
+     * @param ITempManager $tempManager
79
+     */
80
+    public function __construct(EnvironmentHelper $environmentHelper,
81
+                                FileAccessHelper $fileAccessHelper,
82
+                                AppLocator $appLocator,
83
+                                IConfig $config = null,
84
+                                ICacheFactory $cacheFactory,
85
+                                IAppManager $appManager = null,
86
+                                ITempManager $tempManager) {
87
+        $this->environmentHelper = $environmentHelper;
88
+        $this->fileAccessHelper = $fileAccessHelper;
89
+        $this->appLocator = $appLocator;
90
+        $this->config = $config;
91
+        $this->cache = $cacheFactory->createDistributed(self::CACHE_KEY);
92
+        $this->appManager = $appManager;
93
+        $this->tempManager = $tempManager;
94
+    }
95
+
96
+    /**
97
+     * Whether code signing is enforced or not.
98
+     *
99
+     * @return bool
100
+     */
101
+    public function isCodeCheckEnforced(): bool {
102
+        $notSignedChannels = [ '', 'git'];
103
+        if (\in_array($this->environmentHelper->getChannel(), $notSignedChannels, true)) {
104
+            return false;
105
+        }
106
+
107
+        /**
108
+         * This config option is undocumented and supposed to be so, it's only
109
+         * applicable for very specific scenarios and we should not advertise it
110
+         * too prominent. So please do not add it to config.sample.php.
111
+         */
112
+        $isIntegrityCheckDisabled = false;
113
+        if ($this->config !== null) {
114
+            $isIntegrityCheckDisabled = $this->config->getSystemValue('integrity.check.disabled', false);
115
+        }
116
+        if ($isIntegrityCheckDisabled === true) {
117
+            return false;
118
+        }
119
+
120
+        return true;
121
+    }
122
+
123
+    /**
124
+     * Enumerates all files belonging to the folder. Sensible defaults are excluded.
125
+     *
126
+     * @param string $folderToIterate
127
+     * @param string $root
128
+     * @return \RecursiveIteratorIterator
129
+     * @throws \Exception
130
+     */
131
+    private function getFolderIterator(string $folderToIterate, string $root = ''): \RecursiveIteratorIterator {
132
+        $dirItr = new \RecursiveDirectoryIterator(
133
+            $folderToIterate,
134
+            \RecursiveDirectoryIterator::SKIP_DOTS
135
+        );
136
+        if($root === '') {
137
+            $root = \OC::$SERVERROOT;
138
+        }
139
+        $root = rtrim($root, '/');
140
+
141
+        $excludeGenericFilesIterator = new ExcludeFileByNameFilterIterator($dirItr);
142
+        $excludeFoldersIterator = new ExcludeFoldersByPathFilterIterator($excludeGenericFilesIterator, $root);
143
+
144
+        return new \RecursiveIteratorIterator(
145
+            $excludeFoldersIterator,
146
+            \RecursiveIteratorIterator::SELF_FIRST
147
+        );
148
+    }
149
+
150
+    /**
151
+     * Returns an array of ['filename' => 'SHA512-hash-of-file'] for all files found
152
+     * in the iterator.
153
+     *
154
+     * @param \RecursiveIteratorIterator $iterator
155
+     * @param string $path
156
+     * @return array Array of hashes.
157
+     */
158
+    private function generateHashes(\RecursiveIteratorIterator $iterator,
159
+                                    string $path): array {
160
+        $hashes = [];
161
+
162
+        $baseDirectoryLength = \strlen($path);
163
+        foreach($iterator as $filename => $data) {
164
+            /** @var \DirectoryIterator $data */
165
+            if($data->isDir()) {
166
+                continue;
167
+            }
168
+
169
+            $relativeFileName = substr($filename, $baseDirectoryLength);
170
+            $relativeFileName = ltrim($relativeFileName, '/');
171
+
172
+            // Exclude signature.json files in the appinfo and root folder
173
+            if($relativeFileName === 'appinfo/signature.json') {
174
+                continue;
175
+            }
176
+            // Exclude signature.json files in the appinfo and core folder
177
+            if($relativeFileName === 'core/signature.json') {
178
+                continue;
179
+            }
180
+
181
+            // The .htaccess file in the root folder of ownCloud can contain
182
+            // custom content after the installation due to the fact that dynamic
183
+            // content is written into it at installation time as well. This
184
+            // includes for example the 404 and 403 instructions.
185
+            // Thus we ignore everything below the first occurrence of
186
+            // "#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####" and have the
187
+            // hash generated based on this.
188
+            if($filename === $this->environmentHelper->getServerRoot() . '/.htaccess') {
189
+                $fileContent = file_get_contents($filename);
190
+                $explodedArray = explode('#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####', $fileContent);
191
+                if(\count($explodedArray) === 2) {
192
+                    $hashes[$relativeFileName] = hash('sha512', $explodedArray[0]);
193
+                    continue;
194
+                }
195
+            }
196
+
197
+            $hashes[$relativeFileName] = hash_file('sha512', $filename);
198
+        }
199
+
200
+        return $hashes;
201
+    }
202
+
203
+    /**
204
+     * Creates the signature data
205
+     *
206
+     * @param array $hashes
207
+     * @param X509 $certificate
208
+     * @param RSA $privateKey
209
+     * @return array
210
+     */
211
+    private function createSignatureData(array $hashes,
212
+                                            X509 $certificate,
213
+                                            RSA $privateKey): array {
214
+        ksort($hashes);
215
+
216
+        $privateKey->setSignatureMode(RSA::SIGNATURE_PSS);
217
+        $privateKey->setMGFHash('sha512');
218
+        // See https://tools.ietf.org/html/rfc3447#page-38
219
+        $privateKey->setSaltLength(0);
220
+        $signature = $privateKey->sign(json_encode($hashes));
221
+
222
+        return [
223
+                'hashes' => $hashes,
224
+                'signature' => base64_encode($signature),
225
+                'certificate' => $certificate->saveX509($certificate->currentCert),
226
+            ];
227
+    }
228
+
229
+    /**
230
+     * Write the signature of the app in the specified folder
231
+     *
232
+     * @param string $path
233
+     * @param X509 $certificate
234
+     * @param RSA $privateKey
235
+     * @throws \Exception
236
+     */
237
+    public function writeAppSignature($path,
238
+                                        X509 $certificate,
239
+                                        RSA $privateKey) {
240
+        $appInfoDir = $path . '/appinfo';
241
+        try {
242
+            $this->fileAccessHelper->assertDirectoryExists($appInfoDir);
243
+
244
+            $iterator = $this->getFolderIterator($path);
245
+            $hashes = $this->generateHashes($iterator, $path);
246
+            $signature = $this->createSignatureData($hashes, $certificate, $privateKey);
247
+                $this->fileAccessHelper->file_put_contents(
248
+                    $appInfoDir . '/signature.json',
249
+                json_encode($signature, JSON_PRETTY_PRINT)
250
+            );
251
+        } catch (\Exception $e){
252
+            if (!$this->fileAccessHelper->is_writable($appInfoDir)) {
253
+                throw new \Exception($appInfoDir . ' is not writable');
254
+            }
255
+            throw $e;
256
+        }
257
+    }
258
+
259
+    /**
260
+     * Write the signature of core
261
+     *
262
+     * @param X509 $certificate
263
+     * @param RSA $rsa
264
+     * @param string $path
265
+     * @throws \Exception
266
+     */
267
+    public function writeCoreSignature(X509 $certificate,
268
+                                        RSA $rsa,
269
+                                        $path) {
270
+        $coreDir = $path . '/core';
271
+        try {
272
+
273
+            $this->fileAccessHelper->assertDirectoryExists($coreDir);
274
+            $iterator = $this->getFolderIterator($path, $path);
275
+            $hashes = $this->generateHashes($iterator, $path);
276
+            $signatureData = $this->createSignatureData($hashes, $certificate, $rsa);
277
+            $this->fileAccessHelper->file_put_contents(
278
+                $coreDir . '/signature.json',
279
+                json_encode($signatureData, JSON_PRETTY_PRINT)
280
+            );
281
+        } catch (\Exception $e){
282
+            if (!$this->fileAccessHelper->is_writable($coreDir)) {
283
+                throw new \Exception($coreDir . ' is not writable');
284
+            }
285
+            throw $e;
286
+        }
287
+    }
288
+
289
+    /**
290
+     * Verifies the signature for the specified path.
291
+     *
292
+     * @param string $signaturePath
293
+     * @param string $basePath
294
+     * @param string $certificateCN
295
+     * @return array
296
+     * @throws InvalidSignatureException
297
+     * @throws \Exception
298
+     */
299
+    private function verify(string $signaturePath, string $basePath, string $certificateCN): array {
300
+        if(!$this->isCodeCheckEnforced()) {
301
+            return [];
302
+        }
303
+
304
+        $content = $this->fileAccessHelper->file_get_contents($signaturePath);
305
+        $signatureData = null;
306
+
307
+        if (\is_string($content)) {
308
+            $signatureData = json_decode($content, true);
309
+        }
310
+        if(!\is_array($signatureData)) {
311
+            throw new InvalidSignatureException('Signature data not found.');
312
+        }
313
+
314
+        $expectedHashes = $signatureData['hashes'];
315
+        ksort($expectedHashes);
316
+        $signature = base64_decode($signatureData['signature']);
317
+        $certificate = $signatureData['certificate'];
318
+
319
+        // Check if certificate is signed by Nextcloud Root Authority
320
+        $x509 = new \phpseclib\File\X509();
321
+        $rootCertificatePublicKey = $this->fileAccessHelper->file_get_contents($this->environmentHelper->getServerRoot().'/resources/codesigning/root.crt');
322
+        $x509->loadCA($rootCertificatePublicKey);
323
+        $x509->loadX509($certificate);
324
+        if(!$x509->validateSignature()) {
325
+            throw new InvalidSignatureException('Certificate is not valid.');
326
+        }
327
+        // Verify if certificate has proper CN. "core" CN is always trusted.
328
+        if($x509->getDN(X509::DN_OPENSSL)['CN'] !== $certificateCN && $x509->getDN(X509::DN_OPENSSL)['CN'] !== 'core') {
329
+            throw new InvalidSignatureException(
330
+                    sprintf('Certificate is not valid for required scope. (Requested: %s, current: CN=%s)', $certificateCN, $x509->getDN(true)['CN'])
331
+            );
332
+        }
333
+
334
+        // Check if the signature of the files is valid
335
+        $rsa = new \phpseclib\Crypt\RSA();
336
+        $rsa->loadKey($x509->currentCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']);
337
+        $rsa->setSignatureMode(RSA::SIGNATURE_PSS);
338
+        $rsa->setMGFHash('sha512');
339
+        // See https://tools.ietf.org/html/rfc3447#page-38
340
+        $rsa->setSaltLength(0);
341
+        if(!$rsa->verify(json_encode($expectedHashes), $signature)) {
342
+            throw new InvalidSignatureException('Signature could not get verified.');
343
+        }
344
+
345
+        // Fixes for the updater as shipped with ownCloud 9.0.x: The updater is
346
+        // replaced after the code integrity check is performed.
347
+        //
348
+        // Due to this reason we exclude the whole updater/ folder from the code
349
+        // integrity check.
350
+        if($basePath === $this->environmentHelper->getServerRoot()) {
351
+            foreach($expectedHashes as $fileName => $hash) {
352
+                if(strpos($fileName, 'updater/') === 0) {
353
+                    unset($expectedHashes[$fileName]);
354
+                }
355
+            }
356
+        }
357
+
358
+        // Compare the list of files which are not identical
359
+        $currentInstanceHashes = $this->generateHashes($this->getFolderIterator($basePath), $basePath);
360
+        $differencesA = array_diff($expectedHashes, $currentInstanceHashes);
361
+        $differencesB = array_diff($currentInstanceHashes, $expectedHashes);
362
+        $differences = array_unique(array_merge($differencesA, $differencesB));
363
+        $differenceArray = [];
364
+        foreach($differences as $filename => $hash) {
365
+            // Check if file should not exist in the new signature table
366
+            if(!array_key_exists($filename, $expectedHashes)) {
367
+                $differenceArray['EXTRA_FILE'][$filename]['expected'] = '';
368
+                $differenceArray['EXTRA_FILE'][$filename]['current'] = $hash;
369
+                continue;
370
+            }
371
+
372
+            // Check if file is missing
373
+            if(!array_key_exists($filename, $currentInstanceHashes)) {
374
+                $differenceArray['FILE_MISSING'][$filename]['expected'] = $expectedHashes[$filename];
375
+                $differenceArray['FILE_MISSING'][$filename]['current'] = '';
376
+                continue;
377
+            }
378
+
379
+            // Check if hash does mismatch
380
+            if($expectedHashes[$filename] !== $currentInstanceHashes[$filename]) {
381
+                $differenceArray['INVALID_HASH'][$filename]['expected'] = $expectedHashes[$filename];
382
+                $differenceArray['INVALID_HASH'][$filename]['current'] = $currentInstanceHashes[$filename];
383
+                continue;
384
+            }
385
+
386
+            // Should never happen.
387
+            throw new \Exception('Invalid behaviour in file hash comparison experienced. Please report this error to the developers.');
388
+        }
389
+
390
+        return $differenceArray;
391
+    }
392
+
393
+    /**
394
+     * Whether the code integrity check has passed successful or not
395
+     *
396
+     * @return bool
397
+     */
398
+    public function hasPassedCheck(): bool {
399
+        $results = $this->getResults();
400
+        if(empty($results)) {
401
+            return true;
402
+        }
403
+
404
+        return false;
405
+    }
406
+
407
+    /**
408
+     * @return array
409
+     */
410
+    public function getResults(): array {
411
+        $cachedResults = $this->cache->get(self::CACHE_KEY);
412
+        if(!\is_null($cachedResults)) {
413
+            return json_decode($cachedResults, true);
414
+        }
415
+
416
+        if ($this->config !== null) {
417
+            return json_decode($this->config->getAppValue('core', self::CACHE_KEY, '{}'), true);
418
+        }
419
+        return [];
420
+    }
421
+
422
+    /**
423
+     * Stores the results in the app config as well as cache
424
+     *
425
+     * @param string $scope
426
+     * @param array $result
427
+     */
428
+    private function storeResults(string $scope, array $result) {
429
+        $resultArray = $this->getResults();
430
+        unset($resultArray[$scope]);
431
+        if(!empty($result)) {
432
+            $resultArray[$scope] = $result;
433
+        }
434
+        if ($this->config !== null) {
435
+            $this->config->setAppValue('core', self::CACHE_KEY, json_encode($resultArray));
436
+        }
437
+        $this->cache->set(self::CACHE_KEY, json_encode($resultArray));
438
+    }
439
+
440
+    /**
441
+     *
442
+     * Clean previous results for a proper rescanning. Otherwise
443
+     */
444
+    private function cleanResults() {
445
+        $this->config->deleteAppValue('core', self::CACHE_KEY);
446
+        $this->cache->remove(self::CACHE_KEY);
447
+    }
448
+
449
+    /**
450
+     * Verify the signature of $appId. Returns an array with the following content:
451
+     * [
452
+     * 	'FILE_MISSING' =>
453
+     * 	[
454
+     * 		'filename' => [
455
+     * 			'expected' => 'expectedSHA512',
456
+     * 			'current' => 'currentSHA512',
457
+     * 		],
458
+     * 	],
459
+     * 	'EXTRA_FILE' =>
460
+     * 	[
461
+     * 		'filename' => [
462
+     * 			'expected' => 'expectedSHA512',
463
+     * 			'current' => 'currentSHA512',
464
+     * 		],
465
+     * 	],
466
+     * 	'INVALID_HASH' =>
467
+     * 	[
468
+     * 		'filename' => [
469
+     * 			'expected' => 'expectedSHA512',
470
+     * 			'current' => 'currentSHA512',
471
+     * 		],
472
+     * 	],
473
+     * ]
474
+     *
475
+     * Array may be empty in case no problems have been found.
476
+     *
477
+     * @param string $appId
478
+     * @param string $path Optional path. If none is given it will be guessed.
479
+     * @return array
480
+     */
481
+    public function verifyAppSignature(string $appId, string $path = ''): array {
482
+        try {
483
+            if($path === '') {
484
+                $path = $this->appLocator->getAppPath($appId);
485
+            }
486
+            $result = $this->verify(
487
+                    $path . '/appinfo/signature.json',
488
+                    $path,
489
+                    $appId
490
+            );
491
+        } catch (\Exception $e) {
492
+            $result = [
493
+                    'EXCEPTION' => [
494
+                            'class' => \get_class($e),
495
+                            'message' => $e->getMessage(),
496
+                    ],
497
+            ];
498
+        }
499
+        $this->storeResults($appId, $result);
500
+
501
+        return $result;
502
+    }
503
+
504
+    /**
505
+     * Verify the signature of core. Returns an array with the following content:
506
+     * [
507
+     * 	'FILE_MISSING' =>
508
+     * 	[
509
+     * 		'filename' => [
510
+     * 			'expected' => 'expectedSHA512',
511
+     * 			'current' => 'currentSHA512',
512
+     * 		],
513
+     * 	],
514
+     * 	'EXTRA_FILE' =>
515
+     * 	[
516
+     * 		'filename' => [
517
+     * 			'expected' => 'expectedSHA512',
518
+     * 			'current' => 'currentSHA512',
519
+     * 		],
520
+     * 	],
521
+     * 	'INVALID_HASH' =>
522
+     * 	[
523
+     * 		'filename' => [
524
+     * 			'expected' => 'expectedSHA512',
525
+     * 			'current' => 'currentSHA512',
526
+     * 		],
527
+     * 	],
528
+     * ]
529
+     *
530
+     * Array may be empty in case no problems have been found.
531
+     *
532
+     * @return array
533
+     */
534
+    public function verifyCoreSignature(): array {
535
+        try {
536
+            $result = $this->verify(
537
+                    $this->environmentHelper->getServerRoot() . '/core/signature.json',
538
+                    $this->environmentHelper->getServerRoot(),
539
+                    'core'
540
+            );
541
+        } catch (\Exception $e) {
542
+            $result = [
543
+                    'EXCEPTION' => [
544
+                            'class' => \get_class($e),
545
+                            'message' => $e->getMessage(),
546
+                    ],
547
+            ];
548
+        }
549
+        $this->storeResults('core', $result);
550
+
551
+        return $result;
552
+    }
553
+
554
+    /**
555
+     * Verify the core code of the instance as well as all applicable applications
556
+     * and store the results.
557
+     */
558
+    public function runInstanceVerification() {
559
+        $this->cleanResults();
560
+        $this->verifyCoreSignature();
561
+        $appIds = $this->appLocator->getAllApps();
562
+        foreach($appIds as $appId) {
563
+            // If an application is shipped a valid signature is required
564
+            $isShipped = $this->appManager->isShipped($appId);
565
+            $appNeedsToBeChecked = false;
566
+            if ($isShipped) {
567
+                $appNeedsToBeChecked = true;
568
+            } elseif ($this->fileAccessHelper->file_exists($this->appLocator->getAppPath($appId) . '/appinfo/signature.json')) {
569
+                // Otherwise only if the application explicitly ships a signature.json file
570
+                $appNeedsToBeChecked = true;
571
+            }
572
+
573
+            if($appNeedsToBeChecked) {
574
+                $this->verifyAppSignature($appId);
575
+            }
576
+        }
577
+    }
578 578
 }
Please login to merge, or discard this patch.
Spacing   +37 added lines, -37 removed lines patch added patch discarded remove patch
@@ -99,7 +99,7 @@  discard block
 block discarded – undo
99 99
 	 * @return bool
100 100
 	 */
101 101
 	public function isCodeCheckEnforced(): bool {
102
-		$notSignedChannels = [ '', 'git'];
102
+		$notSignedChannels = ['', 'git'];
103 103
 		if (\in_array($this->environmentHelper->getChannel(), $notSignedChannels, true)) {
104 104
 			return false;
105 105
 		}
@@ -133,7 +133,7 @@  discard block
 block discarded – undo
133 133
 			$folderToIterate,
134 134
 			\RecursiveDirectoryIterator::SKIP_DOTS
135 135
 		);
136
-		if($root === '') {
136
+		if ($root === '') {
137 137
 			$root = \OC::$SERVERROOT;
138 138
 		}
139 139
 		$root = rtrim($root, '/');
@@ -160,9 +160,9 @@  discard block
 block discarded – undo
160 160
 		$hashes = [];
161 161
 
162 162
 		$baseDirectoryLength = \strlen($path);
163
-		foreach($iterator as $filename => $data) {
163
+		foreach ($iterator as $filename => $data) {
164 164
 			/** @var \DirectoryIterator $data */
165
-			if($data->isDir()) {
165
+			if ($data->isDir()) {
166 166
 				continue;
167 167
 			}
168 168
 
@@ -170,11 +170,11 @@  discard block
 block discarded – undo
170 170
 			$relativeFileName = ltrim($relativeFileName, '/');
171 171
 
172 172
 			// Exclude signature.json files in the appinfo and root folder
173
-			if($relativeFileName === 'appinfo/signature.json') {
173
+			if ($relativeFileName === 'appinfo/signature.json') {
174 174
 				continue;
175 175
 			}
176 176
 			// Exclude signature.json files in the appinfo and core folder
177
-			if($relativeFileName === 'core/signature.json') {
177
+			if ($relativeFileName === 'core/signature.json') {
178 178
 				continue;
179 179
 			}
180 180
 
@@ -185,10 +185,10 @@  discard block
 block discarded – undo
185 185
 			// Thus we ignore everything below the first occurrence of
186 186
 			// "#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####" and have the
187 187
 			// hash generated based on this.
188
-			if($filename === $this->environmentHelper->getServerRoot() . '/.htaccess') {
188
+			if ($filename === $this->environmentHelper->getServerRoot().'/.htaccess') {
189 189
 				$fileContent = file_get_contents($filename);
190 190
 				$explodedArray = explode('#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####', $fileContent);
191
-				if(\count($explodedArray) === 2) {
191
+				if (\count($explodedArray) === 2) {
192 192
 					$hashes[$relativeFileName] = hash('sha512', $explodedArray[0]);
193 193
 					continue;
194 194
 				}
@@ -237,7 +237,7 @@  discard block
 block discarded – undo
237 237
 	public function writeAppSignature($path,
238 238
 									  X509 $certificate,
239 239
 									  RSA $privateKey) {
240
-		$appInfoDir = $path . '/appinfo';
240
+		$appInfoDir = $path.'/appinfo';
241 241
 		try {
242 242
 			$this->fileAccessHelper->assertDirectoryExists($appInfoDir);
243 243
 
@@ -245,12 +245,12 @@  discard block
 block discarded – undo
245 245
 			$hashes = $this->generateHashes($iterator, $path);
246 246
 			$signature = $this->createSignatureData($hashes, $certificate, $privateKey);
247 247
 				$this->fileAccessHelper->file_put_contents(
248
-					$appInfoDir . '/signature.json',
248
+					$appInfoDir.'/signature.json',
249 249
 				json_encode($signature, JSON_PRETTY_PRINT)
250 250
 			);
251
-		} catch (\Exception $e){
251
+		} catch (\Exception $e) {
252 252
 			if (!$this->fileAccessHelper->is_writable($appInfoDir)) {
253
-				throw new \Exception($appInfoDir . ' is not writable');
253
+				throw new \Exception($appInfoDir.' is not writable');
254 254
 			}
255 255
 			throw $e;
256 256
 		}
@@ -267,7 +267,7 @@  discard block
 block discarded – undo
267 267
 	public function writeCoreSignature(X509 $certificate,
268 268
 									   RSA $rsa,
269 269
 									   $path) {
270
-		$coreDir = $path . '/core';
270
+		$coreDir = $path.'/core';
271 271
 		try {
272 272
 
273 273
 			$this->fileAccessHelper->assertDirectoryExists($coreDir);
@@ -275,12 +275,12 @@  discard block
 block discarded – undo
275 275
 			$hashes = $this->generateHashes($iterator, $path);
276 276
 			$signatureData = $this->createSignatureData($hashes, $certificate, $rsa);
277 277
 			$this->fileAccessHelper->file_put_contents(
278
-				$coreDir . '/signature.json',
278
+				$coreDir.'/signature.json',
279 279
 				json_encode($signatureData, JSON_PRETTY_PRINT)
280 280
 			);
281
-		} catch (\Exception $e){
281
+		} catch (\Exception $e) {
282 282
 			if (!$this->fileAccessHelper->is_writable($coreDir)) {
283
-				throw new \Exception($coreDir . ' is not writable');
283
+				throw new \Exception($coreDir.' is not writable');
284 284
 			}
285 285
 			throw $e;
286 286
 		}
@@ -297,7 +297,7 @@  discard block
 block discarded – undo
297 297
 	 * @throws \Exception
298 298
 	 */
299 299
 	private function verify(string $signaturePath, string $basePath, string $certificateCN): array {
300
-		if(!$this->isCodeCheckEnforced()) {
300
+		if (!$this->isCodeCheckEnforced()) {
301 301
 			return [];
302 302
 		}
303 303
 
@@ -307,7 +307,7 @@  discard block
 block discarded – undo
307 307
 		if (\is_string($content)) {
308 308
 			$signatureData = json_decode($content, true);
309 309
 		}
310
-		if(!\is_array($signatureData)) {
310
+		if (!\is_array($signatureData)) {
311 311
 			throw new InvalidSignatureException('Signature data not found.');
312 312
 		}
313 313
 
@@ -321,11 +321,11 @@  discard block
 block discarded – undo
321 321
 		$rootCertificatePublicKey = $this->fileAccessHelper->file_get_contents($this->environmentHelper->getServerRoot().'/resources/codesigning/root.crt');
322 322
 		$x509->loadCA($rootCertificatePublicKey);
323 323
 		$x509->loadX509($certificate);
324
-		if(!$x509->validateSignature()) {
324
+		if (!$x509->validateSignature()) {
325 325
 			throw new InvalidSignatureException('Certificate is not valid.');
326 326
 		}
327 327
 		// Verify if certificate has proper CN. "core" CN is always trusted.
328
-		if($x509->getDN(X509::DN_OPENSSL)['CN'] !== $certificateCN && $x509->getDN(X509::DN_OPENSSL)['CN'] !== 'core') {
328
+		if ($x509->getDN(X509::DN_OPENSSL)['CN'] !== $certificateCN && $x509->getDN(X509::DN_OPENSSL)['CN'] !== 'core') {
329 329
 			throw new InvalidSignatureException(
330 330
 					sprintf('Certificate is not valid for required scope. (Requested: %s, current: CN=%s)', $certificateCN, $x509->getDN(true)['CN'])
331 331
 			);
@@ -338,7 +338,7 @@  discard block
 block discarded – undo
338 338
 		$rsa->setMGFHash('sha512');
339 339
 		// See https://tools.ietf.org/html/rfc3447#page-38
340 340
 		$rsa->setSaltLength(0);
341
-		if(!$rsa->verify(json_encode($expectedHashes), $signature)) {
341
+		if (!$rsa->verify(json_encode($expectedHashes), $signature)) {
342 342
 			throw new InvalidSignatureException('Signature could not get verified.');
343 343
 		}
344 344
 
@@ -347,9 +347,9 @@  discard block
 block discarded – undo
347 347
 		//
348 348
 		// Due to this reason we exclude the whole updater/ folder from the code
349 349
 		// integrity check.
350
-		if($basePath === $this->environmentHelper->getServerRoot()) {
351
-			foreach($expectedHashes as $fileName => $hash) {
352
-				if(strpos($fileName, 'updater/') === 0) {
350
+		if ($basePath === $this->environmentHelper->getServerRoot()) {
351
+			foreach ($expectedHashes as $fileName => $hash) {
352
+				if (strpos($fileName, 'updater/') === 0) {
353 353
 					unset($expectedHashes[$fileName]);
354 354
 				}
355 355
 			}
@@ -361,23 +361,23 @@  discard block
 block discarded – undo
361 361
 		$differencesB = array_diff($currentInstanceHashes, $expectedHashes);
362 362
 		$differences = array_unique(array_merge($differencesA, $differencesB));
363 363
 		$differenceArray = [];
364
-		foreach($differences as $filename => $hash) {
364
+		foreach ($differences as $filename => $hash) {
365 365
 			// Check if file should not exist in the new signature table
366
-			if(!array_key_exists($filename, $expectedHashes)) {
366
+			if (!array_key_exists($filename, $expectedHashes)) {
367 367
 				$differenceArray['EXTRA_FILE'][$filename]['expected'] = '';
368 368
 				$differenceArray['EXTRA_FILE'][$filename]['current'] = $hash;
369 369
 				continue;
370 370
 			}
371 371
 
372 372
 			// Check if file is missing
373
-			if(!array_key_exists($filename, $currentInstanceHashes)) {
373
+			if (!array_key_exists($filename, $currentInstanceHashes)) {
374 374
 				$differenceArray['FILE_MISSING'][$filename]['expected'] = $expectedHashes[$filename];
375 375
 				$differenceArray['FILE_MISSING'][$filename]['current'] = '';
376 376
 				continue;
377 377
 			}
378 378
 
379 379
 			// Check if hash does mismatch
380
-			if($expectedHashes[$filename] !== $currentInstanceHashes[$filename]) {
380
+			if ($expectedHashes[$filename] !== $currentInstanceHashes[$filename]) {
381 381
 				$differenceArray['INVALID_HASH'][$filename]['expected'] = $expectedHashes[$filename];
382 382
 				$differenceArray['INVALID_HASH'][$filename]['current'] = $currentInstanceHashes[$filename];
383 383
 				continue;
@@ -397,7 +397,7 @@  discard block
 block discarded – undo
397 397
 	 */
398 398
 	public function hasPassedCheck(): bool {
399 399
 		$results = $this->getResults();
400
-		if(empty($results)) {
400
+		if (empty($results)) {
401 401
 			return true;
402 402
 		}
403 403
 
@@ -409,7 +409,7 @@  discard block
 block discarded – undo
409 409
 	 */
410 410
 	public function getResults(): array {
411 411
 		$cachedResults = $this->cache->get(self::CACHE_KEY);
412
-		if(!\is_null($cachedResults)) {
412
+		if (!\is_null($cachedResults)) {
413 413
 			return json_decode($cachedResults, true);
414 414
 		}
415 415
 
@@ -428,7 +428,7 @@  discard block
 block discarded – undo
428 428
 	private function storeResults(string $scope, array $result) {
429 429
 		$resultArray = $this->getResults();
430 430
 		unset($resultArray[$scope]);
431
-		if(!empty($result)) {
431
+		if (!empty($result)) {
432 432
 			$resultArray[$scope] = $result;
433 433
 		}
434 434
 		if ($this->config !== null) {
@@ -480,11 +480,11 @@  discard block
 block discarded – undo
480 480
 	 */
481 481
 	public function verifyAppSignature(string $appId, string $path = ''): array {
482 482
 		try {
483
-			if($path === '') {
483
+			if ($path === '') {
484 484
 				$path = $this->appLocator->getAppPath($appId);
485 485
 			}
486 486
 			$result = $this->verify(
487
-					$path . '/appinfo/signature.json',
487
+					$path.'/appinfo/signature.json',
488 488
 					$path,
489 489
 					$appId
490 490
 			);
@@ -534,7 +534,7 @@  discard block
 block discarded – undo
534 534
 	public function verifyCoreSignature(): array {
535 535
 		try {
536 536
 			$result = $this->verify(
537
-					$this->environmentHelper->getServerRoot() . '/core/signature.json',
537
+					$this->environmentHelper->getServerRoot().'/core/signature.json',
538 538
 					$this->environmentHelper->getServerRoot(),
539 539
 					'core'
540 540
 			);
@@ -559,18 +559,18 @@  discard block
 block discarded – undo
559 559
 		$this->cleanResults();
560 560
 		$this->verifyCoreSignature();
561 561
 		$appIds = $this->appLocator->getAllApps();
562
-		foreach($appIds as $appId) {
562
+		foreach ($appIds as $appId) {
563 563
 			// If an application is shipped a valid signature is required
564 564
 			$isShipped = $this->appManager->isShipped($appId);
565 565
 			$appNeedsToBeChecked = false;
566 566
 			if ($isShipped) {
567 567
 				$appNeedsToBeChecked = true;
568
-			} elseif ($this->fileAccessHelper->file_exists($this->appLocator->getAppPath($appId) . '/appinfo/signature.json')) {
568
+			} elseif ($this->fileAccessHelper->file_exists($this->appLocator->getAppPath($appId).'/appinfo/signature.json')) {
569 569
 				// Otherwise only if the application explicitly ships a signature.json file
570 570
 				$appNeedsToBeChecked = true;
571 571
 			}
572 572
 
573
-			if($appNeedsToBeChecked) {
573
+			if ($appNeedsToBeChecked) {
574 574
 				$this->verifyAppSignature($appId);
575 575
 			}
576 576
 		}
Please login to merge, or discard this patch.