From c488237c150805e61f0882e8761be936847c34ec Mon Sep 17 00:00:00 2001
From: Pawel Andruszkiewicz
Date: Fri, 13 Nov 2015 16:16:10 +0100
Subject: [PATCH] [File] Reworked internal represenation of URLs, added support
for cdvfile://
fullPath - full path relative to the FileSystem root.
[Verification] Code compiles, pass rate: 139/140, all manual tests pass.
Change-Id: I28f82922def166af4ded5e38f494ee05e6230787
Signed-off-by: Pawel Andruszkiewicz
---
src/file/cordova_file_api.js | 1 +
src/file/js/DirectoryEntry.js | 16 ++++-
src/file/js/DirectoryReader.js | 8 ++-
src/file/js/Entry.js | 56 +++++++++++------
src/file/js/File.js | 1 +
src/file/js/FileReader.js | 7 +++
src/file/js/FileSystem.js | 31 ++++++++++
src/file/js/FileWriter.js | 21 ++++++-
src/file/js/resolveLocalFileSystemURI.js | 7 +--
src/file/js/rootsUtils.js | 102 +++++++++++++++++++++++++------
10 files changed, 202 insertions(+), 48 deletions(-)
create mode 100644 src/file/js/FileSystem.js
diff --git a/src/file/cordova_file_api.js b/src/file/cordova_file_api.js
index d28e754..b5a171d 100755
--- a/src/file/cordova_file_api.js
+++ b/src/file/cordova_file_api.js
@@ -26,4 +26,5 @@
//= require('requestFileSystem.js');
//= require('resolveLocalFileSystemURI.js');
+//= require('FileSystem.js');
//= require('File.js');
diff --git a/src/file/js/DirectoryEntry.js b/src/file/js/DirectoryEntry.js
index 820c725..3f11fd4 100644
--- a/src/file/js/DirectoryEntry.js
+++ b/src/file/js/DirectoryEntry.js
@@ -46,13 +46,18 @@ cordova.define('cordova-plugin-file.tizen.DirectoryEntry', function(require, exp
}
var getFunction = function(successCallback, errorCallback, args, isDirectory) {
- var uri = rootsUtils.stripTrailingSlash(args[0]),
+ var uri = rootsUtils.internalUrlToNativePath(args[0]),
path = rootsUtils.stripTrailingSlash(args[1]),
options = args[2] || {},
create_flag = !!options.create,
exclusive_flag = !!options.exclusive,
absolute_path = '';
+ if (!uri) {
+ errorCallback && errorCallback(FileError.ENCODING_ERR);
+ return;
+ }
+
if ('/' === path[0]) {
// path seems absolute, checking if path is a child of this object URI
if (0 === path.indexOf(uri)) {
@@ -69,7 +74,7 @@ cordova.define('cordova-plugin-file.tizen.DirectoryEntry', function(require, exp
absolute_path = uri + '/' + path;
}
- var root = rootsUtils.findFilesystem(absolute_path).fullPath;
+ var root = rootsUtils.findFilesystem(absolute_path).nativeURL;
var clean_path = sanitizePath(absolute_path);
if (0 !== clean_path.indexOf(root)) {
@@ -147,7 +152,12 @@ module.exports = {
getFunction(successCallback, errorCallback, args, true);
},
removeRecursively: function(successCallback, errorCallback, args) {
- var uri = args[0];
+ var uri = rootsUtils.internalUrlToNativePath(args[0]);
+
+ if (!uri) {
+ errorCallback && errorCallback(FileError.ENCODING_ERR);
+ return;
+ }
if (rootsUtils.isRootUri(uri)) {
console.error('It is not allowed to remove root directory.');
diff --git a/src/file/js/DirectoryReader.js b/src/file/js/DirectoryReader.js
index 3d1d926..6feed91 100644
--- a/src/file/js/DirectoryReader.js
+++ b/src/file/js/DirectoryReader.js
@@ -20,7 +20,13 @@ cordova.define('cordova-plugin-file.tizen.DirectoryReader', function(require, ex
module.exports = {
readEntries: function(successCallback, errorCallback, args) {
- var uri = args[0];
+ var uri = rootsUtils.internalUrlToNativePath(args[0]);
+
+ if (!uri) {
+ errorCallback && errorCallback(FileError.ENCODING_ERR);
+ return;
+ }
+
var fail = function(e) {
errorCallback && errorCallback(ConvertTizenFileError(e));
}
diff --git a/src/file/js/Entry.js b/src/file/js/Entry.js
index 1b56c28..1b258e8 100644
--- a/src/file/js/Entry.js
+++ b/src/file/js/Entry.js
@@ -42,10 +42,10 @@ var resolveParent = function(srcURL, errorCallback, rest){
};
var changeFile = function(method, successCallback, errorCallback, args) {
- var srcURL = args[0];
+ var srcURL = rootsUtils.internalUrlToNativePath(args[0]);
var name = args[2];
- var destDir = args[1];
- var destURL = rootsUtils.stripTrailingSlash(destDir) + '/' + name;
+ var destDir = rootsUtils.internalUrlToNativePath(args[1]);
+ var destURL = destDir + '/' + name;
function fail(e, msg) {
console.error(msg);
@@ -54,12 +54,17 @@ var changeFile = function(method, successCallback, errorCallback, args) {
}
}
+ if (!srcURL || !destDir) {
+ fail(FileError.ENCODING_ERR, 'Error - Failed to decode internal URL.');
+ return;
+ }
+
if (!rootsUtils.isValidFileName(name)) {
fail(FileError.ENCODING_ERR, 'Error - Disallowed character detected in the file name: ' + name);
return;
}
- if (-1 !== (rootsUtils.getFullPath(destURL) + '/').indexOf(rootsUtils.getFullPath(srcURL) + '/')) {
+ if (-1 !== (destURL + '/').indexOf(srcURL + '/')) {
fail(FileError.INVALID_MODIFICATION_ERR, 'Error - Cannot copy/move onto itself.');
return;
}
@@ -126,18 +131,23 @@ var changeFile = function(method, successCallback, errorCallback, args) {
module.exports = {
getFileMetadata: function(successCallback, errorCallback, args) {
- try {
- tizen.filesystem.resolve(args[0], function (file) {
- var result = { 'size': file.fileSize, 'lastModifiedDate': file.modified };
- successCallback && successCallback(result);
- }, function (err) {
- errorCallback && errorCallback(ConvertTizenFileError(err));
- }, 'r');
- } catch (exception) {
- console.error('Error - resolve failed');
- errorCallback && errorCallback(ConvertTizenFileError(exception));
- }
- },
+ var uri = rootsUtils.internalUrlToNativePath(args[0]);
+ if (!uri) {
+ errorCallback && errorCallback(FileError.ENCODING_ERR);
+ return;
+ }
+ try {
+ tizen.filesystem.resolve(uri, function (file) {
+ var result = { 'size': file.fileSize, 'lastModifiedDate': file.modified };
+ successCallback && successCallback(result);
+ }, function (err) {
+ errorCallback && errorCallback(ConvertTizenFileError(err));
+ }, 'r');
+ } catch (exception) {
+ console.error('Error - resolve failed');
+ errorCallback && errorCallback(ConvertTizenFileError(exception));
+ }
+ },
setMetadata: function(successCallback, errorCallback, args) {
console.error('setMetadata - Not supported');
errorCallback && errorCallback(FileError.ENCODING_ERR);
@@ -149,7 +159,12 @@ module.exports = {
changeFile('copyTo', successCallback, errorCallback, args);
},
remove: function(successCallback, errorCallback, args) {
- var url = args[0];
+ var url = rootsUtils.internalUrlToNativePath(args[0]);
+
+ if (!url) {
+ errorCallback && errorCallback(FileError.ENCODING_ERR);
+ return;
+ }
if (rootsUtils.isRootUri(url)) {
console.error('It is not allowed to remove root directory.');
@@ -181,7 +196,12 @@ module.exports = {
);
},
getParent: function(successCallback, errorCallback, args) {
- var url = args[0];
+ var url = rootsUtils.internalUrlToNativePath(args[0]);
+
+ if (!url) {
+ errorCallback && errorCallback(FileError.ENCODING_ERR);
+ return;
+ }
if (rootsUtils.isRootUri(url)) {
successCallback && successCallback(rootsUtils.findFilesystem(url));
diff --git a/src/file/js/File.js b/src/file/js/File.js
index 51b67d0..fd6bcd1 100644
--- a/src/file/js/File.js
+++ b/src/file/js/File.js
@@ -51,5 +51,6 @@ console.log('Loaded cordova.file API');
exports = function(require) {
require('cordova-tizen').addPlugin('cordova-plugin-file.File', plugin_name, 'runs');
+ require('cordova-tizen').addPlugin('cordova-plugin-file.FileSystem', 'cordova-plugin-file.tizen.FileSystem', 'merges', 'window.FileSystem');
};
// TODO: remove -> end
diff --git a/src/file/js/FileReader.js b/src/file/js/FileReader.js
index 1651082..d724459 100644
--- a/src/file/js/FileReader.js
+++ b/src/file/js/FileReader.js
@@ -19,6 +19,13 @@ cordova.define('cordova-plugin-file.tizen.FileReader', function(require, exports
// TODO: remove -> end
function read(operation, url, start, end, successCallback, errorCallback, encoding) {
+ url = rootsUtils.internalUrlToNativePath(url);
+
+ if (!url) {
+ errorCallback && errorCallback(FileError.ENCODING_ERR);
+ return;
+ }
+
var fail = function(e) {
errorCallback && errorCallback(ConvertTizenFileError(e));
}
diff --git a/src/file/js/FileSystem.js b/src/file/js/FileSystem.js
new file mode 100644
index 0000000..8eacf99
--- /dev/null
+++ b/src/file/js/FileSystem.js
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// TODO: remove when added to public cordova repository -> begin
+cordova.define('cordova-plugin-file.tizen.FileSystem', function(require, exports, module) {
+// TODO: remove -> end
+
+module.exports = {
+ __format__: function(fullPath) {
+ return 'cdvfile://localhost/' + this.name + fullPath;
+ }
+};
+
+console.log('Loaded cordova.file FileSystem');
+
+// TODO: remove when added to public cordova repository -> begin
+});
+// TODO: remove -> end
diff --git a/src/file/js/FileWriter.js b/src/file/js/FileWriter.js
index b606a3b..9150723 100644
--- a/src/file/js/FileWriter.js
+++ b/src/file/js/FileWriter.js
@@ -55,11 +55,16 @@ function toUTF8Array(str) {
module.exports = {
write: function(successCallback, errorCallback, args) {
- var uri = args[0];
+ var uri = rootsUtils.internalUrlToNativePath(args[0]);
var data = args[1];
var position = args[2];
var isBinary = args[3];
+ if (!uri) {
+ errorCallback && errorCallback(FileError.ENCODING_ERR);
+ return;
+ }
+
if (!isBinary) {
if ('string' === typeof data) {
// convert to UTF-8, as this is the default encoding for read operations
@@ -103,7 +108,7 @@ module.exports = {
// for this, we need to truncate after write...
module.exports.truncate(function() {
successCallback && successCallback(length);
- }, errorCallback, [uri, stream.position]);
+ }, errorCallback, [args[0], stream.position]);
} catch (error) {
errorCallback && errorCallback(ConvertTizenFileError(error));
}
@@ -132,9 +137,19 @@ module.exports = {
},
truncate: function(successCallback, errorCallback, args) {
- var uri = rootsUtils.getFullPath(args[0]);
+ var uri = rootsUtils.internalUrlToNativePath(args[0]);
var length = args[1];
+ if (!uri) {
+ errorCallback && errorCallback(FileError.ENCODING_ERR);
+ return;
+ }
+
+ var uriPrefix = 'file://';
+ if (0 === uri.indexOf(uriPrefix)) {
+ uri = uri.substring(uriPrefix.length);
+ }
+
var callArgs = {
'uri': uri,
'length': length
diff --git a/src/file/js/resolveLocalFileSystemURI.js b/src/file/js/resolveLocalFileSystemURI.js
index 19491fe..54b2fc8 100644
--- a/src/file/js/resolveLocalFileSystemURI.js
+++ b/src/file/js/resolveLocalFileSystemURI.js
@@ -20,12 +20,9 @@ cordova.define('cordova-plugin-file.tizen.resolveLocalFileSystemURI', function(r
module.exports = {
resolveLocalFileSystemURI: function(successCallback, errorCallback, args) {
- var path = args[0];
- // fix for file.spec.10
- path = path.split('?')[0];
+ var path = rootsUtils.internalUrlToNativePath(args[0]);
- // fix for file.spec.12
- if (0 !== path.indexOf('file://')) { // 'file://' scheme is required
+ if (!path) {
errorCallback && errorCallback(FileError.ENCODING_ERR);
return;
}
diff --git a/src/file/js/rootsUtils.js b/src/file/js/rootsUtils.js
index 7760557..1a3fa88 100644
--- a/src/file/js/rootsUtils.js
+++ b/src/file/js/rootsUtils.js
@@ -15,24 +15,26 @@
*/
var rootsUtils = (function() {
- var uriPrefix = 'file://';
+ var filePrefix = 'file:///';
function stripTrailingSlash(str) {
- if ('/' === str.substr(-1)) {
+ if (filePrefix !== str && '/' === str.substr(-1)) {
return str.substr(0, str.length - 1);
}
return str;
}
function getName(uri) {
- return stripTrailingSlash(uri).replace(/^.*(\\|\/|\:)/, '');
+ return getFullPath(uri).replace(/^.*(\\|\/|\:)/, '');
}
function getFullPath(uri) {
- if (0 === uri.indexOf(uriPrefix)) {
- uri = uri.substr(uriPrefix.length);
+ var tmp = findFilesystem(uri);
+ tmp = getNativeUrl(uri).substring(tmp.nativeURL.length);
+ if (!tmp) {
+ tmp = '/';
}
- return stripTrailingSlash(uri);
+ return tmp;
}
function getNativeUrl(uri) {
@@ -53,32 +55,32 @@ var rootsUtils = (function() {
{
filesystemName: 'temporary',
name: '',
- fullPath: '',
+ fullPath: '/',
nativeURL: 'wgt-private-tmp'
},
{
filesystemName: 'persistent',
name: '',
- fullPath: '',
+ fullPath: '/',
nativeURL: 'wgt-private'
}
];
- var rootDirUri = 'file:///';
-
var roots = [
{
filesystemName: 'root',
- name: getName(rootDirUri),
- fullPath: getFullPath(rootDirUri),
- nativeURL: getNativeUrl(rootDirUri)
+ name: '',
+ fullPath: '/',
+ nativeURL: 'file:///'
}
];
+ var name_to_root;
+
function getRoots(successCallback) {
if (roots_to_resolve.length > 0) {
tizen.filesystem.resolve(roots_to_resolve[0].nativeURL, function(dir) {
- roots_to_resolve[0] = createEntry(dir, roots_to_resolve[0].filesystemName);
+ roots_to_resolve[0].nativeURL = getNativeUrl(dir.toURI());
roots.push(roots_to_resolve[0]);
roots_to_resolve.splice(0, 1); // remove first item
@@ -90,6 +92,12 @@ var rootsUtils = (function() {
successCallback(roots);
});
} else {
+ if (!name_to_root) {
+ name_to_root = {};
+ for (var i = 0; i < roots.length; ++i) {
+ name_to_root[roots[i].filesystemName] = roots[i];
+ }
+ }
successCallback(roots.slice());
}
}
@@ -101,9 +109,9 @@ var rootsUtils = (function() {
}
function findFilesystem(uri) {
- var fullPath = getFullPath(uri);
+ var nativeUrl = getNativeUrl(uri);
for (var i = roots.length - 1; i > 0; --i) {
- if (0 === strncmp(fullPath, roots[i].fullPath, roots[i].fullPath.length)) {
+ if (0 === strncmp(nativeUrl, roots[i].nativeURL, roots[i].nativeURL.length)) {
return roots[i];
}
}
@@ -113,7 +121,7 @@ var rootsUtils = (function() {
function isRootUri(uri) {
var fs = findFilesystem(uri);
- return (fs.fullPath === getFullPath(uri));
+ return (fs.nativeURL === getNativeUrl(uri));
}
// http://www.w3.org/TR/2011/WD-file-system-api-20110419/#naming-restrictions
@@ -129,6 +137,63 @@ var rootsUtils = (function() {
return true;
}
+ var localhost = '//localhost/'
+ var cdvPrefix = 'cdvfile:///';
+
+ function internalUrlToNativePath(url) {
+ var input = url;
+
+ // skip parameters
+ url = url.split('?')[0];
+
+ // remove localhost
+ url = url.replace(localhost, '///');
+
+ if (0 === url.indexOf(cdvPrefix)) {
+ // cdvfile protocol
+ url = url.substring(cdvPrefix.length);
+
+ var idx = url.indexOf('/');
+
+ if (-1 !== idx) {
+ var fsName = url.substring(0, idx);
+ var fullPath = url.substring(idx);
+ url = name_to_root[fsName] ? name_to_root[fsName].nativeURL + fullPath : undefined;
+ } else {
+ // malformed URL
+ url = undefined;
+ }
+ } else if (0 === url.indexOf(filePrefix)) {
+ // check if the filesystem for this URL exists
+ var found = false;
+ for (var i = 0; i < roots.length && !found; ++i) {
+ if (0 === url.indexOf(roots[i].nativeURL)) {
+ found = true;
+ }
+ }
+
+ if (!found) {
+ url = undefined;
+ }
+ } else {
+ // backwards compatibility, device absolute path
+ // only TEMPORARY and PERSISTENT paths are allowed
+ url = filePrefix + url.substring(1); // skip '/'
+ if (0 !== url.indexOf(name_to_root.temporary.nativeURL) &&
+ 0 !== url.indexOf(name_to_root.persistent.nativeURL)) {
+ url = undefined;
+ }
+ }
+
+ if (url) {
+ url = getNativeUrl(url);
+ } else {
+ console.error('Failed to decode internal URL: ' + input);
+ }
+
+ return url;
+ }
+
return {
getRoots: getRoots,
findFilesystem: findFilesystem,
@@ -138,6 +203,7 @@ var rootsUtils = (function() {
stripTrailingSlash: stripTrailingSlash,
createEntry: createEntry,
isRootUri: isRootUri,
- isValidFileName: isValidFileName
+ isValidFileName: isValidFileName,
+ internalUrlToNativePath: internalUrlToNativePath
};
})();
--
2.7.4