Skip to content

Commit 3606434

Browse files
committed
Set the file's mtime from the headers in bulk upload
Signed-off-by: Louis Chemineau <[email protected]>
1 parent db0414a commit 3606434

File tree

10 files changed

+103
-19
lines changed

10 files changed

+103
-19
lines changed

apps/dav/composer/composer/ClassLoader.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ public function getFallbackDirsPsr4()
149149

150150
/**
151151
* @return string[] Array of classname => path
152-
* @psalm-var array<string, string>
152+
* @psalm-return array<string, string>
153153
*/
154154
public function getClassMap()
155155
{

apps/dav/composer/composer/autoload_classmap.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@
163163
'OCA\\DAV\\Connector\\Sabre\\FilesReportPlugin' => $baseDir . '/../lib/Connector/Sabre/FilesReportPlugin.php',
164164
'OCA\\DAV\\Connector\\Sabre\\LockPlugin' => $baseDir . '/../lib/Connector/Sabre/LockPlugin.php',
165165
'OCA\\DAV\\Connector\\Sabre\\MaintenancePlugin' => $baseDir . '/../lib/Connector/Sabre/MaintenancePlugin.php',
166+
'OCA\\DAV\\Connector\\Sabre\\MtimeSanitizer' => $baseDir . '/../lib/Connector/Sabre/MtimeSanitizer.php',
166167
'OCA\\DAV\\Connector\\Sabre\\Node' => $baseDir . '/../lib/Connector/Sabre/Node.php',
167168
'OCA\\DAV\\Connector\\Sabre\\ObjectTree' => $baseDir . '/../lib/Connector/Sabre/ObjectTree.php',
168169
'OCA\\DAV\\Connector\\Sabre\\Principal' => $baseDir . '/../lib/Connector/Sabre/Principal.php',

apps/dav/composer/composer/autoload_static.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ class ComposerStaticInitDAV
178178
'OCA\\DAV\\Connector\\Sabre\\FilesReportPlugin' => __DIR__ . '/..' . '/../lib/Connector/Sabre/FilesReportPlugin.php',
179179
'OCA\\DAV\\Connector\\Sabre\\LockPlugin' => __DIR__ . '/..' . '/../lib/Connector/Sabre/LockPlugin.php',
180180
'OCA\\DAV\\Connector\\Sabre\\MaintenancePlugin' => __DIR__ . '/..' . '/../lib/Connector/Sabre/MaintenancePlugin.php',
181+
'OCA\\DAV\\Connector\\Sabre\\MtimeSanitizer' => __DIR__ . '/..' . '/../lib/Connector/Sabre/MtimeSanitizer.php',
181182
'OCA\\DAV\\Connector\\Sabre\\Node' => __DIR__ . '/..' . '/../lib/Connector/Sabre/Node.php',
182183
'OCA\\DAV\\Connector\\Sabre\\ObjectTree' => __DIR__ . '/..' . '/../lib/Connector/Sabre/ObjectTree.php',
183184
'OCA\\DAV\\Connector\\Sabre\\Principal' => __DIR__ . '/..' . '/../lib/Connector/Sabre/Principal.php',

apps/dav/composer/composer/installed.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
'type' => 'library',
66
'install_path' => __DIR__ . '/../',
77
'aliases' => array(),
8-
'reference' => 'c6429e6cd19c57582364338362e543580821cf99',
8+
'reference' => 'e2c675724fc4ea50f1275bf0027b96f277c32578',
99
'name' => '__root__',
1010
'dev' => false,
1111
),
@@ -16,7 +16,7 @@
1616
'type' => 'library',
1717
'install_path' => __DIR__ . '/../',
1818
'aliases' => array(),
19-
'reference' => 'c6429e6cd19c57582364338362e543580821cf99',
19+
'reference' => 'e2c675724fc4ea50f1275bf0027b96f277c32578',
2020
'dev_requirement' => false,
2121
),
2222
),

apps/dav/lib/BulkUpload/BulkUploadPlugin.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
use Sabre\HTTP\ResponseInterface;
3030
use OCP\Files\Folder;
3131
use OCP\AppFramework\Http;
32+
use OCA\DAV\Connector\Sabre\MtimeSanitizer;
3233

3334
class BulkUploadPlugin extends ServerPlugin {
3435

@@ -78,7 +79,16 @@ public function httpPost(RequestInterface $request, ResponseInterface $response)
7879
}
7980

