PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.11
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.11
1.1.11 1.1.10 1.1.9 1.1.8 1.1.7 1.1.6 1.1.5 1.1.4 1.1.3 1.1.2 1.1.1 1.1.0 1.0.1 1.0.0 0.9.8 0.9.7 0.9.6 0.9.4 0.9.5 0.9.3 0.9.2 0.9.1 0.9.0 0.8.9 0.8.8 All 35 releases
← All changes | includes/desktop-files/downloads.php +76 -68 0.9.8 → 1.1.11 View file →
@@ -1,7 +1,7 @@
1 1 <?php
2 2 /**
3 - * Desktop Mode — authenticated downloads for stored files.
3 + * OpenStation — authenticated downloads for stored files.
4 4 *
5 5 * Two routes:
6 6 *
7 7 * GET /desktop-mode/v1/files/uploads/(?P<id>\d+)/download
@@ -26,9 +26,9 @@
26 26 *
27 27 * Not-found and no-access are both 404 — a download probe must not
28 28 * reveal that a file exists (the Drive behavior).
29 29 *
30 - * @package WPDesktopMode
30 + * @package OpenStation
31 31 */
32 32
33 33 defined( 'ABSPATH' ) || exit;
34 34
@@ -34,22 +34,30 @@
34 34
35 35 /**
36 36 * Register the download routes.
37 37 */
38 -function desktop_mode_files_register_download_rest_routes() {
38 +function openstation_files_register_download_rest_routes() {
39 39 $ns = 'desktop-mode/v1';
40 - register_rest_route( $ns, '/files/uploads/(?P<id>\d+)/download', array(
41 - 'methods' => WP_REST_Server::READABLE,
42 - 'permission_callback' => 'desktop_mode_files_rest_permission',
43 - 'callback' => 'desktop_mode_files_rest_download_file',
44 - ) );
45 - register_rest_route( $ns, '/files/folders/(?P<id>\d+)/download', array(
46 - 'methods' => WP_REST_Server::READABLE,
47 - 'permission_callback' => 'desktop_mode_files_rest_permission',
48 - 'callback' => 'desktop_mode_files_rest_download_folder_zip',
49 - ) );
40 + register_rest_route(
41 + $ns,
42 + '/files/uploads/(?P<id>\d+)/download',
43 + array(
44 + 'methods' => WP_REST_Server::READABLE,
45 + 'permission_callback' => 'openstation_files_rest_permission',
46 + 'callback' => 'openstation_files_rest_download_file',
47 + )
48 + );
49 + register_rest_route(
50 + $ns,
51 + '/files/folders/(?P<id>\d+)/download',
52 + array(
53 + 'methods' => WP_REST_Server::READABLE,
54 + 'permission_callback' => 'openstation_files_rest_permission',
55 + 'callback' => 'openstation_files_rest_download_folder_zip',
56 + )
57 + );
50 58 }
51 -add_action( 'rest_api_init', 'desktop_mode_files_register_download_rest_routes' );
59 +add_action( 'rest_api_init', 'openstation_files_register_download_rest_routes' );
52 60
53 61 /**
54 62 * The masked not-found error shared by every failure path that
55 63 * must not leak existence.
@@ -55,11 +63,11 @@
55 63 * must not leak existence.
56 64 *
57 65 * @return WP_Error
58 66 */
59 -function desktop_mode_files_download_not_found() {
67 +function openstation_files_download_not_found() {
60 68 return new WP_Error(
61 - 'desktop_mode_files_not_found',
69 + 'openstation_files_not_found',
62 70 __( 'File not found.', 'desktop-mode' ),
63 71 array( 'status' => 404 )
64 72 );
65 73 }
@@ -69,18 +77,18 @@
69 77 *
70 78 * @param WP_REST_Request $req Request.
71 79 * @return WP_REST_Response|WP_Error
72 80 */
73 -function desktop_mode_files_rest_download_file( WP_REST_Request $req ) {
81 +function openstation_files_rest_download_file( WP_REST_Request $req ) {
74 82 $file_id = (int) $req['id'];
75 83 $user_id = get_current_user_id();
76 - $row = desktop_mode_stored_files_get( $file_id );
77 - if ( ! $row || ! desktop_mode_stored_file_user_can_read( $file_id, $user_id ) ) {
78 - return desktop_mode_files_download_not_found();
84 + $row = openstation_stored_files_get( $file_id );
85 + if ( ! $row || ! openstation_stored_file_user_can_read( $file_id, $user_id ) ) {
86 + return openstation_files_download_not_found();
79 87 }
80 - $path = desktop_mode_stored_file_path( $row );
88 + $path = openstation_stored_file_path( $row );
81 89 if ( ! $path || ! file_exists( $path ) ) {
82 - return desktop_mode_files_download_not_found();
90 + return openstation_files_download_not_found();
83 91 }
84 92
85 93 /**
86 94 * Fires when a stored-file download is about to be served.
@@ -87,11 +95,11 @@
87 95 *
88 96 * @param int $file_id Stored-file id.
89 97 * @param int $user_id Downloader.
90 98 */
91 - do_action( 'desktop_mode_stored_file_downloaded', $file_id, $user_id );
99 + do_action( 'openstation_stored_file_downloaded', $file_id, $user_id );
92 100
93 - return desktop_mode_files_download_stream_response(
101 + return openstation_files_download_stream_response(
94 102 $path,
95 103 $row['display_name'],
96 104 $row['mime'],
97 105 false
@@ -103,27 +111,27 @@
103 111 *
104 112 * @param WP_REST_Request $req Request.
105 113 * @return WP_REST_Response|WP_Error
106 114 */
107 -function desktop_mode_files_rest_download_folder_zip( WP_REST_Request $req ) {
115 +function openstation_files_rest_download_folder_zip( WP_REST_Request $req ) {
108 116 $folder_id = (int) $req['id'];
109 117 $user_id = get_current_user_id();
110 - $folder = desktop_mode_files_get_folder( $folder_id );
118 + $folder = openstation_files_get_folder( $folder_id );
111 119 if ( ! $folder ) {
112 - return desktop_mode_files_download_not_found();
120 + return openstation_files_download_not_found();
113 121 }
114 122 $is_owner = (int) $folder['owner_id'] === $user_id;
115 123 if ( ! $is_owner ) {
116 - $cap = function_exists( 'desktop_mode_folder_share_user_capability' )
117 - ? desktop_mode_folder_share_user_capability( $folder_id, $user_id )
124 + $cap = function_exists( 'openstation_folder_share_user_capability' )
125 + ? openstation_folder_share_user_capability( $folder_id, $user_id )
118 126 : 'none';
119 127 if ( 'none' === $cap ) {
120 - return desktop_mode_files_download_not_found();
128 + return openstation_files_download_not_found();
121 129 }
122 130 }
123 131 if ( ! class_exists( 'ZipArchive' ) ) {
124 132 return new WP_Error(
125 - 'desktop_mode_stored_files_no_zip',
133 + 'openstation_stored_files_no_zip',
126 134 __( 'Folder download requires the PHP zip extension.', 'desktop-mode' ),
127 135 array( 'status' => 501 )
128 136 );
129 137 }
@@ -132,9 +140,9 @@
132 140 'entries' => array(), // path-in-zip => absolute path.
133 141 'empty_dirs' => array(), // path-in-zip (with trailing /).
134 142 'total_bytes' => 0,
135 143 );
136 - $result = desktop_mode_files_collect_zip_entries( $folder_id, $user_id, '', $manifest, array( $folder_id => true ), 0 );
144 + $result = openstation_files_collect_zip_entries( $folder_id, $user_id, '', $manifest, array( $folder_id => true ), 0 );
137 145 if ( is_wp_error( $result ) ) {
138 146 return $result;
139 147 }
140 148 $manifest = $result;
@@ -139,11 +147,11 @@
139 147 }
140 148 $manifest = $result;
141 149
142 150 require_once ABSPATH . 'wp-admin/includes/file.php';
143 - $tmp = wp_tempnam( 'desktop-mode-folder-zip' );
151 + $tmp = wp_tempnam( 'os-folder-zip' );
144 152 if ( ! $tmp ) {
145 - return new WP_Error( 'desktop_mode_stored_files_zip_failed', __( 'Could not create the archive.', 'desktop-mode' ), array( 'status' => 500 ) );
153 + return new WP_Error( 'openstation_stored_files_zip_failed', __( 'Could not create the archive.', 'desktop-mode' ), array( 'status' => 500 ) );
146 154 }
147 155 // Belt and braces for aborted connections — the normal path
148 156 // deletes right after streaming.
149 157 register_shutdown_function( 'wp_delete_file', $tmp );
@@ -150,9 +158,9 @@
150 158
151 159 $zip = new ZipArchive();
152 160 if ( true !== $zip->open( $tmp, ZipArchive::OVERWRITE ) ) {
153 161 wp_delete_file( $tmp );
154 - return new WP_Error( 'desktop_mode_stored_files_zip_failed', __( 'Could not create the archive.', 'desktop-mode' ), array( 'status' => 500 ) );
162 + return new WP_Error( 'openstation_stored_files_zip_failed', __( 'Could not create the archive.', 'desktop-mode' ), array( 'status' => 500 ) );
155 163 }
156 164 foreach ( $manifest['empty_dirs'] as $dir_entry ) {
157 165 $zip->addEmptyDir( $dir_entry );
158 166 }
@@ -167,9 +175,9 @@
167 175 }
168 176 }
169 177 if ( ! $zip->close() ) {
170 178 wp_delete_file( $tmp );
171 - return new WP_Error( 'desktop_mode_stored_files_zip_failed', __( 'Could not finish the archive (disk full?).', 'desktop-mode' ), array( 'status' => 500 ) );
179 + return new WP_Error( 'openstation_stored_files_zip_failed', __( 'Could not finish the archive (disk full?).', 'desktop-mode' ), array( 'status' => 500 ) );
172 180 }
173 181
174 182 /**
175 183 * Fires when a folder-zip download is about to be served.
@@ -177,12 +185,12 @@
177 185 * @param int $folder_id Folder id.
178 186 * @param int $user_id Downloader.
179 187 * @param int $count Number of files in the archive.
180 188 */
181 - do_action( 'desktop_mode_folder_zip_downloaded', $folder_id, $user_id, count( $manifest['entries'] ) );
189 + do_action( 'openstation_folder_zip_downloaded', $folder_id, $user_id, count( $manifest['entries'] ) );
182 190
183 191 $zip_name = sanitize_file_name( '' !== (string) $folder['name'] ? (string) $folder['name'] : 'folder' ) . '.zip';
184 - return desktop_mode_files_download_stream_response( $tmp, $zip_name, 'application/zip', true );
192 + return openstation_files_download_stream_response( $tmp, $zip_name, 'application/zip', true );
185 193 }
186 194
187 195 /**
188 196 * Recursive manifest collector. Walks the folder's placements
@@ -191,17 +199,17 @@
191 199 * directories, and enforces the caps.
192 200 *
193 201 * @internal
194 202 *
195 - * @param int $folder_id Folder to walk.
196 - * @param int $user_id Viewer.
203 + * @param int $folder_id Folder to walk.
204 + * @param int $user_id Viewer.
197 205 * @param string $prefix Path prefix inside the zip ('' at root).
198 - * @param array $manifest Accumulator (entries / empty_dirs / total_bytes).
199 - * @param array $visited Folder ids already on the walk path (cycle guard).
200 - * @param int $depth Current depth.
206 + * @param array $manifest Accumulator (entries / empty_dirs / total_bytes).
207 + * @param array $visited Folder ids already on the walk path (cycle guard).
208 + * @param int $depth Current depth.
201 209 * @return array|WP_Error The updated manifest.
202 210 */
203 -function desktop_mode_files_collect_zip_entries( $folder_id, $user_id, $prefix, $manifest, $visited, $depth ) {
211 +function openstation_files_collect_zip_entries( $folder_id, $user_id, $prefix, $manifest, $visited, $depth ) {
204 212 if ( $depth > 32 ) {
205 213 return $manifest; // Depth cap — quietly stop descending.
206 214 }
207 215
@@ -211,9 +219,9 @@
211 219 *
212 220 * @param array $caps `{ max_entries: int, max_bytes: int }`.
213 221 */
214 222 $caps = (array) apply_filters(
215 - 'desktop_mode_stored_files_zip_caps',
223 + 'openstation_stored_files_zip_caps',
216 224 array(
217 225 'max_entries' => 1000,
218 226 'max_bytes' => 500 * MB_IN_BYTES,
219 227 )
@@ -218,9 +226,9 @@
218 226 'max_bytes' => 500 * MB_IN_BYTES,
219 227 )
220 228 );
221 229
222 - $rows = desktop_mode_files_get_for_user_folder( $user_id, $folder_id );
230 + $rows = openstation_files_get_for_user_folder( $user_id, $folder_id );
223 231 $used_names = array(); // lowercase name => count, per directory.
224 232 $had_child = false;
225 233
226 234 foreach ( $rows as $row ) {
@@ -228,13 +236,13 @@
228 236 $sub_id = (int) $row['file_ref'];
229 237 if ( $sub_id <= 0 || isset( $visited[ $sub_id ] ) ) {
230 238 continue;
231 239 }
232 - $sub = desktop_mode_files_get_folder( $sub_id );
240 + $sub = openstation_files_get_folder( $sub_id );
233 241 if ( ! $sub ) {
234 242 continue;
235 243 }
236 - $dir_name = desktop_mode_files_zip_unique_name(
244 + $dir_name = openstation_files_zip_unique_name(
237 245 sanitize_file_name( '' !== (string) $sub['name'] ? (string) $sub['name'] : 'folder' ),
238 246 $used_names
239 247 );
240 248 $had_child = true;
@@ -239,9 +247,9 @@
239 247 );
240 248 $had_child = true;
241 249 $visited[ $sub_id ] = true;
242 250 $before = count( $manifest['entries'] ) + count( $manifest['empty_dirs'] );
243 - $manifest = desktop_mode_files_collect_zip_entries( $sub_id, $user_id, $prefix . $dir_name . '/', $manifest, $visited, $depth + 1 );
251 + $manifest = openstation_files_collect_zip_entries( $sub_id, $user_id, $prefix . $dir_name . '/', $manifest, $visited, $depth + 1 );
244 252 if ( is_wp_error( $manifest ) ) {
245 253 return $manifest;
246 254 }
247 255 if ( count( $manifest['entries'] ) + count( $manifest['empty_dirs'] ) === $before ) {
@@ -254,18 +262,18 @@
254 262 if ( 'upload' !== $row['file_type'] ) {
255 263 continue; // References are not bytes; skipped by design.
256 264 }
257 265 $file_id = (int) $row['file_ref'];
258 - $file = desktop_mode_stored_files_get( $file_id );
259 - if ( ! $file || ! desktop_mode_stored_file_user_can_read( $file_id, $user_id ) ) {
266 + $file = openstation_stored_files_get( $file_id );
267 + if ( ! $file || ! openstation_stored_file_user_can_read( $file_id, $user_id ) ) {
260 268 continue;
261 269 }
262 - $path = desktop_mode_stored_file_path( $file );
270 + $path = openstation_stored_file_path( $file );
263 271 if ( ! $path || ! file_exists( $path ) ) {
264 272 continue;
265 273 }
266 274 $had_child = true;
267 - $entry_name = desktop_mode_files_zip_unique_name(
275 + $entry_name = openstation_files_zip_unique_name(
268 276 sanitize_file_name( '' !== $file['display_name'] ? $file['display_name'] : 'file' ),
269 277 $used_names
270 278 );
271 279
@@ -271,9 +279,9 @@
271 279
272 280 $manifest['total_bytes'] += (int) $file['size_bytes'];
273 281 if ( count( $manifest['entries'] ) + 1 > (int) $caps['max_entries'] ) {
274 282 return new WP_Error(
275 - 'desktop_mode_stored_files_zip_too_big',
283 + 'openstation_stored_files_zip_too_big',
276 284 __( 'This folder has too many files to download as one archive.', 'desktop-mode' ),
277 285 array( 'status' => 400 )
278 286 );
279 287 }
@@ -278,9 +286,9 @@
278 286 );
279 287 }
280 288 if ( (int) $caps['max_bytes'] > 0 && $manifest['total_bytes'] > (int) $caps['max_bytes'] ) {
281 289 return new WP_Error(
282 - 'desktop_mode_stored_files_zip_too_big',
290 + 'openstation_stored_files_zip_too_big',
283 291 __( 'This folder is too large to download as one archive.', 'desktop-mode' ),
284 292 array( 'status' => 400 )
285 293 );
286 294 }
@@ -305,15 +313,15 @@
305 313 * @param string $name Sanitized candidate name.
306 314 * @param array $used_names By-ref lowercase tally for the directory.
307 315 * @return string
308 316 */
309 -function desktop_mode_files_zip_unique_name( $name, &$used_names ) {
317 +function openstation_files_zip_unique_name( $name, &$used_names ) {
310 318 $key = strtolower( $name );
311 319 if ( ! isset( $used_names[ $key ] ) ) {
312 320 $used_names[ $key ] = 1;
313 321 return $name;
314 322 }
315 - $used_names[ $key ]++;
323 + ++$used_names[ $key ];
316 324 $n = $used_names[ $key ];
317 325 $dot = strrpos( $name, '.' );
318 326 if ( false === $dot || 0 === $dot ) {
319 327 return $name . " ($n)";
@@ -333,12 +341,12 @@
333 341 * @param string $mime MIME type ('' = octet-stream).
334 342 * @param bool $delete_after Delete `$path` after streaming (zip temp).
335 343 * @return WP_REST_Response
336 344 */
337 -function desktop_mode_files_download_stream_response( $path, $name, $mime, $delete_after ) {
345 +function openstation_files_download_stream_response( $path, $name, $mime, $delete_after ) {
338 346 return new WP_REST_Response(
339 347 array(
340 - '__desktop_mode_stream' => array(
348 + '__openstation_stream' => array(
341 349 'path' => (string) $path,
342 350 'name' => (string) $name,
343 351 'mime' => (string) $mime,
344 352 'delete_after' => (bool) $delete_after,
@@ -357,9 +365,9 @@
357 365 * @param WP_HTTP_Response $result Result to send.
358 366 * @param WP_REST_Request $request Request used.
359 367 * @return bool
360 368 */
361 -function desktop_mode_files_serve_download( $served, $result, $request ) {
369 +function openstation_files_serve_download( $served, $result, $request ) {
362 370 if ( $served || ! $result instanceof WP_HTTP_Response ) {
363 371 return $served;
364 372 }
365 373 $route = (string) $request->get_route();
@@ -366,18 +374,18 @@
366 374 if ( ! preg_match( '#^/desktop-mode/v1/files/(uploads|folders)/\d+/download$#', $route ) ) {
367 375 return $served;
368 376 }
369 377 $data = $result->get_data();
370 - if ( ! is_array( $data ) || empty( $data['__desktop_mode_stream'] ) || 200 !== $result->get_status() ) {
378 + if ( ! is_array( $data ) || empty( $data['__openstation_stream'] ) || 200 !== $result->get_status() ) {
371 379 return $served; // Error shapes serialize as normal JSON.
372 380 }
373 - $stream = $data['__desktop_mode_stream'];
381 + $stream = $data['__openstation_stream'];
374 382 $path = (string) $stream['path'];
375 383 if ( '' === $path || ! file_exists( $path ) || ! is_readable( $path ) ) {
376 384 return $served;
377 385 }
378 386
379 - desktop_mode_files_emit_download( $path, (string) $stream['name'], (string) $stream['mime'] );
387 + openstation_files_emit_download( $path, (string) $stream['name'], (string) $stream['mime'] );
380 388
381 389 if ( ! empty( $stream['delete_after'] ) ) {
382 390 wp_delete_file( $path );
383 391 }
@@ -382,9 +390,9 @@
382 390 wp_delete_file( $path );
383 391 }
384 392 return true;
385 393 }
386 -add_filter( 'rest_pre_serve_request', 'desktop_mode_files_serve_download', 10, 3 );
394 +add_filter( 'rest_pre_serve_request', 'openstation_files_serve_download', 10, 3 );
387 395
388 396 /**
389 397 * Send the headers and the bytes. Split out so PHPUnit can target
390 398 * the header/name logic without hijacking output.
@@ -394,9 +402,9 @@
394 402 * @param string $path Absolute file path.
395 403 * @param string $name Download filename.
396 404 * @param string $mime MIME type.
397 405 */
398 -function desktop_mode_files_emit_download( $path, $name, $mime ) {
406 +function openstation_files_emit_download( $path, $name, $mime ) {
399 407 $size = (int) filesize( $path );
400 408
401 409 // Kill every output buffer + compression layer so
402 410 // Content-Length stays exact and readfile streams instead of
@@ -435,10 +443,10 @@
435 443 /**
436 444 * Daily sweep of stale zip temp files (aborted downloads whose
437 445 * shutdown cleanup never ran).
438 446 */
439 -function desktop_mode_stored_files_sweep_zip_temps() {
440 - $entries = glob( trailingslashit( get_temp_dir() ) . 'desktop-mode-folder-zip*' );
447 +function openstation_stored_files_sweep_zip_temps() {
448 + $entries = glob( trailingslashit( get_temp_dir() ) . 'os-folder-zip*' );
441 449 foreach ( (array) $entries as $entry ) {
442 450 if ( ! is_file( $entry ) ) {
443 451 continue;
444 452 }
@@ -447,5 +455,5 @@
447 455 wp_delete_file( $entry );
448 456 }
449 457 }
450 458 }
451 -add_action( 'desktop_mode_files_daily_prune', 'desktop_mode_stored_files_sweep_zip_temps' );
459 +add_action( 'desktop_mode_files_daily_prune', 'openstation_stored_files_sweep_zip_temps' );