Completed
Push — master ( a464c3...d1314b )
by
unknown
30:12 queued 10s
created
apps/dav/lib/Connector/Sabre/ZipFolderPlugin.php 1 patch
Indentation   +163 added lines, -163 removed lines patch added patch discarded remove patch
@@ -33,167 +33,167 @@
 block discarded – undo
33 33
  */
34 34
 class ZipFolderPlugin extends ServerPlugin {
35 35
 
36
-	/**
37
-	 * Reference to main server object
38
-	 */
39
-	private ?Server $server = null;
40
-
41
-	public function __construct(
42
-		private Tree $tree,
43
-		private LoggerInterface $logger,
44
-		private IEventDispatcher $eventDispatcher,
45
-		private IDateTimeZone $timezoneFactory,
46
-	) {
47
-	}
48
-
49
-	/**
50
-	 * This initializes the plugin.
51
-	 *
52
-	 * This function is called by \Sabre\DAV\Server, after
53
-	 * addPlugin is called.
54
-	 *
55
-	 * This method should set up the required event subscriptions.
56
-	 */
57
-	public function initialize(Server $server): void {
58
-		$this->server = $server;
59
-		$this->server->on('method:GET', $this->handleDownload(...), 100);
60
-		// low priority to give any other afterMethod:* a chance to fire before we cancel everything
61
-		$this->server->on('afterMethod:GET', $this->afterDownload(...), 999);
62
-	}
63
-
64
-	/**
65
-	 * Adding a node to the archive streamer.
66
-	 * This will recursively add new nodes to the stream if the node is a directory.
67
-	 */
68
-	protected function streamNode(Streamer $streamer, NcNode $node, string $rootPath): void {
69
-		// Remove the root path from the filename to make it relative to the requested folder
70
-		$filename = str_replace($rootPath, '', $node->getPath());
71
-
72
-		$mtime = $node->getMTime();
73
-		if ($node instanceof NcFile) {
74
-			$resource = $node->fopen('rb');
75
-			if ($resource === false) {
76
-				$this->logger->info('Cannot read file for zip stream', ['filePath' => $node->getPath()]);
77
-				throw new \Sabre\DAV\Exception\ServiceUnavailable('Requested file can currently not be accessed.');
78
-			}
79
-			$streamer->addFileFromStream($resource, $filename, $node->getSize(), $mtime);
80
-		} elseif ($node instanceof NcFolder) {
81
-			$streamer->addEmptyDir($filename, $mtime);
82
-			$content = $node->getDirectoryListing();
83
-			foreach ($content as $subNode) {
84
-				$this->streamNode($streamer, $subNode, $rootPath);
85
-			}
86
-		}
87
-	}
88
-
89
-	/**
90
-	 * Download a folder as an archive.
91
-	 * It is possible to filter / limit the files that should be downloaded,
92
-	 * either by passing (multiple) `X-NC-Files: the-file` headers
93
-	 * or by setting a `files=JSON_ARRAY_OF_FILES` URL query.
94
-	 *
95
-	 * @return false|null
96
-	 */
97
-	public function handleDownload(Request $request, Response $response): ?bool {
98
-		$node = $this->tree->getNodeForPath($request->getPath());
99
-		if (!($node instanceof Directory)) {
100
-			// only handle directories
101
-			return null;
102
-		}
103
-
104
-		$query = $request->getQueryParameters();
105
-
106
-		// Get accept header - or if set overwrite with accept GET-param
107
-		$accept = $request->getHeaderAsArray('Accept');
108
-		$acceptParam = $query['accept'] ?? '';
109
-		if ($acceptParam !== '') {
110
-			$accept = array_map(fn (string $name) => strtolower(trim($name)), explode(',', $acceptParam));
111
-		}
112
-		$zipRequest = !empty(array_intersect(['application/zip', 'zip'], $accept));
113
-		$tarRequest = !empty(array_intersect(['application/x-tar', 'tar'], $accept));
114
-		if (!$zipRequest && !$tarRequest) {
115
-			// does not accept zip or tar stream
116
-			return null;
117
-		}
118
-
119
-		$files = $request->getHeaderAsArray('X-NC-Files');
120
-		$filesParam = $query['files'] ?? '';
121
-		// The preferred way would be headers, but this is not possible for simple browser requests ("links")
122
-		// so we also need to support GET parameters
123
-		if ($filesParam !== '') {
124
-			$files = json_decode($filesParam);
125
-			if (!is_array($files)) {
126
-				$files = [$files];
127
-			}
128
-
129
-			foreach ($files as $file) {
130
-				if (!is_string($file)) {
131
-					// we log this as this means either we - or an app - have a bug somewhere or a user is trying invalid things
132
-					$this->logger->notice('Invalid files filter parameter for ZipFolderPlugin', ['filter' => $filesParam]);
133
-					// no valid parameter so continue with Sabre behavior
134
-					return null;
135
-				}
136
-			}
137
-		}
138
-
139
-		$folder = $node->getNode();
140
-		$event = new BeforeZipCreatedEvent($folder, $files);
141
-		$this->eventDispatcher->dispatchTyped($event);
142
-		if ((!$event->isSuccessful()) || $event->getErrorMessage() !== null) {
143
-			$errorMessage = $event->getErrorMessage();
144
-			if ($errorMessage === null) {
145
-				// Not allowed to download but also no explaining error
146
-				// so we abort the ZIP creation and fall back to Sabre default behavior.
147
-				return null;
148
-			}
149
-			// Downloading was denied by an app
150
-			throw new Forbidden($errorMessage);
151
-		}
152
-
153
-		$content = empty($files) ? $folder->getDirectoryListing() : [];
154
-		foreach ($files as $path) {
155
-			$child = $node->getChild($path);
156
-			assert($child instanceof Node);
157
-			$content[] = $child->getNode();
158
-		}
159
-
160
-		$archiveName = $folder->getName();
161
-		if (count(explode('/', trim($folder->getPath(), '/'), 3)) === 2) {
162
-			// this is a download of the root folder
163
-			$archiveName = 'download';
164
-		}
165
-
166
-		$rootPath = $folder->getPath();
167
-		if (empty($files)) {
168
-			// We download the full folder so keep it in the tree
169
-			$rootPath = dirname($folder->getPath());
170
-		}
171
-
172
-		$streamer = new Streamer($tarRequest, -1, count($content), $this->timezoneFactory);
173
-		$streamer->sendHeaders($archiveName);
174
-		// For full folder downloads we also add the folder itself to the archive
175
-		if (empty($files)) {
176
-			$streamer->addEmptyDir($archiveName);
177
-		}
178
-		foreach ($content as $node) {
179
-			$this->streamNode($streamer, $node, $rootPath);
180
-		}
181
-		$streamer->finalize();
182
-		return false;
183
-	}
184
-
185
-	/**
186
-	 * Tell sabre/dav not to trigger it's own response sending logic as the handleDownload will have already send the response
187
-	 *
188
-	 * @return false|null
189
-	 */
190
-	public function afterDownload(Request $request, Response $response): ?bool {
191
-		$node = $this->tree->getNodeForPath($request->getPath());
192
-		if (!($node instanceof Directory)) {
193
-			// only handle directories
194
-			return null;
195
-		} else {
196
-			return false;
197
-		}
198
-	}
36
+    /**
37
+     * Reference to main server object
38
+     */
39
+    private ?Server $server = null;
40
+
41
+    public function __construct(
42
+        private Tree $tree,
43
+        private LoggerInterface $logger,
44
+        private IEventDispatcher $eventDispatcher,
45
+        private IDateTimeZone $timezoneFactory,
46
+    ) {
47
+    }
48
+
49
+    /**
50
+     * This initializes the plugin.
51
+     *
52
+     * This function is called by \Sabre\DAV\Server, after
53
+     * addPlugin is called.
54
+     *
55
+     * This method should set up the required event subscriptions.
56
+     */
57
+    public function initialize(Server $server): void {
58
+        $this->server = $server;
59
+        $this->server->on('method:GET', $this->handleDownload(...), 100);
60
+        // low priority to give any other afterMethod:* a chance to fire before we cancel everything
61
+        $this->server->on('afterMethod:GET', $this->afterDownload(...), 999);
62
+    }
63
+
64
+    /**
65
+     * Adding a node to the archive streamer.
66
+     * This will recursively add new nodes to the stream if the node is a directory.
67
+     */
68
+    protected function streamNode(Streamer $streamer, NcNode $node, string $rootPath): void {
69
+        // Remove the root path from the filename to make it relative to the requested folder
70
+        $filename = str_replace($rootPath, '', $node->getPath());
71
+
72
+        $mtime = $node->getMTime();
73
+        if ($node instanceof NcFile) {
74
+            $resource = $node->fopen('rb');
75
+            if ($resource === false) {
76
+                $this->logger->info('Cannot read file for zip stream', ['filePath' => $node->getPath()]);
77
+                throw new \Sabre\DAV\Exception\ServiceUnavailable('Requested file can currently not be accessed.');
78
+            }
79
+            $streamer->addFileFromStream($resource, $filename, $node->getSize(), $mtime);
80
+        } elseif ($node instanceof NcFolder) {
81
+            $streamer->addEmptyDir($filename, $mtime);
82
+            $content = $node->getDirectoryListing();
83
+            foreach ($content as $subNode) {
84
+                $this->streamNode($streamer, $subNode, $rootPath);
85
+            }
86
+        }
87
+    }
88
+
89
+    /**
90
+     * Download a folder as an archive.
91
+     * It is possible to filter / limit the files that should be downloaded,
92
+     * either by passing (multiple) `X-NC-Files: the-file` headers
93
+     * or by setting a `files=JSON_ARRAY_OF_FILES` URL query.
94
+     *
95
+     * @return false|null
96
+     */
97
+    public function handleDownload(Request $request, Response $response): ?bool {
98
+        $node = $this->tree->getNodeForPath($request->getPath());
99
+        if (!($node instanceof Directory)) {
100
+            // only handle directories
101
+            return null;
102
+        }
103
+
104
+        $query = $request->getQueryParameters();
105
+
106
+        // Get accept header - or if set overwrite with accept GET-param
107
+        $accept = $request->getHeaderAsArray('Accept');
108
+        $acceptParam = $query['accept'] ?? '';
109
+        if ($acceptParam !== '') {
110
+            $accept = array_map(fn (string $name) => strtolower(trim($name)), explode(',', $acceptParam));
111
+        }
112
+        $zipRequest = !empty(array_intersect(['application/zip', 'zip'], $accept));
113
+        $tarRequest = !empty(array_intersect(['application/x-tar', 'tar'], $accept));
114
+        if (!$zipRequest && !$tarRequest) {
115
+            // does not accept zip or tar stream
116
+            return null;
117
+        }
118
+
119
+        $files = $request->getHeaderAsArray('X-NC-Files');
120
+        $filesParam = $query['files'] ?? '';
121
+        // The preferred way would be headers, but this is not possible for simple browser requests ("links")
122
+        // so we also need to support GET parameters
123
+        if ($filesParam !== '') {
124
+            $files = json_decode($filesParam);
125
+            if (!is_array($files)) {
126
+                $files = [$files];
127
+            }
128
+
129
+            foreach ($files as $file) {
130
+                if (!is_string($file)) {
131
+                    // we log this as this means either we - or an app - have a bug somewhere or a user is trying invalid things
132
+                    $this->logger->notice('Invalid files filter parameter for ZipFolderPlugin', ['filter' => $filesParam]);
133
+                    // no valid parameter so continue with Sabre behavior
134
+                    return null;
135
+                }
136
+            }
137
+        }
138
+
139
+        $folder = $node->getNode();
140
+        $event = new BeforeZipCreatedEvent($folder, $files);
141
+        $this->eventDispatcher->dispatchTyped($event);
142
+        if ((!$event->isSuccessful()) || $event->getErrorMessage() !== null) {
143
+            $errorMessage = $event->getErrorMessage();
144
+            if ($errorMessage === null) {
145
+                // Not allowed to download but also no explaining error
146
+                // so we abort the ZIP creation and fall back to Sabre default behavior.
147
+                return null;
148
+            }
149
+            // Downloading was denied by an app
150
+            throw new Forbidden($errorMessage);
151
+        }
152
+
153
+        $content = empty($files) ? $folder->getDirectoryListing() : [];
154
+        foreach ($files as $path) {
155
+            $child = $node->getChild($path);
156
+            assert($child instanceof Node);
157
+            $content[] = $child->getNode();
158
+        }
159
+
160
+        $archiveName = $folder->getName();
161
+        if (count(explode('/', trim($folder->getPath(), '/'), 3)) === 2) {
162
+            // this is a download of the root folder
163
+            $archiveName = 'download';
164
+        }
165
+
166
+        $rootPath = $folder->getPath();
167
+        if (empty($files)) {
168
+            // We download the full folder so keep it in the tree
169
+            $rootPath = dirname($folder->getPath());
170
+        }
171
+
172
+        $streamer = new Streamer($tarRequest, -1, count($content), $this->timezoneFactory);
173
+        $streamer->sendHeaders($archiveName);
174
+        // For full folder downloads we also add the folder itself to the archive
175
+        if (empty($files)) {
176
+            $streamer->addEmptyDir($archiveName);
177
+        }
178
+        foreach ($content as $node) {
179
+            $this->streamNode($streamer, $node, $rootPath);
180
+        }
181
+        $streamer->finalize();
182
+        return false;
183
+    }
184
+
185
+    /**
186
+     * Tell sabre/dav not to trigger it's own response sending logic as the handleDownload will have already send the response
187
+     *
188
+     * @return false|null
189
+     */
190
+    public function afterDownload(Request $request, Response $response): ?bool {
191
+        $node = $this->tree->getNodeForPath($request->getPath());
192
+        if (!($node instanceof Directory)) {
193
+            // only handle directories
194
+            return null;
195
+        } else {
196
+            return false;
197
+        }
198
+    }
199 199
 }
Please login to merge, or discard this patch.