8081
try {
82+
// TODO: Remove 'x-file-mtime' when the desktop client no longer use it.
83+
if (isset($headers['x-file-mtime'])) {
84+
$mtime = MtimeSanitizer::sanitizeMtime($headers['x-file-mtime']);
85+
} else {
86+
$mtime = MtimeSanitizer::sanitizeMtime($headers['x-oc-mtime']);
87+
}
88+
8189
$node = $this->userFolder->newFile($headers['x-file-path'], $content);
90+
$node->touch($mtime);
91+
8292
$writtenFiles[$headers['x-file-path']] = [
8393
"error" => false,
8494
"etag" => $node->getETag(),
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
/**
3+
* @copyright Copyright (c) 2021, Louis Chemineau <[email protected]>
4+
*
5+
* @author Louis Chemineau <[email protected]>
6+
*
7+
* @license AGPL-3.0
8+
*
9+
* This code is free software: you can redistribute it and/or modify
10+
* it under the terms of the GNU Affero General Public License, version 3,
11+
* as published by the Free Software Foundation.
12+
*
13+
* This program is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
* GNU Affero General Public License for more details.
17+
*
18+
* You should have received a copy of the GNU Affero General Public License, version 3,
19+
* along with this program. If not, see <http://www.gnu.org/licenses/>
20+
*
21+
*/
22+
23+
namespace OCA\DAV\Connector\Sabre;
24+
25+
class MtimeSanitizer {
26+
public static function sanitizeMtime($mtimeFromRequest) {
27+
// In PHP 5.X "is_numeric" returns true for strings in hexadecimal
28+
// notation. This is no longer the case in PHP 7.X, so this check
29+
// ensures that strings with hexadecimal notations fail too in PHP 5.X.
30+
$isHexadecimal = is_string($mtimeFromRequest) && preg_match('/^\s*0[xX]/', $mtimeFromRequest);
31+
if ($isHexadecimal || !is_numeric($mtimeFromRequest)) {
32+
throw new \InvalidArgumentException('X-OC-MTime header must be an integer (unix timestamp).');
33+
}
34+
35+
// Prevent writing invalid mtime (timezone-proof)
36+
if ((int)$mtimeFromRequest <= 24 * 60 * 60) {
37+
throw new \InvalidArgumentException('X-OC-MTime header must be a valid positive integer');
38+
}
39+
40+
return (int)$mtimeFromRequest;
41+
}
42+
}

apps/dav/lib/Connector/Sabre/Node.php

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -404,19 +404,6 @@ public function getFileInfo() {
404404
}
405405

406406
protected function sanitizeMtime($mtimeFromRequest) {
407-
// In PHP 5.X "is_numeric" returns true for strings in hexadecimal
408-
// notation. This is no longer the case in PHP 7.X, so this check
409-
// ensures that strings with hexadecimal notations fail too in PHP 5.X.
410-
$isHexadecimal = is_string($mtimeFromRequest) && preg_match('/^\s*0[xX]/', $mtimeFromRequest);
411-
if ($isHexadecimal || !is_numeric($mtimeFromRequest)) {
412-
throw new \InvalidArgumentException('X-OC-MTime header must be an integer (unix timestamp).');
413-
}
414-
415-
// Prevent writing invalid mtime (timezone-proof)
416-
if ((int)$mtimeFromRequest <= 24 * 60 * 60) {
417-
throw new \InvalidArgumentException('X-OC-MTime header must be a valid positive integer');
418-
}
419-
420-
return (int)$mtimeFromRequest;
407+
return MtimeSanitizer::sanitizeMtime($mtimeFromRequest);
421408
}
422409
}

apps/dav/tests/benchmarks/bulk_upload.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ do
3737
echo -en "--$BOUNDARY\r\n"
3838
# echo -en "Content-ID: $file_name\r\n"
3939
echo -en "X-File-Path: $file_remote_path\r\n"
40-
echo -en "X-File-Mtime: $file_mtime\r\n"
40+
echo -en "X-OC-Mtime: $file_mtime\r\n"
4141
# echo -en "X-File-Id: $file_id\r\n"
4242
echo -en "X-File-Md5: $file_hash\r\n"
4343
echo -en "Content-Length: $file_size\r\n"

build/integration/features/bootstrap/WebDav.php

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,19 @@ public function downloadedContentShouldBe($content) {
207207
Assert::assertEquals($content, (string)$this->response->getBody());
208208
}
209209

210+
/**
211+
* @Then /^File "([^"]*)" should have prop "([^"]*):([^"]*)" equal to "([^"]*)"$/
212+
* @param string $file
213+
* @param string $prefix
214+
* @param string $prop
215+
* @param string $value
216+
*/
217+
public function checkPropForFile($file, $prefix, $prop, $value) {
218+
$elementList = $this->propfindFile($this->currentUser, $file, "<$prefix:$prop/>");
219+
$property = $elementList['/'.$this->getDavFilesPath($this->currentUser).$file][200]["{DAV:}$prop"];
220+
Assert::assertEquals($property, $value);
221+
}
222+
210223
/**
211224
* @Then /^Downloaded content when downloading file "([^"]*)" with range "([^"]*)" should be "([^"]*)"$/
212225
* @param string $fileSource
@@ -380,6 +393,30 @@ public function listFolder($user, $path, $folderDepth, $properties = null) {
380393
return $response;
381394
}
382395

396+
/* Returns the elements of a report command
397+
* @param string $user
398+
* @param string $path
399+
* @param string $properties properties which needs to be included in the report
400+
* @param string $filterRules filter-rules to choose what needs to appear in the report
401+
*/
402+
public function propfindFile($user, $path, $properties = '') {
403+
$client = $this->getSabreClient($user);
404+
405+
$body = '<?xml version="1.0" encoding="utf-8" ?>
406+
<d:propfind xmlns:d="DAV:"
407+
xmlns:oc="http://owncloud.org/ns"
408+
xmlns:nc="http://nextcloud.org/ns"
409+
xmlns:ocs="http://open-collaboration-services.org/ns">
410+
<d:prop>
411+
' . $properties . '
412+
</d:prop>
413+
</d:propfind>';
414+
415+
$response = $client->request('PROPFIND', $this->makeSabrePath($user, $path), $body);
416+
$parsedResponse = $client->parseMultistatus($response['body']);
417+
return $parsedResponse;
418+
}
419+
383420
/* Returns the elements of a report command
384421
* @param string $user
385422
* @param string $path
@@ -561,25 +598,28 @@ public function userUploadsChunkFileOfWithToWithChecksum($user, $num, $total, $d
561598
* @param string $name3
562599
* @param string $content3
563600
*/
564-
public function userUploadsChunkedFiles($user, $name1, $content1, $name2, $content2, $name3, $content3) {
601+
public function userUploadsBulkedFiles($user, $name1, $content1, $name2, $content2, $name3, $content3) {
565602
$boundary = "boundary_azertyuiop";
566603

567604
$body = "";
568605
$body .= '--'.$boundary."\r\n";
569606
$body .= "X-File-Path: ".$name1."\r\n";
570607
$body .= "X-File-MD5: f6a6263167c92de8644ac998b3c4e4d1\r\n";
608+
$body .= "X-OC-Mtime: 1111111111\r\n";
571609
$body .= "Content-Length: ".strlen($content1)."\r\n";
572610
$body .= "\r\n";
573611
$body .= $content1."\r\n";
574612
$body .= '--'.$boundary."\r\n";
575613
$body .= "X-File-Path: ".$name2."\r\n";
576614
$body .= "X-File-MD5: 87c7d4068be07d390a1fffd21bf1e944\r\n";
615+
$body .= "X-OC-Mtime: 2222222222\r\n";
577616
$body .= "Content-Length: ".strlen($content2)."\r\n";
578617
$body .= "\r\n";
579618
$body .= $content2."\r\n";
580619
$body .= '--'.$boundary."\r\n";
581620
$body .= "X-File-Path: ".$name3."\r\n";
582621
$body .= "X-File-MD5: e86a1cf0678099986a901c79086f5617\r\n";
622+
$body .= "X-File-Mtime: 3333333333\r\n";
583623
$body .= "Content-Length: ".strlen($content3)."\r\n";
584624
$body .= "\r\n";
585625
$body .= $content3."\r\n";

build/integration/features/webdav-related.feature

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,10 +615,13 @@ Feature: webdav-related
615615
When As an "user0"
616616
Then Downloading file "/A.txt"
617617
And Downloaded content should be "AAAAA"
618+
And File "/A.txt" should have prop "d:getlastmodified" equal to "Fri, 18 Mar 2005 01:58:31 GMT"
618619
And Downloading file "/B.txt"
619620
And Downloaded content should be "BBBBB"
621+
And File "/B.txt" should have prop "d:getlastmodified" equal to "Sat, 02 Jun 2040 03:57:02 GMT"
620622
And Downloading file "/C.txt"
621623
And Downloaded content should be "CCCCC"
624+
And File "/C.txt" should have prop "d:getlastmodified" equal to "Sun, 18 Aug 2075 05:55:33 GMT"
622625

623626
Scenario: Creating a folder with invalid characters
624627
Given using new dav path

0 commit comments

Comments
 (0)