[File] Reworked internal represenation of URLs, added support for cdvfile://
authorPawel Andruszkiewicz <p.andruszkie@samsung.com>
Fri, 13 Nov 2015 15:16:10 +0000 (16:16 +0100)
committerPawel Andruszkiewicz <p.andruszkie@samsung.com>
Fri, 13 Nov 2015 15:17:54 +0000 (16:17 +0100)
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 <p.andruszkie@samsung.com>
src/file/cordova_file_api.js
src/file/js/DirectoryEntry.js
src/file/js/DirectoryReader.js
src/file/js/Entry.js
src/file/js/File.js
src/file/js/FileReader.js
src/file/js/FileSystem.js [new file with mode: 0644]
src/file/js/FileWriter.js
src/file/js/resolveLocalFileSystemURI.js
src/file/js/rootsUtils.js

index d28e754..b5a171d 100755 (executable)
@@ -26,4 +26,5 @@
 //= require('requestFileSystem.js');
 //= require('resolveLocalFileSystemURI.js');
 
+//= require('FileSystem.js');
 //= require('File.js');
index 820c725..3f11fd4 100644 (file)
@@ -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.');
index 3d1d926..6feed91 100644 (file)
@@ -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));
     }
index 1b56c28..1b258e8 100644 (file)
@@ -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));
index 51b67d0..fd6bcd1 100644 (file)
@@ -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
index 1651082..d724459 100644 (file)
@@ -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 (file)
index 0000000..8eacf99
--- /dev/null
@@ -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
index b606a3b..9150723 100644 (file)
@@ -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
index 19491fe..54b2fc8 100644 (file)
@@ -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;
     }
index 7760557..1a3fa88 100644 (file)
  */
 
 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
   };
 })();